7.自定义ViewGroup-下滑抽屉

1.效果

2.思路

分析效果

1.布局分为两部分,后面部分,前面部分,默认状态后面被挡住;

2.后面不可以滑动,前面可以滑动;

3.如果前面的布局本身是可以滑动的,那么当前面布局滑动到第一个时,后面的布局才显示出来

基于以上的效果,我们自定义ViewGroup 继承自FrameLayout,使用ViewDragHelper处理滑动事件;

当前面的view 本身是可以滑动的,那么当其手指向下move的过程中需要判断前面的view是否在最顶部,从而决定是否拦截事件;

ViewDragHelper 的用法

//重点
private ViewDragHelper mDragHelper;
//初始化
mDragHelper = ViewDragHelper.create(this, mDragCallback);

ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            //只有mDragView 可以滑动
            return mDragView == child;
        }

        @Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            //控制垂直方向滑动的距离
            if (top < 0) {
                top = 0;
            }
            if (top > mMenuHeight) {
                top = mMenuHeight;
            }
            return top;
        }

        //当手指松开的时候回调
        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            int dy = mDragView.getTop();
           
            if (dy > mMenuHeight / 2) {
                //打开
                mDragHelper.settleCapturedViewAt(0, mMenuHeight);
            } else {
                //关闭
                mDragHelper.settleCapturedViewAt(0, 0);
            }

            invalidate();
        }
    };



 	@Override
    public boolean onTouchEvent(MotionEvent event) {
        //处理event事件
        mDragHelper.processTouchEvent(event);
        return true;
    }

全部代码

class VerticalDragListView extends FrameLayout {
    private static final String TAG = "VerticalDragListView";

    private ViewDragHelper mDragHelper;
    private View mMenuView;
    private View mDragView;
    private int mMenuHeight;
    private boolean isOpen = false;

    public VerticalDragListView(@NonNull Context context) {
        this(context, null);
    }

    public VerticalDragListView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VerticalDragListView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper = ViewDragHelper.create(this, mDragCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mDragView = getChildAt(1);
    }

    ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            //只有mDragView 可以滑动
            return mDragView == child;
        }

        @Override
        public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
            //通过top 控制范围
            if (top < 0) {
                top = 0;
            }
            if (top > mMenuHeight) {
                top = mMenuHeight;
            }
            return top;
        }

        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            int dy = mDragView.getTop();
            //当手指松开的时候
            if (dy > mMenuHeight / 2) {
                //打开
                mDragHelper.settleCapturedViewAt(0, mMenuHeight);
                isOpen = true;
            } else {
                //关闭
                mDragHelper.settleCapturedViewAt(0, 0);
                isOpen = false;
            }

            invalidate();
        }
    };

    @Override
    public void computeScroll() {
        if (mDragHelper.continueSettling(true)) {
            invalidate();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mMenuHeight = mMenuView.getMeasuredHeight();
    }


    float downY = 0;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (isOpen) {
            return true;
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mDragHelper.processTouchEvent(ev);
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //子view滑动到顶部&&已经到顶了
                if (ev.getY() - downY > 0 && !canChildScrollUp(mDragView)) {
                    return true;
                }
                break;
        }

        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }

    /**
     * 判断是否可以继续向上滑
     *
     * @param mTarget
     * @return
     */
    public boolean canChildScrollUp(View mTarget) {

        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mTarget instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mTarget;
                return absListView.getChildCount() > 0
                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                        .getTop() < absListView.getPaddingTop());
            } else {
                return mTarget.canScrollVertically(-1) || mTarget.getScrollY() > 0;
            }
        } else {
            return mTarget.canScrollVertically(-1);
        }
    }
}

3.目前存在的问题

当滑动的最顶端的时候,只有松开手,再按下滑,才可以滑的动

源码地址

posted @ 2020-11-20 15:14  Nixon  阅读(118)  评论(0编辑  收藏  举报