亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android ViewDragHelper使用介紹

 更新時(shí)間:2017年08月03日 09:39:58   作者:mChenys  
ViewDragHelper是support.v4下提供的用于處理拖拽滑動(dòng)的輔助類。接下來(lái)通過(guò)本文給大家介紹Android ViewDragHelper使用,感興趣的朋友一起看看吧

ViewDragHelper是support.v4下提供的用于處理拖拽滑動(dòng)的輔助類,查看Android的DrawerLayout源碼,可以發(fā)現(xiàn),它內(nèi)部就是使用了該輔助類來(lái)處理滑動(dòng)事件的.

public DrawerLayout(Context context, AttributeSet attrs, int defStyle) { 
  super(context, attrs, defStyle); 
  setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); 
  final float density = getResources().getDisplayMetrics().density; 
  mMinDrawerMargin = (int) (MIN_DRAWER_MARGIN * density + 0.5f); 
  final float minVel = MIN_FLING_VELOCITY * density; 
  mLeftCallback = new ViewDragCallback(Gravity.LEFT); 
  mRightCallback = new ViewDragCallback(Gravity.RIGHT); 
  mLeftDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mLeftCallback); 
  mLeftDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT); 
  mLeftDragger.setMinVelocity(minVel); 
  mLeftCallback.setDragger(mLeftDragger); 
 
  mRightDragger = ViewDragHelper.create(this, TOUCH_SLOP_SENSITIVITY, mRightCallback); 
  mRightDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_RIGHT); 
  mRightDragger.setMinVelocity(minVel); 
  mRightCallback.setDragger(mRightDragger); 
 
  //省略 
 } 

有了ViewDragHelper,我們?cè)趯懪c拖拽滑動(dòng)相關(guān)的自定義控件的時(shí)候就變得非常簡(jiǎn)單了,例如我們可以用來(lái)實(shí)現(xiàn)自定義側(cè)滑菜單,再也不需要在onTouchEvent方法里計(jì)算滑動(dòng)距離來(lái)改變布局邊框的位置了.

使用ViewDragHelper類的大體步驟分為3步:

步驟1.在自定義的ViewGroup子類下通過(guò)ViewDragHelper的靜態(tài)方法獲取到ViewDragHelper的實(shí)例引用,注意它是一個(gè)單例的.

查看源碼:

/** 
 * Factory method to create a new ViewDragHelper. 
 * 
 * @param forParent Parent view to monitor 
 * @param cb Callback to provide information and receive events 
 * @return a new ViewDragHelper instance 
 */ 
 public static ViewDragHelper create(ViewGroup forParent, Callback cb) { 
  return new ViewDragHelper(forParent.getContext(), forParent, cb); 
 } 

可以發(fā)現(xiàn)它需要接收2個(gè)參數(shù),參數(shù)1就是當(dāng)前要使用ViewDragHelper的自定義控件的引用,Callback是一個(gè)回調(diào)抽象類,該回調(diào)接口是用于建立當(dāng)前自定義控件與ViewDragHelper溝通的橋梁,Callback內(nèi)定義了多個(gè)回調(diào)函數(shù),這些回調(diào)函數(shù)涵蓋了與當(dāng)前自定義控件相關(guān)的是否允許拖拽,當(dāng)前拖拽的View是哪一個(gè)view,拖拽的view的位置如何變化,釋放的時(shí)候那個(gè)view被釋放了,釋放時(shí)的速度是怎么樣的等等.稍后會(huì)詳細(xì)介紹.

步驟2.有了ViewDragHelper的引用后,我們就需要傳遞相關(guān)的觸摸事件給ViewDragHelper來(lái)幫我們處理,那么怎么傳遞呢?

