7.自定義ViewGroup-下滑抽屜
阿新 • • 發佈:2020-11-20
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.目前存在的問題
當滑動的最頂端的時候,只有鬆開手,再按下滑,才可以滑的動