天天看点

一个简单的自定义侧滑菜单

 这只是为了学习自定义view,自己写的一个简单的侧滑菜单,建议真实项目中还是使用成熟的侧滑菜单框架(比如Slidingmenu),不要去重复造轮子。代码的逻辑不复杂,且注释写的还算清楚,就不多解释了,直接上代码。

/**
 * 自定义的侧滑菜单
 * 创建日期:2018-03-15 on 13:45
 * 作者:ls
 */

public class SlidingMenu extends ViewGroup {

    private static final String TAG = "SlidingMenu";

    private int mTouchSlop;//被判定为滑动的最小距离

    private Scroller mScroller;//弹性滑动

    private VelocityTracker mVelocityTracker;//速度跟踪器

    private float mFirstX;

    private float mFirstY;

    private float mLastX;

    private float mLastY;

    private int menuWidth;//菜单的宽度

    private int contentWidth;//内容的宽度

    private View menu;//菜单

    private View content;//内容

    public SlidingMenu(Context context) {
        this(context, null);
    }

    public SlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlidingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mTouchSlop = ViewConfiguration.getTouchSlop();
        mScroller = new Scroller(getContext());
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //测量所有的子view
        int childCount = getChildCount();
        for(int i = 0; i < childCount; i++){
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.d(TAG, "l = " + l + "....." + "t = " + t + "....." + "r = " + r + "....." + "b = " + b);
        menu = getChildAt(0);
        content = getChildAt(1);
        menuWidth = menu.getMeasuredWidth();
        contentWidth = content.getMeasuredWidth();
        menu.layout(-1 * menuWidth, 0, 0, menu.getMeasuredHeight());
        content.layout(0, 0, contentWidth, content.getMeasuredHeight());
        Log.d(TAG, "content.getMeasuredWidth() = " + content.getMeasuredWidth());
        Log.d(TAG, "content.getMeasuredHeight() = " + content.getMeasuredHeight());
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onInterceptTouchEvent.........ACTION_DOWN");
                mFirstX = mLastX = ev.getX();
                mFirstY = mLastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onInterceptTouchEvent.........ACTION_MOVE");
                float deltaX = ev.getX() - mLastX;
                float deltaY = ev.getY() - mLastY;
                if(Math.abs(deltaX) > mTouchSlop && Math.abs(deltaX) > Math.abs(deltaY)){
                    return true;
                }
                return false;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mVelocityTracker.addMovement(event);
        mVelocityTracker.computeCurrentVelocity(1000);
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent.........ACTION_DOWN");
                return true;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent.........ACTION_MOVE");
                Log.d(TAG, "getScrollX() = " + getScrollX());
                //边界判断和处理
                if (checkBound(event)) return true;
                //手指按在屏幕上滑动的处理
                float deltaX = event.getX() - mLastX;
                scrollBy((int) (-1 * deltaX), 0);
                mLastX = event.getX();
                mLastY = event.getY();
                break;
            case MotionEvent.ACTION_UP: //根据滚动的距离判断怎么还原
                Log.d(TAG, "onTouchEvent.........ACTION_UP");
                int vx = (int) Math.abs(mVelocityTracker.getXVelocity());
                Log.d(TAG, "vx = " + vx);
                //快速滑动的处理,未必一定是200
                if(vx > 200){
                    if(event.getX() - mFirstX > 0){ //向右快速滑动
                        showMenuComplete();
                        return true;
                    }else{ //向左快速滑动
                        hideMenuComplete();
                        return true;
                    }
                }
                //正常滑动的处理
                if(getScrollX() > -1 * menuWidth && getScrollX() <= -0.5f * menuWidth){ //拉出过半了
                    showMenuComplete();
                    return true;
                }
                if(getScrollX() > -0.5f * menuWidth && getScrollX() <= 0){ //拉出未过半
                    hideMenuComplete();
                    return true;
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 完全隐藏menu
     */
    private void hideMenuComplete() {
        mScroller.startScroll(getScrollX(), 0, 0 - getScrollX(), 0);
        invalidate();
    }

    /**
     * 完全展示menu
     */
    private void showMenuComplete() {
        mScroller.startScroll(getScrollX(), 0, -1 * menuWidth - getScrollX(), 0);
        invalidate();
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if(mScroller.computeScrollOffset()){
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    /**
     * 边界的判断和处理
     */
    private boolean checkBound(MotionEvent event) {
        float tempX = event.getX() - mFirstX;
        if(tempX > 0){ //说明手指在向右滑动
            if(getScrollX() <= -1 * menuWidth){
                scrollTo(-1 * menuWidth, 0);
                Log.d(TAG, "已经碰到边界了...不能再往右滑动");
                return true;
            }
        }else{ //说明手指在向左滑动
            if(getScrollX() >= 0){
                scrollTo(0, 0);
                Log.d(TAG, "已经碰到边界了...不能再往左滑动");
                return true;
            }
        }
        return false;
    }
}