可以通過(guò)重寫onInterceptTouchEvent和onTouchEvent這2個(gè)函數(shù)來(lái)傳遞,前者是用于決定是否要攔截中斷事件的,后者是用于消費(fèi)觸摸事件的,如果前者return true則表示事件需要被攔截,那么事件就會(huì)直接回調(diào)給onTouchEvent去處理,如果onTouchEvent返回true,則事件被消費(fèi),返回false則向上返回它的父類調(diào)用處,如果事件在向上層層返回的過(guò)程中沒有被處理的話,那么事件最終將會(huì)消失;當(dāng)然,如果onInterceptTouchEvent返回false的話,那么事件就會(huì)繼續(xù)向下傳遞個(gè)它的直接子View去分發(fā)處理,關(guān)于事件分發(fā)的更多理論知識(shí),大家可以看這篇文章事件分發(fā)機(jī)制的原理總結(jié).

實(shí)例代碼:

@Override 
public boolean onInterceptTouchEvent(MotionEvent ev) { 
 //由ViewDragHelper類來(lái)決定是否要攔截事件 
 return dragHelper.shouldInterceptTouchEvent(ev); 
} 
@Override 
public boolean onTouchEvent(MotionEvent event) { 
 try { 
  //由ViewDragHelper類來(lái)決定是否要處理觸摸事件,這里可能有異常 
  dragHelper.processTouchEvent(event); 
 } catch (Exception e) { 
  e.printStackTrace(); 
 } 
 //返回true,可以持續(xù)接收到后續(xù)事件 
 return true; 
} 

步驟3:重寫ViewDragHelper.Callback()的相關(guān)回調(diào)方法,處理事件,大體有如下方法:

下面將通過(guò)demo來(lái)分別介紹幾個(gè)常用的方法,先來(lái)看看demo的整體代碼,這是一個(gè)自定義的ViewGroup的子類,里面有2個(gè)子控件,分別是側(cè)邊欄和主體布局

/** 
 * Created by mChenys on 2015/12/16. 
 */ 
public class DragLayout extends FrameLayout { 
 private String TAG = "DragLayout"; 
 private ViewDragHelper dragHelper; 
 private LinearLayout mLeftContent; //左側(cè)面板 
 private LinearLayout mMainContent;//主體面板 
 
 public DragLayout(Context context) { 
  this(context, null); 
 } 
 
 public DragLayout(Context context, AttributeSet attrs) { 
  this(context, attrs, 0); 
 } 
 
 public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) { 
  super(context, attrs, defStyleAttr); 
  init(); 
 } 
 //重寫此方法,可以獲取該容器下的所有的直接子View 
 @Override 
 protected void onFinishInflate() { 
  super.onFinishInflate(); 
  mLeftContent = (LinearLayout) getChildAt(0); 
  mMainContent = (LinearLayout) getChildAt(1); 
 } 
  
 private void init() { 
  //step1 通過(guò)ViewDragHelper的單例方法獲取ViewDragHelper的實(shí)例 
  dragHelper = ViewDragHelper.create(this, mCallback); 
 } 
 //step2 傳遞觸摸事件,需要重寫onInterceptTouchEvent和onTouchEvent 
 @Override 
 public boolean onInterceptTouchEvent(MotionEvent ev) { 
  //由ViewDragHelper類來(lái)決定是否要攔截事件 
  return dragHelper.shouldInterceptTouchEvent(ev); 
 } 
 @Override 
 public boolean onTouchEvent(MotionEvent event) { 
  try { 
   //由ViewDragHelper類來(lái)決定是否要處理觸摸事件 
   dragHelper.processTouchEvent(event); 
  } catch (Exception e) { 
   e.printStackTrace(); 
  } 
  //返回true,可以持續(xù)接收到后續(xù)事件 
  return true; 
 } 
 //step3 重寫ViewDragHelper.Callback()的相關(guān)回調(diào)方法,處理事件 
 private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() { 
  /** 
   * 1.改方法是abstract的方法,必須要實(shí)現(xiàn),其返回結(jié)果決定當(dāng)前child是否可以拖拽 
   * @param child 當(dāng)前被拖拽的view 
   * @param pointerId pointerId 區(qū)分多點(diǎn)觸摸的id 
   * @return true表示允許拖拽, false則不允許拖拽 ,默認(rèn)返回false 
   */ 
  @Override 
  public boolean tryCaptureView(View child, int pointerId) { 
   Log.d(TAG, "tryCaptureView:當(dāng)前被拖拽的view:" + child); 
   return false; 
  } 
 }; 
} 

