您好,登錄后才能下訂單哦!
這篇文章主要介紹了Android中ViewDragHelper如何實現京東、淘寶拖拽詳情功能,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
效果圖如下所述:
要實現這個效果有三種方式:
① 手勢
② 動畫
③ ViewDragHelper
這里我使用的是ViewDragHelper類.
public class ViewDragLayout extends ViewGroup { //垂直方向的滑動速度 private static final int VEL_THRESHOLD = 300; //垂直方向的滑動距離 private static final int DISTANCE_THRESHOLD = 300; //上面可見的View private View mTopView; //下面詳情View private View mBottomView; //ViewDragHelper實例 private ViewDragHelper mViewDragHelper; private GestureDetectorCompat mGestureDetectorCompat; private int mFirstHeight; public ViewDragLayout(Context context) { super(context); init(); } public ViewDragLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ViewDragLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public ViewDragLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init() { mViewDragHelper = ViewDragHelper.create(this, 1.0f, new DragHelperCallback()); mGestureDetectorCompat = new GestureDetectorCompat(getContext(), new YScrollDetector()); } @Override protected void onFinishInflate() { super.onFinishInflate(); mTopView = getChildAt(0); mBottomView = getChildAt(1); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (mTopView.getTop() == 0) { mTopView.layout(l, 0, r, b-t ); mBottomView.layout(l, 0, r, b-t ); mFirstHeight = mTopView.getMeasuredHeight(); mBottomView.offsetTopAndBottom(mFirstHeight); }else{ mTopView.layout(l, mTopView.getTop(), r, mTopView.getBottom()); mBottomView.layout(l, mBottomView.getTop(), r, mBottomView.getBottom()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildren(widthMeasureSpec,heightMeasureSpec); int maxWidth = MeasureSpec.getSize(widthMeasureSpec); int maxHeight = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, 0), resolveSizeAndState(maxHeight, heightMeasureSpec, 0)); } private class DragHelperCallback extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View child, int pointerId) { return true; } /** * @param child * @param top * @param dy * @return */ @Override public int clampViewPositionVertical(View child, int top, int dy) { int finalTop=top; if (child == mTopView) { if (top > 0) { finalTop=0; } }else if(child==mBottomView){ if(top<0){ finalTop=0; } } return finalTop; } @Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { if (changedView == mTopView) { mBottomView.offsetTopAndBottom(dy); }else if (changedView==mBottomView){ mTopView.offsetTopAndBottom(dy); } ViewCompat.postInvalidateOnAnimation(ViewDragLayout.this); } /** * * @param releasedChild * @param xvel 水平方向的速度(向右為正) * @param yvel 豎直方向的速度(向下為正) */ @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { animTopOrBottom(releasedChild, yvel); } } //動畫實現滾動 private void animTopOrBottom(View releasedChild, float yvel) { int finalTop=0; if (releasedChild == mTopView) { if (yvel < -VEL_THRESHOLD || (releasedChild.getTop() < -DISTANCE_THRESHOLD)) { finalTop=-mFirstHeight; } } else if (releasedChild == mBottomView) { if (yvel > VEL_THRESHOLD || (releasedChild.getTop() > DISTANCE_THRESHOLD)) { finalTop=mFirstHeight; } } if (mViewDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) { ViewCompat.postInvalidateOnAnimation(this); } } @Override public void computeScroll() { if (mViewDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } } //是否攔截手勢操作 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mTopView.getTop() < 0 && mTopView.getBottom() > 0) { return false; } boolean isCanTouch = mGestureDetectorCompat.onTouchEvent(ev); boolean shouldIntercept = mViewDragHelper.shouldInterceptTouchEvent(ev); if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { mViewDragHelper.processTouchEvent(ev); } return isCanTouch&&shouldIntercept; } //將touch事件交給ViewDragHelper處理 @Override public boolean onTouchEvent(MotionEvent event) { mViewDragHelper.processTouchEvent(event); return true; } //垂直方向上才滾動 private class YScrollDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return Math.abs(distanceY) > Math.abs(distanceX); } } }
使用ViewDragLayout
<gesture.com.cn.widget.ViewDragLayout android:id="@+id/view_drag_layout" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:id="@+id/top_fragment_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/bottom_fragment_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </gesture.com.cn.widget.ViewDragLayout>
bottom_fragment_view中使用了ScrollView,但是原生是不行的,所以這里我又將ScrollView重寫了一下
這里主要是處理dispatchTouchEvent(MotionEvent ev)方法,判斷將touch事件交給自己處理還是交給父View處理
public class CustomScrollView extends ScrollView { //滾動臨界值 private int mTouchSlop; //獲取初始X坐標 private float mRawX; //獲取初始Y坐標 private float mRawY; //是否向上滑動 private boolean mCanScrollUp; //是否向下滑動 private boolean mCanScrollDown; public CustomScrollView(Context context) { super(context); init(); } public CustomScrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public CustomScrollView(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { ViewConfiguration configuration = ViewConfiguration.get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: mRawX = ev.getRawX(); mRawY = ev.getRawY(); mCanScrollUp = canScrollingUp(); mCanScrollDown = canScrollingDown(); //表示子View要自己消費這次事件,告訴父View不攔截這次的事件。 getParent().requestDisallowInterceptTouchEvent(true); break; case MotionEvent.ACTION_MOVE: float xDis = Math.abs(mRawX - ev.getRawX()); float yDis = Math.abs(mRawY - ev.getRawY()); if (yDis > xDis && yDis > mTouchSlop) { if (mRawY < ev.getRawY() && mCanScrollUp) { //表示子View不消費這次事件,告訴父View攔截這次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } if (mRawY > ev.getRawY() && mCanScrollDown) { //表示子View不消費這次事件,告訴父View攔截這次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } } break; } return super.dispatchTouchEvent(ev); } /** * 手指向下滑動(內容向上滑動) * @return */ private boolean canScrollingUp() { if (ViewCompat.canScrollVertically(this, -1)) { return false; } else { return true; } } /** * 手指向上滑動(內容向下滑動) * @return */ private boolean canScrollingDown() { if (ViewCompat.canScrollVertically(this, 1)) { return false; } else { return true; } } }
好了,具體拖拽代碼就是這些了,界面我用的兩個Fragment,相信大家也看出來了。里面大家換成自己的業務UI就可以了。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android中ViewDragHelper如何實現京東、淘寶拖拽詳情功能”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。