国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > Android > 正文

Android ViewDragHelper仿淘寶拖動(dòng)加載效果

2019-12-12 02:15:57
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

拖動(dòng)加載是我在淘寶的商品詳情界面發(fā)現(xiàn)的,感覺(jué)很實(shí)用。于是就分析它的實(shí)現(xiàn)方式,感覺(jué)用ViewDragHelper可以很方便的實(shí)現(xiàn)這種效果。下面大致把我的思路分步驟寫(xiě)一下。先上圖吧。

首先建工程什么的我就不多說(shuō)了。咱從ViewDragHelper的實(shí)現(xiàn)開(kāi)始說(shuō)吧,ViewDragHelper一般用在一個(gè)自定義ViewGroup的內(nèi)部,可以對(duì)其子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ù)一:該類(lèi)生成的對(duì)象(當(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; } 

接著我們開(kāi)始實(shí)現(xiàn)DragHelperCallback,這個(gè)ViewDragHelper.Callback回調(diào)中可以對(duì)ViewGroup中的一些View進(jìn)行操作,在此我們只對(duì)本項(xiàng)目涉及到的相關(guān)用法做解析,詳細(xì)點(diǎn)請(qǐng)自行查閱資料。

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)做了注釋?zhuān)赾lampViewPositionVertical中我們對(duì)View的豎直方向活動(dòng)區(qū)域做了限制,防止滑出ViewGroup,當(dāng)然你可以直接return top;不過(guò)為了效果我先這么限定一下。還有一個(gè)clampViewPositionHorizontal方法,同樣是對(duì)其水平邊界進(jìn)行控制的,先不多說(shuō)啦。這個(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)加載怎么搞起這東西了,不要急,這才剛剛開(kāi)始,大家想想拖動(dòng)加載是不是就是兩個(gè)View在同一個(gè)ViewGroup里通過(guò)ViewDragHelper的滑動(dòng)操作然后實(shí)現(xiàn)的?是不是有思路的?沒(méi)有思路也沒(méi)關(guān)系,咱慢慢來(lái),想要兩個(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跟著往上走。來(lái)看看我們的實(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后我們?cè)诨卣{(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)沒(méi)有被點(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滿(mǎn)屏,android:layout_height="match_parent",把clampViewPositionVertical方法里限制的邊界去掉吧,暫時(shí)先return top;這樣試一下是不是有點(diǎn)像拖動(dòng)加載了,呵呵噠,可是第一個(gè)View下拉的時(shí)候由于上面沒(méi)有View怎么辦?我們可以在clampViewPositionVertical中將它限定邊界啊!

@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)松開(kāi)之后,所以就需要用到ViewDragHelper回調(diào)里的onViewReleased方法,該方法就是在滑動(dòng)松開(kāi)之后調(diào)用,接下來(lái)實(shí)現(xiàn)它:

@Override     public void onViewReleased(View releasedChild, float xvel, float yvel) {       putStickOrDown(releasedChild);// 滑動(dòng)松開(kāi)后,需要置頂或置底     }     private void putStickOrDown(View releasedChild, float yvel) {     int finalTop = 0; // 默認(rèn)是粘到最頂端     if (releasedChild == t1) {       // 滑動(dòng)第一個(gè)view松開(kāi)       if (yvel < 0)//靈敏度自己調(diào)吧         finalTop = -viewH;     } else {       // 滑動(dòng)第二個(gè)view松開(kāi)       if (yvel > 0)//同上         finalTop = viewH;     }     if (mDragHelper.smoothSlideViewTo(releasedChild, 0, finalTop)) {       ViewCompat.postInvalidateOnAnimation(this);// 會(huì)在下一個(gè)Frame開(kāi)始的時(shí)候,發(fā)起一些invalidate操作     }     }      @Override     public void computeScroll() {       if (mDragHelper.continueSettling(true)) {         ViewCompat.postInvalidateOnAnimation(this);       }     } 

ok,是可以自動(dòng)置頂或置底了。對(duì)了,那種拖動(dòng)粘滯效果可以設(shè)置clampViewPositionVertical里的返回值,return child.getTop() + (finalTop - child.getTop()) / num;num值越大越粘滯。

然后淘寶第一個(gè)View是可以滑動(dòng)的滑動(dòng)到最底部然后才把手勢(shì)事件交給ViewDragHelper處理的。這塊試想如果用ScrollView的話,手勢(shì)事件肯定會(huì)優(yōu)先被它消費(fèi),這樣肯定達(dá)不到我們想要的效果,所以在此我們需要對(duì)ScrollView進(jìn)行自定義,大致的實(shí)現(xiàn)思路是當(dāng)用戶(hù)用戶(hù)從觸發(fā)屏幕開(kāi)始判斷是不是ScrollView在最底端,如果在最底端然后判斷手勢(shì)是否是向上滑動(dòng)的如果也是則滿(mǎn)足條件將touch事件交給父View就可以了,即requestDisallowInterceptTouchEvent該方法。然后自定義的ViewGroup中的onInterceptTouchEvent方法也要做相應(yīng)修改,這里用GestureDetectorCompat處理事件,其回調(diào)用來(lái)判斷是否是上下滑動(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方向絕對(duì)值大于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開(kāi)始工作,否則有時(shí)候?qū)е庐惓?  }   return shouldIntercept && is_y_slide; } 

OK。就這樣。是不是達(dá)到了想要的效果了?

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 莆田市| 西乌| 柯坪县| 玛曲县| 彰武县| 章丘市| 潼关县| 陆良县| 建平县| 葵青区| 海伦市| 阳东县| 刚察县| 上栗县| 瑞昌市| 长子县| 墨玉县| 九台市| 定南县| 安福县| 抚松县| 濮阳市| 福鼎市| 喀喇| 保靖县| 瑞金市| 武强县| 海丰县| 色达县| 丰城市| 昆山市| 宝山区| 西乌珠穆沁旗| 稻城县| 太和县| 长子县| 咸宁市| 保定市| 普兰县| 阳原县| 黔西|