布局文件:

<?xml version="1.0" encoding="utf-8"?> 
<mchenys.net.csdn.blog.mytencentqq.view.DragLayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:id="@+id/dragLayout" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent"> 
 <!--左側(cè)--> 
 <LinearLayout 
  android:id="@+id/layout_left" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@android:color/holo_red_light" 
  android:orientation="vertical"> 
  <TextView 
   android:layout_width="match_parent" 
   android:layout_height="match_parent" 
   android:gravity="center" 
   android:text="左側(cè)" 
   android:textSize="30sp" /> 
 </LinearLayout> 
 <!--主體布局--> 
 <LinearLayout 
  android:id="@+id/layout_main" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:background="@android:color/holo_blue_light" 
  android:orientation="vertical"> 
  <TextView 
   android:layout_width="match_parent" 
   android:layout_height="match_parent" 
   android:gravity="center" 
   android:text="主體" 
   android:textSize="30sp" /> 
 </LinearLayout> 
</mchenys.net.csdn.blog.mytencentqq.view.DragLayout> 

運(yùn)行效果:

打印的log

D/DragLayout: tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
D/DragLayout: tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
D/DragLayout: tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
D/DragLayout: tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{32f4d44b V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 

由上面的log可以知道tryCaptureView方法被執(zhí)行了,但是mMainContent卻沒有被拖動(dòng)出來(lái),只是為什么呢,因?yàn)閠ryCaptureView返回了false.默認(rèn)是返回false的,下面修改為mMainContent可以拖動(dòng),mLeftContent不可以拖動(dòng):

@Override 
public boolean tryCaptureView(View child, int pointerId) { 
 Log.d(TAG, "tryCaptureView:當(dāng)前被拖拽的view:" + child); 
 if (child == mMainContent) { 
  return true; //只有主題布局可以被拖動(dòng) 
 } 
 return false; 
} 

運(yùn)行效果還是移動(dòng)不了,這是為什么呢?

這是以因?yàn)槲覀冞€沒有重寫clampViewPositionHorizontal方法,下面將介紹該方法的使用

/** 
 * 根據(jù)建議值修正將要移動(dòng)到的橫向位置,此時(shí)沒有發(fā)生真正的移動(dòng) 
 * @param child 當(dāng)前被拖拽的View 
 * @param left 新的建議值 
 * @param dx 水平位置的變化量 
 * @return 
 */ 
@Override 
public int clampViewPositionHorizontal(View child, int left, int dx) { 
 Log.d(TAG, "clampViewPositionHorizontal:" + "舊的left坐標(biāo)oldLeft:" + child.getLeft() 
   + " 水平位置的變化量dx:" + dx + " 新的建議值left:" + left); 
  
 return super.clampViewPositionHorizontal(child, left, dx); //父類默認(rèn)返回0 
} 

該方法返回的是水平方向的移動(dòng)建議值,該建議值等于當(dāng)前的X坐標(biāo)+水平方向的變化量,向右移動(dòng),偏移量為正值,向左移動(dòng)則為負(fù)數(shù).默認(rèn)返回的是調(diào)用父類的重寫的方法,查看源碼可以發(fā)現(xiàn)默認(rèn)返回的是0,如果建議值等于0的話,就表示水平方向不會(huì)移動(dòng).如果想要移動(dòng),我們需要返回它提供的建議值left,我們來(lái)看看運(yùn)行的log:

tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{23a3c537 V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:1 新的建議值left:1 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:12 新的建議值left:12 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:63 新的建議值left:63 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:53 新的建議值left:53 
tryCaptureView:當(dāng)前被拖拽的view:android.widget.LinearLayout{23a3c537 V.E..... ........ 0,0-720,1134 #7f0c0052 app:id/layout_main} 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:-5 新的建議值left:-5 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:-2 新的建議值left:-2 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:-6 新的建議值left:-6 
clampViewPositionHorizontal:舊的left坐標(biāo)oldLeft:0 水平位置的變化量dx:-11 新的建議值left:-11 

由上面的log可以看出,分別是向右拖拽和向左拖拽的結(jié)果,如果我們返回了它的建議值,就可以實(shí)現(xiàn)水平方向的拖動(dòng)了.

將clampViewPositionHorizontal的返回值修改成return left;看看運(yùn)行效果:

跟我們預(yù)想的結(jié)果一樣,只有主體布局可以滑動(dòng),左側(cè)的布局不能滑動(dòng),如果想要左側(cè)布局也可以滑動(dòng),那么只需要在tryCaptureView直接返回true即可.效果如下:

同樣的,如果要實(shí)現(xiàn)垂直方向的拖拽滾動(dòng),就需要重新下面這個(gè)方法了.

/** 
 * 根據(jù)建議值修正將要移動(dòng)到的縱向位置,此時(shí)沒有發(fā)生真正的移動(dòng) 
 * @param child 當(dāng)前被拖拽的View 
 * @param top 新的建議值 
 * @param dy 垂直位置的變化量 
 * @return 
 */ 
@Override 
public int clampViewPositionVertical(View child, int top, int dy) { 
 Log.d(TAG, "clampViewPositionHorizontal:" + "舊的top坐標(biāo)oldTop:" + child.getTop() 
   + " 垂直位置的變化量dy:" + dy + " 新的建議值top:" + top); 
 return top; 
} 

 效果如下,可以隨意的滑動(dòng),實(shí)現(xiàn)起來(lái)是不是很簡(jiǎn)單啊

繼續(xù)介紹剩下的回調(diào)方法

/** 
 * 當(dāng)capturedChild被捕獲時(shí)調(diào)用 
 * @param capturedChild 當(dāng)前被拖拽的view 
 * @param activePointerId 
 */ 
@Override 
public void onViewCaptured(View capturedChild, int activePointerId) { 
 Log.d(TAG, "onViewCaptured:當(dāng)前被拖拽的view:" + capturedChild); 
 super.onViewCaptured(capturedChild, activePointerId); 
} 

該回調(diào)方法和tryCaptureView一樣都可以獲取到當(dāng)前被拖拽的View,不同點(diǎn)在于tryCaptureView是可以決定哪個(gè)View是可以被拖拽滑動(dòng)的,而onViewCaptured只是用來(lái)獲取當(dāng)前到底哪個(gè)View被正在拖拽而已.

/** 
 * 返回拖拽的范圍,不對(duì)拖拽進(jìn)行真正的限制,僅僅決定了動(dòng)畫執(zhí)行的速度 
 * @param child 
 * @return 返回一個(gè)固定值 
 */ 
@Override 
public int getViewHorizontalDragRange(View child) { 
 int range = super.getViewHorizontalDragRange(child); 
 Log.d(TAG, "被拖拽的范圍getViewHorizontalDragRange:" + range); 
 return range; 
} 

該回調(diào)方法是用于決定水平方向的動(dòng)畫執(zhí)行速度,相對(duì)的垂直方向肯定也會(huì)有相應(yīng)的方法,沒錯(cuò),就是下面這個(gè):

@Override 
public int getViewVerticalDragRange(View child) { 
 return super.getViewVerticalDragRange(child); 
}

那么話又說(shuō)回來(lái),我們有什么辦法可以限制子View的滑動(dòng)范圍呢,如果范圍不能很好的控制的話,那滑動(dòng)也沒有什么意義了.還記得上面介紹的clampViewPositionHorizontal和clampViewPositionVertical嗎,分別用于設(shè)置水平方向和垂直方向的移動(dòng)建議值,假設(shè)我們要限制該自定義控件的子View在水平方向移動(dòng)的范圍為0-屏幕寬度的0.6,那么如何控制呢.要實(shí)現(xiàn)這個(gè)限制,我們現(xiàn)在獲取屏幕的寬度,由于當(dāng)前的自定義控件是全屏顯示的,所以直接就可以那控件的寬度來(lái)作為屏幕的寬度,那么如何獲取呢?有2種方式,分別是在onMeasure和onSizeChange方法里面調(diào)用getMeasuredWidth()方法獲取.前者是測(cè)量完之后獲取,后者是當(dāng)控件的寬高發(fā)生變化后獲取,例如:

@Override 
protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
 super.onSizeChanged(w, h, oldw, oldh); 
 // 當(dāng)尺寸有變化的時(shí)候調(diào)用 
 mHeight = getMeasuredHeight(); 
 mWidth = getMeasuredWidth(); 
 // 移動(dòng)的范圍 
 mRange = (int) (mWidth * 0.6f); 
} 

接下來(lái),在clampViewPositionHorizontal方法內(nèi)部做判斷,如果當(dāng)前的建議值left超過(guò)了mRange,那么返回mRange,如果小于了0,則返回0,這樣一來(lái)子View的滑動(dòng)范圍就限定在0-mRange之間了,修改代碼如下:

@Override 
public int clampViewPositionHorizontal(View child, int left, int dx) { 
 Log.d(TAG, "clampViewPositionHorizontal 建議值left:" + left + " mRange:" + mRange); 
 if (left > mRange) { 
  left = mRange; 
 } else if (left < 0) { 
  left = 0; 
 } 
 return left; 
} 

如果垂直方向也想限定的話,那就修改clampViewPositionVertical返回的建議值

@Override 
public int clampViewPositionVertical(View child, int top, int dy) { 
 Log.d(TAG, "clampViewPositionVertical 建議值top:" + top + " mRange:" + mRange); 
 if (top > mRange) { 
  top = mRange; 
 } else if (top < 0) { 
  top = 0; 
 } 
 return top; 
} 

來(lái)看看運(yùn)行的效果:

如此一來(lái),我們就可以隨意的控制子View的拖拽滑動(dòng)的范圍了.那么新的問(wèn)題又來(lái)的,如果現(xiàn)在的需求是只能mMainContent被拖拽,mLeftContent不能被拖拽,也許你會(huì)說(shuō),這還不簡(jiǎn)單嗎,直接在tryCaptureView判斷當(dāng)前拖拽的子View是mLeftContent的話就返回false不就得了,沒錯(cuò),如果需求只是這樣的話確實(shí)可以滿足了,但是如果在加上一個(gè)條件,那就是拖拽mLeftContent的時(shí)候的效果相當(dāng)于把mMainContent拖拽了,什么意思呢,也就說(shuō)現(xiàn)在mMainContent已經(jīng)是打開的狀態(tài)了,我想通過(guò)滑動(dòng)mLeftContent就能把mMainContent滑動(dòng)了.而mLeftContent還是原來(lái)的位置不動(dòng).這個(gè)要怎么實(shí)現(xiàn)呢?

首先可以肯定的是,tryCaptureView方法必須返回true,表示mMainContent和mLeftContent都可以被滑動(dòng),接下來(lái)要處理的就是如何在mLeftContent滑動(dòng)的時(shí)候是滑動(dòng)mMainContent的.那么現(xiàn)在就要介紹另一個(gè)回調(diào)方法了,如下所示:

/** 
 * 當(dāng)View的位置發(fā)生變化的時(shí)候,處理要做的事情(更新狀態(tài),伴隨動(dòng)畫,重繪界面) 
 * 此時(shí),View已經(jīng)發(fā)生了位置的改變 
 * 
 * @param changedView 改變位置的View 
 * @param left  新的左邊值 
 * @param top   新的上邊值 
 * @param dx   水平方向的變化量 
 * @param dy   垂直方向的變化量 
 */ 
@Override 
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 
 Log.d(TAG, "位置發(fā)生變化onViewPositionChanged:" + "新的左邊值left: " + left + " 水平方向的變化量dx:" + dx 
   + " 新的上邊值top:" + top + " 垂直方向的變化量dy:" + dy); 
 super.onViewPositionChanged(changedView, left, top, dx, dy); 
 // 為了兼容低版本, 每次修改值之后, 進(jìn)行重繪 
 invalidate(); 
} 

