Android ViewDragHelper仿淘寶拖動(dòng)加載效果
拖動(dòng)加載是我在淘寶的商品詳情界面發(fā)現(xiàn)的,感覺很實(shí)用。于是就分析它的實(shí)現(xiàn)方式,感覺用ViewDragHelper可以很方便的實(shí)現(xiàn)這種效果。下面大致把我的思路分步驟寫一下。先上圖吧。
首先建工程什么的我就不多說了。咱從ViewDragHelper的實(shí)現(xiàn)開始說吧,ViewDragHelper一般用在一個(gè)自定義ViewGroup的內(nèi)部,可以對其子View進(jìn)行移動(dòng)操作。
創(chuàng)建自定義ViewGroup:
package com.maxi.viewdraghelpertest.widget; import android.content.Context; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; public class DragHelperLayout extends LinearLayout{ private ViewDragHelper mDragHelper; @SuppressWarnings("static-access") public DragHelperLayout(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub /* * 創(chuàng)建帶回調(diào)接口的ViewDragHelper */ mDragHelper = ViewDragHelper.create(this, 10.0f,new DragHelperCallback());// 參數(shù)一:該類生成的對象(當(dāng)前的ViewGroup) // 參數(shù)二:敏感度(越大越敏感) } class DragHelperCallback extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View arg0, int arg1) { // TODO Auto-generated method stub return false; } } }
然后將觸摸事件傳遞給ViewDragHelper:
@Override public boolean onInterceptTouchEvent(MotionEvent event) { return mDragHelper.shouldInterceptTouchEvent(event);//是否應(yīng)該打斷MotionEvent的傳遞 } @Override public boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true; }
接著我們開始實(shí)現(xiàn)DragHelperCallback,這個(gè)ViewDragHelper.Callback回調(diào)中可以對ViewGroup中的一些View進(jìn)行操作,在此我們只對本項(xiàng)目涉及到的相關(guān)用法做解析,詳細(xì)點(diǎn)請自行查閱資料。
class DragHelperCallback extends ViewDragHelper.Callback { @Override public boolean tryCaptureView(View arg0, int arg1) { // TODO Auto-generated method stub return true; //返回true表示可以捕捉ViewGroup中的View } /* * (non-Javadoc) * @see android.support.v4.widget.ViewDragHelper.Callback#clampViewPositionVertical(android.view.View, int, int) * 限定View豎直方向上的活動(dòng)區(qū)域,防止滑出ViewGroup */ @Override public int clampViewPositionVertical(View child, int top, int dy) { int topBound = getPaddingTop(); int bottomBound = getHeight() - child.getHeight() - topBound; int newHeight = Math.min(Math.max(top, topBound), bottomBound); return newHeight; } }
在上面的代碼段中我已經(jīng)做了注釋,在clampViewPositionVertical中我們對View的豎直方向活動(dòng)區(qū)域做了限制,防止滑出ViewGroup,當(dāng)然你可以直接return top;不過為了效果我先這么限定一下。還有一個(gè)clampViewPositionHorizontal方法,同樣是對其水平邊界進(jìn)行控制的,先不多說啦。這個(gè)時(shí)候咱們自定義的ViewGroup初期已經(jīng)完成,先去試試水。
在activity_main.xml中加入
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.maxi.viewdraghelpertest.MainActivity" > <com.maxi.viewdraghelpertest.widget.DragHelperLayout android:id="@+id/dhl" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="@android:color/darker_gray" > <TextView android:layout_width="match_parent" android:layout_height="100dp" android:background="@android:color/holo_blue_bright" /> <TextView android:layout_width="match_parent" android:layout_height="100dp" android:background="@android:color/holo_orange_dark" /> </com.maxi.viewdraghelpertest.widget.DragHelperLayout> </RelativeLayout>
運(yùn)行后的效果:
大家是不是都急了,做個(gè)拖動(dòng)加載怎么搞起這東西了,不要急,這才剛剛開始,大家想想拖動(dòng)加載是不是就是兩個(gè)View在同一個(gè)ViewGroup里通過ViewDragHelper的滑動(dòng)操作然后實(shí)現(xiàn)的?是不是有思路的?沒有思路也沒關(guān)系,咱慢慢來,想要兩個(gè)View相關(guān)聯(lián),就是拖動(dòng)一個(gè)View然后另一個(gè)View跟著它走該怎么實(shí)現(xiàn)呢?首先我們需要ViewDragHelper回調(diào)里的另一個(gè)方法onViewPositionChanged,該方法是在View位置發(fā)生改變時(shí)回調(diào)的。為的就是在上面的View上拉的時(shí)候讓下面的View跟著往上走。來看看我們的實(shí)現(xiàn)方法:
首先先將兩個(gè)View初始化:
private View t1, t2; /* * (non-Javadoc) * @see android.view.View#onFinishInflate() * 初始化兩個(gè)View */ @Override protected void onFinishInflate() { t1 = getChildAt(0); t2 = getChildAt(1); }
得到兩個(gè)View后我們在回調(diào)中判斷哪個(gè)位置發(fā)生了改變,
@Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { // TODO Auto-generated method stub int childIndex = 1; if (changedView == t2) { childIndex = 2; } viewFollowChanged(childIndex, top); }
上面的代碼段中有個(gè)方法viewFollowChanged,主要實(shí)現(xiàn)的就是View跟著動(dòng)。
private void viewFollowChanged(int viewIndex, int posTop) { viewH = t1.getMeasuredHeight(); if (viewIndex == 1) { int offsetTopBottom = viewH + t1.getTop() - t2.getTop(); t2.offsetTopAndBottom(offsetTopBottom); } else if (viewIndex == 2) { int offsetTopBottom = t2.getTop() - viewH - t1.getTop(); t1.offsetTopAndBottom(offsetTopBottom); } invalidate(); }
在運(yùn)行是不是發(fā)現(xiàn)沒有被點(diǎn)擊拖動(dòng)的View會(huì)跟著View一起移動(dòng),像一個(gè)整體雙宿雙飛。圖我就不加了,大家運(yùn)行看吧。因?yàn)槲覀円@取View的實(shí)際大小所以需要以下代碼段的支持:
@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)); } public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: if (specSize < size) { result = specSize | MEASURED_STATE_TOO_SMALL; } else { result = size; } break; case MeasureSpec.EXACTLY: result = specSize; break; } return result | (childMeasuredState & MEASURED_STATE_MASK); }
然后我們可以嘗試將兩個(gè)View滿屏,android:layout_height="match_parent",把clampViewPositionVertical方法里限制的邊界去掉吧,暫時(shí)先return top;這樣試一下是不是有點(diǎn)像拖動(dòng)加載了,呵呵噠,可是第一個(gè)View下拉的時(shí)候由于上面沒有View怎么辦?我們可以在clampViewPositionVertical中將它限定邊界?。?br />
@Override public int clampViewPositionVertical(View child, int top, int dy) { int slideTop = top; if (child == t1) { if (top > 0) { slideTop = 0; } } else if (child == t2) { if (top < 0) { slideTop = 0; } } return child.getTop() + (slideTop - child.getTop()); }
已經(jīng)大致成型了,然后就是拖動(dòng)的時(shí)候?qū)iew自動(dòng)置頂或置底,因?yàn)樽詣?dòng)置頂或置底是在滑動(dòng)松開之后,所以就需要用到ViewDragHelper回調(diào)里的onViewReleased方法,該方法就是在滑動(dòng)松開之后調(diào)用,接下來實(shí)現(xiàn)它:
@Override public void onViewReleased(View releasedChild, float xvel, float yvel) { putStickOrDown(releasedChild);// 滑動(dòng)松開后,需要置頂或置底 } private void putStickOrDown(View releasedChild, float yvel) { int finalTop = 0; // 默認(rèn)是粘到最頂端 if (releasedChild == t1) { // 滑動(dòng)第一個(gè)view松開 if (yvel < 0)//靈敏度自己調(diào)吧 finalTop = -viewH; } else { // 滑動(dòng)第二個(gè)view松開 if (yvel > 0)//同上 finalTop = viewH; } if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) { ViewCompat.postInvalidateOnAnimation(this);// 會(huì)在下一個(gè)Frame開始的時(shí)候,發(fā)起一些invalidate操作 } } @Override public void computeScroll() { if (mDragHelper.continueSettling(true)) { ViewCompat.postInvalidateOnAnimation(this); } }
ok,是可以自動(dòng)置頂或置底了。對了,那種拖動(dòng)粘滯效果可以設(shè)置clampViewPositionVertical里的返回值,return child.getTop() + (finalTop - child.getTop()) / num;num值越大越粘滯。
然后淘寶第一個(gè)View是可以滑動(dòng)的滑動(dòng)到最底部然后才把手勢事件交給ViewDragHelper處理的。這塊試想如果用ScrollView的話,手勢事件肯定會(huì)優(yōu)先被它消費(fèi),這樣肯定達(dá)不到我們想要的效果,所以在此我們需要對ScrollView進(jìn)行自定義,大致的實(shí)現(xiàn)思路是當(dāng)用戶用戶從觸發(fā)屏幕開始判斷是不是ScrollView在最底端,如果在最底端然后判斷手勢是否是向上滑動(dòng)的如果也是則滿足條件將touch事件交給父View就可以了,即requestDisallowInterceptTouchEvent該方法。然后自定義的ViewGroup中的onInterceptTouchEvent方法也要做相應(yīng)修改,這里用GestureDetectorCompat處理事件,其回調(diào)用來判斷是否是上下滑動(dòng)。先聲明private GestureDetectorCompat gestureDC;然后再gestureDC = new GestureDetectorCompat(context,new YSlideDetector());
class YSlideDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return Math.abs(distanceY) > Math.abs(distanceX);//Y方向絕對值大于X方向,上下滑動(dòng) } }<pre name="code" class="java"> @Override public boolean onInterceptTouchEvent(MotionEvent event) { boolean is_y_slide = gestureDC.onTouchEvent(event); boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(event); int action = event.getActionMasked(); if (action == MotionEvent.ACTION_DOWN) { mDragHelper.processTouchEvent(event);// action_down時(shí)就讓mDragHelper開始工作,否則有時(shí)候?qū)е庐惓? } return shouldIntercept && is_y_slide; }
OK。就這樣。是不是達(dá)到了想要的效果了?
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)ImageView圖片縮放和拖動(dòng)
- Android實(shí)現(xiàn)跟隨手指拖動(dòng)并自動(dòng)貼邊的View樣式(實(shí)例demo)
- Android自定義View實(shí)現(xiàn)拖動(dòng)選擇按鈕
- Android實(shí)現(xiàn)單頁面浮層可拖動(dòng)view的一種方法
- Android通過自定義ImageView控件實(shí)現(xiàn)圖片的縮放和拖動(dòng)的實(shí)現(xiàn)代碼
- Android開發(fā)實(shí)現(xiàn)可拖動(dòng)排序的ListView功能【附源碼下載】
- Android DragImageView實(shí)現(xiàn)下拉拖動(dòng)圖片放大效果
- Android RecyclerView滑動(dòng)刪除和拖動(dòng)排序
- Android自定義View圓形和拖動(dòng)圓、跟隨手指拖動(dòng)效果
- android實(shí)現(xiàn)可拖動(dòng)的浮動(dòng)view
相關(guān)文章
Android實(shí)現(xiàn)微信側(cè)滑關(guān)閉頁面效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)微信側(cè)滑關(guān)閉頁面效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android6.0 storage目錄sd卡存儲(chǔ)的路徑創(chuàng)建詳解
這篇文章主要介紹了Android6.0 storage目錄sd卡存儲(chǔ)的路徑創(chuàng)建的相關(guān)資料,需要的朋友可以參考下2017-01-01android studio實(shí)現(xiàn)計(jì)算器
這篇文章主要為大家詳細(xì)介紹了android studio實(shí)現(xiàn)計(jì)算器的具體方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07Android自定義View實(shí)現(xiàn)黑客帝國數(shù)字雨效果
這篇文章主要給大家介紹了關(guān)于Android自定義View實(shí)現(xiàn)黑客帝國數(shù)字雨效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08Android 使用Vitamio打造自己的萬能播放器(9)—— 在線播放 (在線電視)
本文主要介紹Android 使用Vitamio開發(fā)播放器,實(shí)現(xiàn)在線電視播放,這里提供效果圖和實(shí)例代碼以便大家參考,2016-07-07Android AIDL——進(jìn)程通信機(jī)制詳解
這篇文章主要介紹了Android AIDL——進(jìn)程通信機(jī)制詳解的相關(guān)資料,并附簡單實(shí)例,和實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-10-10Android編程實(shí)現(xiàn)調(diào)用相冊、相機(jī)及拍照后直接裁剪的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)調(diào)用相冊、相機(jī)及拍照后直接裁剪的方法,涉及Android拍照及圖形處理相關(guān)操作技巧,需要的朋友可以參考下2017-02-02android Launcher3設(shè)置默認(rèn)桌面應(yīng)用
這篇文章主要為大家詳細(xì)介紹了android Launcher3設(shè)置默認(rèn)桌面應(yīng)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07總結(jié)Android中MD風(fēng)格相關(guān)控件
自Android5.0發(fā)布以來,谷歌推出全新的Material Desigen設(shè)計(jì)風(fēng)格,時(shí)過一年多了,在國內(nèi)也看到很多應(yīng)用在慢慢適應(yīng)MD設(shè)計(jì)風(fēng)格。今天小編給大家總結(jié)下Android中MD風(fēng)格相關(guān)控件的知識(shí),有需要的可以參考學(xué)習(xí)。2016-08-08