自定义控件-侧拉菜单

    手机版qq有一个挺不错的效果是侧拉菜单,在主界面向右滑动就会在左侧出现关于自己qq的一些信息;
    实现侧滑效果目前知道有三种:
	一是SlidingDrawer,谷歌在Android4.2之后已经不建议使用
	二是DrawerLayout,谷歌提供的挺好用的控件,只需在布局文件中加载即可
	三是第三方提供的开源控件,更加的灵活;下面就来简单介绍一下原理
  一、需求
    1.定义两个布局,菜单和主内容,然后整合在一个自定义的控件中
    2.分别测量出菜单和主内容的宽和高
    3.主内容全屏显示在屏幕上,那么左侧的菜单就会看不到,达到了隐藏菜单的效果
    4.处理屏幕的onTouchEvent()事件,实现左右滑动控制菜单的隐藏与显示
  二、具体实现
    1.定义布局文件菜单和主内容,然后整合在自定义的SlideMenu控件中
    2.在SlideMenu中通过onMeasure测量布局文件的宽和高
	
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //测量菜单的宽和高,宽为240dip,高为父窗体高度
        //0代表菜单
        View menuView = getChildAt(0);
        menuView.measure(menuView.getLayoutParams().width, heightMeasureSpec);

        //测量内容正文的宽和高,与父窗体一致
        //1代表正文内容
        View contentView = getChildAt(1);
        contentView.measure(widthMeasureSpec, heightMeasureSpec);
    }

        在系统底层measure调用了onMeasure(定义了宽高的约束)
	onMeasrue调用了setMeasureDimension(真实的宽高信息)
    3.通过onLayout方法绘制布局
	
 @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //把测量出菜单和内容绘制屏幕上
        View menuView = getChildAt(0);
        //设置菜单在屏幕的左侧显示(即隐藏)
        menuView.layout(-menuView.getMeasuredWidth(), t, 0, b);
        //主内容的绘制
        View contentView = getChildAt(1);
        contentView.layout(l, t, r, b);
    }

    4.处理屏幕的onTouchEvent事件实现菜单的隐藏与显示
	
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int scrollX;

        switch (event.getAction()) {
            //按下
            case MotionEvent.ACTION_DOWN:
                downX = (int) event.getX();
                break;
            //移动
            case MotionEvent.ACTION_MOVE:
                int moveX = (int) event.getX();
                //偏移量
                int deltax = downX - moveX;

                scrollX = getScrollX() + deltax;
                //根据当前位置确定屏幕的显示
                if (scrollX < -getChildAt(0).getMeasuredWidth()) {
                    scrollTo(-getChildAt(0).getMeasuredWidth(), 0);
                } else if (scrollX > 0) {
                    scrollTo(0, 0);
                } else {
                    scrollBy(deltax, 0);
                }
                downX = moveX;
                break;
            //擡起
            case MotionEvent.ACTION_UP:
                int center = -getChildAt(0).getMeasuredWidth() / 2;
                scrollX = getScrollX();
                if (scrollX < center) {
                    currentState = SCREEN_CONTENT;
                } else {
                    currentState = SCREEN_MAIN;
                }
                switchScreen();
                break;
        }
        return true;
    }

    /**
     * 根据currentState的状态来更改屏幕的显示
     */
    private void switchScreen() {
        int startX = getScrollX();  //当前屏幕的X位置
        int dX = 0;                 //增量=目的位置-当前位置
        switch (currentState) {
            case SCREEN_CONTENT:
//                scrollTo(-getChildAt(0).getMeasuredWidth(),0);
                dX = -getChildAt(0).getMeasuredWidth() - startX;
                break;
            case SCREEN_MAIN:
//                scrollTo(0,0);
                dX = 0 - startX;
                break;
        }
        int duration = Math.abs(dX) * 10;
        if (duration > 1000) {
            duration = 1000;
        }

        scroller.startScroll(startX, 0, dX, 0, duration);
        invalidate();
    }

    @Override
    public void computeScroll() {
        //当不在模拟数据时候跳出递归
        if (scroller.computeScrollOffset()) {
            //获取当前模拟的数据
            int currX = scroller.getCurrX();
            //更新位置
            scrollTo(currX, 0);
            invalidate();
        }
    }


       滑动的分析:
	 1.当点击屏幕时记录下当前的位置downX,在移动的过程中根据当前位置moveX和downX计算偏移量
	   根据偏移量的值不断的修正菜单和主内容在屏幕的显示位置;
         2.当擡起时,如果菜单显示出来的内容是否大于二分之一,那么滑动显示菜单,反之显示主内容
	 3.currentState用来记录当前屏幕显示的是菜单还是主内容,并且是屏幕固定时偏移量计算的依据
	 4.scroller.startScroll(startX,0,dX,0,duration)
	   用来模拟X轴从startX到偏移dX经过duration时间的过程,但是这个方法只是模拟,并不会改变
	   屏幕的显示,还需要通过invalidate()来修改屏幕的显示
	 5.invalidate()在ViewGroup中通过调用DrawChild()->再调view.draw->computeScroll修改屏幕
	   显示的内容,由于startScroll的数据是缓慢变化的,因此需要用递归不断的更新当前的显示,就
	   实现了松手后菜单缓慢移动的效果
   三、回调简介
	Android回调方法用的特别多,其原理类似于设计模式中的观察者模式,回调通过自身提供一个内部
	接口,在接口定义统一的方法,那么不管是谁调用我,只要实现我接口中定义的方法,使代码有了很
	好的封装性
   四、个人总结
	写自定义控件一定要有的步骤:
	1.分析控件的实现都有什么状态
	2.找出状态之间转换的临界条件
	3.实现即可


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章