該方法是隨著View的位置發(fā)生變化而不斷回調(diào)的.四個(gè)參數(shù)如上面的注釋所述,通過(guò)該方法可以拿到當(dāng)前正在拖拽滑動(dòng)的View是哪個(gè)View,有了這依據(jù)之后,我們就將在mLeftContent上的滑動(dòng)的水平方向和垂直方向的變化量傳遞給mMainContent,這樣一來(lái),滑動(dòng)mLeftContent的效果不就等于滑動(dòng)mMainContent了嗎.需要注意的是,該回調(diào)方法在低版本上為了兼容還需要加上invalidate();這句代碼,invalidate是用來(lái)刷新界面的,他會(huì)導(dǎo)致界面的重繪.

滑動(dòng)mMainContent來(lái)看看log

D/DragLayout: 位置發(fā)生變化onViewPositionChanged:新的左邊值left: 15 水平方向的變化量dx:15 新的上邊值top:8 垂直方向的變化量dy:8 
D/DragLayout: 位置發(fā)生變化onViewPositionChanged:新的左邊值left: 32 水平方向的變化量dx:17 新的上邊值top:16 垂直方向的變化量dy:8 
D/DragLayout: 位置發(fā)生變化onViewPositionChanged:新的左邊值left: 121 水平方向的變化量dx:89 新的上邊值top:46 垂直方向的變化量dy:30 
D/DragLayout: 位置發(fā)生變化onViewPositionChanged:新的左邊值left: 145 水平方向的變化量dx:24 新的上邊值t 
由log可以看出,最新的left和top值是等于上一次的位置+水平/垂直方向的變化量.這個(gè)特點(diǎn)有點(diǎn)類似建議值了.不同的是建議值發(fā)生了改變不代表View就一定已經(jīng)處于滑動(dòng),除非返回的值也是建議值,但是onViewPositionChanged方法就不同了,這個(gè)方法只要一執(zhí)行,就說(shuō)明目標(biāo)View是處于滑動(dòng)狀態(tài)的.

