Android自定义View,实现全屏滑动的DrawerLayout

    转载请标明出处:http://blog.csdn.net/weixin_39059543/article/details/73826250

 

    对与DrawerLayout大家应该用过,是Google官方推出的一种抽屉式导航控件。打开左右两边菜单的方式是从手机屏

幕的边缘处滑动来触发,不过总有些**的需求要让它可以全屏滑动触发菜单,网上也有一些解决办法,无非就是用

setDrawerLeftEdgeSize()来设置边缘大小,将一边的屏幕边缘扩大至整个屏幕,不过这会产生一些bug比如点击屏幕或上下滑动屏幕都会弹出菜单、多种手势不能识别,并且只能实现一边的全屏滑动,因为不能两边的边缘都扩大至全屏。所以要想从根本上解决这个这个问题,就要从源码上分析。
 

我们来看一下DrawerLayout的源码:

 

public class DrawerLayout extends ViewGroup implements DrawerLayoutImpl {
    private static final String TAG = "DrawerLayout";

    @IntDef({STATE_IDLE, STATE_DRAGGING, STATE_SETTLING})
    @Retention(RetentionPolicy.SOURCE)
    private @interface State {}

    /**
     * Indicates that any drawers are in an idle, settled state. No animation is in progress.
     */
    public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE;

    /**
     * Indicates that a drawer is currently being dragged by the user.
     */
    public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING;

它是继承ViewGroup的一个自定义类,所以我们可以仿照它的写法自定义一个MyDrawerLayout 只要在

onTouchEvent(MotionEvent ev)判断用户手势,左右滑动打开相对应用的菜单,原理就是这样。

     我们继续看源码打开抽屉的方法:

 

@Override
public void onEdgeDragStarted(int edgeFlags, int pointerId) {
    final View toCapture;
    if ((edgeFlags & ViewDragHelper.EDGE_LEFT) == ViewDragHelper.EDGE_LEFT) {
        toCapture = findDrawerWithGravity(Gravity.LEFT);
    } else {
        toCapture = findDrawerWithGravity(Gravity.RIGHT);
    }

    if (toCapture != null && getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
        mDragger.captureChildView(toCapture, pointerId);
    }
}

这个方法是当用户触发手机屏幕边缘的时候回调,我们可以将这个方法屏蔽掉,然后在 

onTouchEvent(MotionEventev)里调用打开方法。

 

private static final int MIN_DRAWER_MARGIN = 64; // dp

注意一下源码中这个这是设置菜单的最大宽度离另一边的最小距离,简单来说要想你的菜单是全屏显示的话就把这

个值设置为0。

 

   

核心代码:

 

@Override
public boolean onTouchEvent(MotionEvent ev) {

    mLeftDragger.processTouchEvent(ev);
    mRightDragger.processTouchEvent(ev);
    final View toCapture;
    final int action = ev.getAction();
    boolean wantTouchEvents = true;

    if (ev.getPointerCount() > 1) {  //多点触屏拦截,防止多点触屏滑出菜单 如果主内容里面有子内控件,可写到onInterceptTouchEvent交给子类拦截

        return false;
    }
    switch (action & MotionEventCompat.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = ev.getX();
            final float y = ev.getY();
            mInitialMotionX = x;
            mInitialMotionY = y;
            closeDrawers(true);
            closeKeyboard();
            mDisallowInterceptRequested = false;
            mChildrenCanceledTouch = false;
            break;
        }

        case MotionEvent.ACTION_UP: {
            final float x = ev.getX();
            final float y = ev.getY();
            boolean peekingOnly = true;
            final View touchedView = mLeftDragger.findTopChildUnder((int) x, (int) y);
            if (touchedView != null && isContentView(touchedView)) {
                final float dx = x - mInitialMotionX;
                final float dy = y - mInitialMotionY;
                final int slop = mLeftDragger.getTouchSlop();
                if (dx * dx + dy * dy < slop * slop) {
                    // Taps close a dimmed open drawer but only if it isn't
                    // locked open.
                    final View openDrawer = findOpenDrawer();
                    if (openDrawer != null) {
                        peekingOnly = getDrawerLockMode(openDrawer) == LOCK_MODE_LOCKED_OPEN;
                    }
                }
            }
            closeDrawers(peekingOnly);
            mDisallowInterceptRequested = false;
            isCheck = false;
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            closeDrawers(true);
            mDisallowInterceptRequested = false;
            mChildrenCanceledTouch = false;
            break;
        }

        //核心代码  滑动打开菜单
       case MotionEvent.ACTION_MOVE: {   

            if (findDrawerWithGravity(Gravity.END).getVisibility() != View.VISIBLE && 
                    findDrawerWithGravity(Gravity.START).getVisibility() != View.VISIBLE) {
                final float x = ev.getX();
                final float dx = x - mInitialMotionX;
             
                if (dx > 10) {
                    //isCheck为全局变量,用来控制每次滑动只能滑动一侧菜单
                    if (!isCheck) {
                        toCapture = findDrawerWithGravity(Gravity.START);
                        if (getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED){
                            mLeftDragger.captureChildView(toCapture,ev.getPointerId(0));
                        }
                        isCheck = true;
                    }

                } else if ((dx < -10)) {
                    if (!isCheck) {
                        toCapture = findDrawerWithGravity(Gravity.END);
                        if (getDrawerLockMode(toCapture) == LOCK_MODE_UNLOCKED) {
                            mRightDragger.captureChildView(toCapture, ev.getPointerId(0));
                        }
                        isCheck = true;
                    }
                }

            }
            break;

        }
    }

    return wantTouchEvents;
}

 

 

 

如果主内容里面有子控件,触摸拦截与传递在子父类的onIntercptTouchEvent和onTouchEvent的这两个方法中处理事件分发。

 

 

          好了废话不多说,提供源码下载,自己分析:

          https://download.csdn.net/download/weixin_39059543/11615398

 

 

 

 

 

 

你可能感兴趣的:(Android,UI控件)