1. 程式人生 > 實用技巧 >7.自定義ViewGroup-下滑抽屜

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.目前存在的問題

當滑動的最頂端的時候,只有鬆開手,再按下滑,才可以滑的動

原始碼地址