以水平方向滑動(dòng)為例,垂直方向不移動(dòng),接下來(lái)就可以在onViewPositionChanged方法內(nèi)做判斷了,如下所示:

@Override 
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { 
 super.onViewPositionChanged(changedView, left, top, dx, dy); 
 //獲取最新的left坐標(biāo) 
 int newLeft = left; 
 if (changedView == mLeftContent) { 
  //如果滑動(dòng)的是mLeftContent,則將其水平變化量dx傳遞給mMainContent,記錄在newLeft中 
  newLeft = mMainContent.getLeft() + dx; 
 } 
 //矯正范圍 
 if (newLeft > mRange) { 
  newLeft = mRange; 
 } else if (newLeft < 0) { 
  newLeft = 0; 
 } 
 //再次判斷,限制mLeftContent的滑動(dòng) 
 if (changedView == mLeftContent) { 
  //強(qiáng)制將mLeftContent的位置擺會(huì)原來(lái)的位置,這里通過(guò)layout方法傳入左,上,右,下坐標(biāo)來(lái)實(shí)現(xiàn) 
  //當(dāng)然方法不限于這一種,例如還可以通過(guò)scrollTo(x,y)方法 
  mLeftContent.layout(0, 0, mWidth, mHeight); 
  //改變mMainContent的位置 
  mMainContent.layout(newLeft, 0, newLeft + mWidth, mHeight); 
 } 
 // 為了兼容低版本, 每次修改值之后, 進(jìn)行重繪 
 invalidate(); 
} 

