Android ViewDragHelper實(shí)現(xiàn)京東、淘寶拖拽詳情功能的實(shí)現(xiàn)
先上效果圖,如果大家感覺不錯,請參考實(shí)例代碼,效果圖如下所述:
要實(shí)現(xiàn)這個(gè)效果有三種方式:
① 手勢
② 動畫
③ 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實(shí)例 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); } } //動畫實(shí)現(xiàn)滾動 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坐標(biāo) private float mRawX; //獲取初始Y坐標(biāo) 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要自己消費(fèi)這次事件,告訴父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不消費(fèi)這次事件,告訴父View攔截這次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } if (mRawY > ev.getRawY() && mCanScrollDown) { //表示子View不消費(fèi)這次事件,告訴父View攔截這次的事件。 getParent().requestDisallowInterceptTouchEvent(false); return false; } } break; } return super.dispatchTouchEvent(ev); } /** * 手指向下滑動(內(nèi)容向上滑動) * @return */ private boolean canScrollingUp() { if (ViewCompat.canScrollVertically(this, -1)) { return false; } else { return true; } } /** * 手指向上滑動(內(nèi)容向下滑動) * @return */ private boolean canScrollingDown() { if (ViewCompat.canScrollVertically(this, 1)) { return false; } else { return true; } } }
好了,具體拖拽代碼就是這些了,界面我用的兩個(gè)Fragment,相信大家也看出來了。里面大家換成自己的業(yè)務(wù)UI就可以了。
以上所述是小編給大家介紹的Android ViewDragHelper實(shí)現(xiàn)京東、淘寶拖拽詳情功能的實(shí)現(xiàn),希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時(shí)回復(fù)大家的!
相關(guān)文章
Android WebView使用方法詳解 附j(luò)s交互調(diào)用方法
這篇文章主要為大家詳細(xì)介紹了Android WebView使用方法詳解,文中附j(luò)s交互調(diào)用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05ViewPager頂部導(dǎo)航欄聯(lián)動效果(標(biāo)題欄條目多)
這篇文章主要介紹了ViewPager頂部導(dǎo)航欄聯(lián)動效果,代碼簡單易懂,感興趣的朋友參考下吧2016-08-08最近較流行的效果 Android自定義View實(shí)現(xiàn)傾斜列表/圖片
最近較流行的效果,這篇文章主要介紹了Android自定義View實(shí)現(xiàn)傾斜列表/圖片的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-06-06Android開發(fā)中ImageLoder進(jìn)行圖片加載和緩存
這篇文章主要介紹了Android開發(fā)中ImageLoder進(jìn)行圖片加載和緩存的相關(guān)資料,需要的朋友可以參考下2016-04-04Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例
這篇文章主要介紹了Android網(wǎng)絡(luò)編程之UDP通信模型實(shí)例,本文給出了服務(wù)端代碼和客戶端代碼,需要的朋友可以參考下2014-10-10Android 實(shí)現(xiàn)帶字母索引的側(cè)邊欄功能
這篇文章主要介紹了Android 實(shí)現(xiàn)帶字母索引的側(cè)邊欄功能,需要的朋友可以參考下2017-08-08Android中實(shí)現(xiàn)長按照片彈出右鍵菜單功能的實(shí)例代碼
這篇文章主要介紹了Android中實(shí)現(xiàn)長按照片彈出右鍵菜單功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-01-01