效果圖:

由上面的效果圖可以發(fā)現(xiàn)已經(jīng)可以實(shí)現(xiàn)當(dāng)手指向右滑動(dòng)mLeftContent時(shí),滑動(dòng)的效果等于向右滑動(dòng)mMainContent,當(dāng)同時(shí)也會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,那就是手指在mLeftContent向左滑動(dòng)的時(shí)候并沒有效果,這是因?yàn)槲覀兿拗屏俗覸iew的滑動(dòng)范圍就是0-mRange,所以,如果滑動(dòng)時(shí)小于0是沒有效果的.那如果我們想要實(shí)現(xiàn)在mLeftContent當(dāng)手指有向左滑動(dòng)的趨勢(shì),或者手指在mMainContent有向左滑動(dòng)的趨勢(shì)時(shí),就關(guān)閉mLeftContent,讓mMainContent自動(dòng)向左滑動(dòng)到x=0的位置,反之就是打開mLeftContent,讓mMainContent滑動(dòng)到x=mRange的位置,這個(gè)要怎么實(shí)現(xiàn)呢?首先我們要能夠想到的時(shí),這個(gè)向左滑動(dòng)的趨勢(shì)肯定是與手指松手后相關(guān)的,那有沒有一個(gè)回調(diào)方法是與手指觸摸松開相關(guān)的呢?下面將介紹另一個(gè)回調(diào)方法,如下所示:

/** 
 * 當(dāng)被拖拽的View釋放的時(shí)候回調(diào),通常用于執(zhí)行收尾的操作(例如執(zhí)行動(dòng)畫) 
 * @param releasedChild 被釋放的View 
 * @param xvel 水平方向的速度,向右釋放為正值,向左為負(fù)值 
 * @param yvel 垂直方向的速度,向下釋放為正值,向上為負(fù)值 
 */ 
@Override 
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
 Log.d(TAG, "View被釋放onViewReleased:" + "釋放時(shí)水平速度xvel:" + xvel + " 釋放時(shí)垂直速度yvel:" + yvel); 
 super.onViewReleased(releasedChild, xvel, yvel); 
} 

有了這個(gè)方法,我們就可以實(shí)現(xiàn)我們剛剛說(shuō)到的效果了,首先我們來(lái)考慮下那些情況是和打開mLeftContent相關(guān)的,有2種情況:

1.當(dāng)前水平方向的速度xvel=0,同時(shí)mMainContent的x位置是大于mRange/2.0f的.

2.當(dāng)前水平方向的速度xvel>0

考慮了所有打開的情況,那么剩下的情況就是關(guān)閉mLeftContent.

具體邏輯如下:

@Override 
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
 Log.d(TAG, "View被釋放onViewReleased:" + "釋放時(shí)水平速度xvel:" + xvel + " 釋放時(shí)垂直速度yvel:" + yvel); 
 super.onViewReleased(releasedChild, xvel, yvel); 
 if (xvel > 0 || (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f)) { 
  //打開mLeftContent,即mMainContent的x=mRange 
  mMainContent.layout(mRange, 0, mRange + mWidth, mHeight); 
 } else { 
  //關(guān)閉mLeftContent,即mMainContent的x=0 
  mMainContent.layout(0, 0, mWidth, mHeight); 
 } 
} 

效果圖:

細(xì)心的話,可以發(fā)現(xiàn)上面的打開和關(guān)閉動(dòng)畫都是瞬間完成的,看起來(lái)效果不怎么好,如何實(shí)現(xiàn)平滑的打開和關(guān)閉呢?

通過(guò)ViewDragHelper的smoothSlideViewTo(View child, int finalLeft, int finalTop)方法可以實(shí)現(xiàn)平滑的滾動(dòng)目標(biāo)View到指定的left或者top坐標(biāo)位置,接收3個(gè)參數(shù),參數(shù)child表示要滑動(dòng)的目標(biāo)View,finalLeft和finalTop表示目標(biāo)view最終平滑滑動(dòng)到的位置.翻看源碼,發(fā)現(xiàn)其實(shí)現(xiàn)原理是通過(guò)Scroller對(duì)象來(lái)實(shí)現(xiàn)的,也就說(shuō)我們還需要重寫computeScroll方法,不斷的刷新當(dāng)前界面,具體設(shè)置如下:

@Override 
public void onViewReleased(View releasedChild, float xvel, float yvel) { 
 Log.d(TAG, "View被釋放onViewReleased:" + "釋放時(shí)水平速度xvel:" + xvel + " 釋放時(shí)垂直速度yvel:" + yvel); 
 super.onViewReleased(releasedChild, xvel, yvel); 
 if (xvel > 0 || (xvel == 0 && mMainContent.getLeft() > mRange / 2.0f)) { 
  //打開mLeftContent,即mMainContent的x=mRange 
  if (dragHelper.smoothSlideViewTo(mMainContent, mRange, 0)) { 
   // 返回true代表還沒有移動(dòng)到指定位置, 需要刷新界面. 
   // 參數(shù)傳this(child所在的ViewGroup) 
   ViewCompat.postInvalidateOnAnimation(DragLayout.this); 
  } 
 } else { 
  //關(guān)閉mLeftContent,即mMainContent的x=0 
  if (dragHelper.smoothSlideViewTo(mMainContent, 0, 0)) { 
   ViewCompat.postInvalidateOnAnimation(DragLayout.this); 
  } 
 } 
@Override 
public void computeScroll() { 
 super.computeScroll(); 
 // 2. 持續(xù)平滑動(dòng)畫 (高頻率調(diào)用) 
 if(dragHelper.continueSettling(true)){ 
  // 如果返回true, 動(dòng)畫還需要繼續(xù)執(zhí)行 
  ViewCompat.postInvalidateOnAnimation(this); 
 } 
} 

效果如下:

總結(jié)

以上所述是小編給大家介紹的Android ViewDragHelper使用介紹,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Kotlin Jetpack組件ViewModel使用詳解

    Kotlin Jetpack組件ViewModel使用詳解

    作為Jetpack組件之一的ViewModel,也是框架MVVM中的一部分,其功能主要用于屏幕反轉(zhuǎn)后的數(shù)據(jù)保存;因?yàn)锳ctivity翻轉(zhuǎn)屏幕后或?qū)崿F(xiàn)onCreat()方法,也就是說(shuō)會(huì)重新創(chuàng)建頁(yè)面,之前頁(yè)面的臨時(shí)數(shù)據(jù)都會(huì)清除
    2022-12-12
  • Android如何為按鍵添加聲音

    Android如何為按鍵添加聲音

    這篇文章主要告訴大家Android為按鍵添加聲音的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • Android使用Notification實(shí)現(xiàn)通知功能

    Android使用Notification實(shí)現(xiàn)通知功能

    這篇文章主要為大家詳細(xì)介紹了Android使用Notification實(shí)現(xiàn)通知功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-11-11
  • Android開發(fā)之TableLayout表格布局

    Android開發(fā)之TableLayout表格布局

    這篇文章主要為大家詳細(xì)介紹了Android開發(fā)之TableLayout表格布局,表格布局模型是以行列的形式管理子控件,對(duì)TableLayout表格布局感興趣的小伙伴們可以參考一下
    2016-03-03
  • 最新評(píng)論