ViewDragHelper應(yīng)用基礎(chǔ)篇

下面記錄下自己對(duì)ViewDragHelper的理解.

一锯厢、什么是ViewDragHelper

首先,ViewDragHelper實(shí)在官方supportV4包里面叭爱,為了方便開(kāi)發(fā)者自定義自己的控件而提出來(lái)的勤婚,其中官方的DrawLayout的基礎(chǔ)實(shí)現(xiàn)類是通過(guò)ViewDragHelper實(shí)現(xiàn)的。我相信讀者應(yīng)該都對(duì)DrawLayout比較熟悉涤伐,其實(shí)自定義一個(gè)類似DrawLayout的功能馒胆,并不是一件難事!好了凝果,現(xiàn)在直接進(jìn)入正文祝迂。

二、入門小Demo

首先簡(jiǎn)單說(shuō)明下使用ViewDragHelper的步驟(這里筆者使用典型的View拖動(dòng)小Demo器净,盡可能加入注釋描述ViewDragHelper應(yīng)用)型雳。

1.創(chuàng)建一個(gè)ViewGroup
2.實(shí)例化ViewDragHelper對(duì)象
3.重新部分ViewDragHelper對(duì)象回調(diào)接口方法
(一)利用ViewDragHelper,實(shí)現(xiàn)子View可以拖動(dòng)效果

首先我們直接看代碼:

/**
* 簡(jiǎn)單的ViewDrawHleper應(yīng)用 案例
* Created by wsy on 2016/2/29.
*/
public class BasicMoveView extends LinearLayout {
   private final String TAG = "Wusy BasicMoveView";
   private ViewDragHelper dragHelper;
   private Context mContext;
   private View mDragView1;
   private View mDragView2;
   private View mDragView3;
   public BasicMoveView(Context context) {
       super(context);
       this.mContext = context;
       init();
   }
   public BasicMoveView(Context context, AttributeSet attrs) {
       super(context, attrs);
       this.mContext = context;
       init();
   }
   private void init() {
       dragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
           @Override
           public boolean tryCaptureView(View view, int i) {
               return mDragView1 == view || mDragView2 == view || mDragView3 == view;
           }
           @Override
           public int clampViewPositionHorizontal(View child, int left, int dx) {
               int lefPad = getPaddingLeft();
               int realWidth = getWidth() - child.getWidth();
               int newLeft = Math.min(Math.max(left, lefPad), realWidth);
               return newLeft;
           }
           @Override
           public int clampViewPositionVertical(View child, int top, int dy) {
               int topPad = getPaddingTop();
               int realHeight = getHeight() - child.getHeight();
               int newTop = Math.min(Math.max(top, topPad), realHeight);
               return newTop;
           }      
       });
   }
   /**
    * 事件從父-》子控件 事件傳遞
    **/
   @Override
   public boolean onInterceptTouchEvent(MotionEvent ev) {
       final int action = MotionEventCompat.getActionMasked(ev);
       if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
           dragHelper.cancel();
           return false;
       }
       return dragHelper.shouldInterceptTouchEvent(ev);
   }
   /**
    * 事件從父《-子控件 事件傳遞
    **/
   @Override
   public boolean onTouchEvent(MotionEvent event) {
       dragHelper.processTouchEvent(event);
       return true;
   }
   @Override
   protected void onFinishInflate() {
       super.onFinishInflate();
       mDragView1 = getChildAt(0);
       mDragView2 = getChildAt(1);
       mDragView3 = getChildAt(2);
   }
}

說(shuō)明:
1.首先創(chuàng)建ViewDragHelper實(shí)例山害,是通過(guò)靜態(tài)工程提供實(shí)例化方法:

dragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {... });

關(guān)于這里三個(gè)形參的解釋:1.自身拖動(dòng)對(duì)象(必須為ViewGroup) 2.敏感度(越大纠俭,觸發(fā)越敏感) 3.操作回調(diào)。

2.觸摸事件傳遞給ViewDragHelper必須重寫(xiě)的兩個(gè)方法

  /**
    * 事件從父-》子控件 事件傳遞 決定當(dāng)前事件是否要被截?cái)?    **/
   @Override
   public boolean onInterceptTouchEvent(MotionEvent ev) {
       return dragHelper.shouldInterceptTouchEvent(ev);
   }
   /**
    * 事件從父《-子控件 事件傳遞 決定當(dāng)前事件的處理
    **/
   @Override
   public boolean onTouchEvent(MotionEvent event) {
       dragHelper.processTouchEvent(event);
       return true;
   }

3.一切觸摸移動(dòng)事件取決于ViewDragHelper.CallCack回調(diào)重寫(xiě)浪慌。
這里我們看下如果實(shí)現(xiàn)子View需要重寫(xiě)的三個(gè)方法

@Override
public boolean tryCaptureView(View childView, int i) {
              return mDragView1 == childView || mDragView2 == childView|| mDragView3 == childView;
          }

/** 水平控制子View位置 left為x坐標(biāo) childView橫向的移動(dòng)的范圍**/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
              int lefPad = getPaddingLeft();
              int realWidth = getWidth() - child.getWidth();
              int newLeft = Math.min(Math.max(left, lefPad), realWidth);
              return newLeft;
          }

/** 垂直控制子View位置 top y坐標(biāo) childView縱向的移動(dòng)的范圍**/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
              int topPad = getPaddingTop();
              int realHeight = getHeight() - child.getHeight();
              int newTop = Math.min(Math.max(top, topPad), realHeight);
              return newTop;
          }

(1)tryCaptureView()方法主要決定受控制子View冤荆,當(dāng)判斷滿足條件的childView,即返回true后即可回調(diào)下面出發(fā)事件
(3)clampViewPositionHorizontal() 控制子View移動(dòng)位置权纤,返回值為橫向移動(dòng)范圍
(3)clampViewPositionVertical()控制子View移動(dòng)位置钓简,返回值為縱向移動(dòng)范圍
4.具體應(yīng)用
一下是效果與布局代碼

<cn.wsy.viewdraghelper.Views.BasicMoveView 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"
   android:orientation="vertical"
   tools:context=".MainActivity">
   <TextView
       android:id="@+id/text1"
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#000000" />
   <TextView
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#ccc" />
   <TextView
       android:layout_width="100dp"
       android:layout_height="100dp"
       android:background="#44ff0000" />
</cn.wsy.viewdraghelper.Views.BasicMoveView>
(二)通過(guò)ViewDragHelper實(shí)現(xiàn)邊緣控制與運(yùn)動(dòng)回放
/** 邊緣控制 **/
           @Override
           public void onEdgeTouched(int edgeFlags, int pointerId) {
               super.onEdgeTouched(edgeFlags, pointerId);
               Toast.makeText(mContext, "在最左邊", Toast.LENGTH_SHORT).show();
           }
           /** 邊緣動(dòng)態(tài)控制view **/
           @Override
           public void onEdgeDragStarted(int edgeFlags, int pointerId) {
               super.onEdgeDragStarted(edgeFlags, pointerId);
               dragHelper.captureChildView(mDragView1, pointerId);
           }
           /** 拖動(dòng)回放 **/
           @Override
           public void onViewReleased(View releasedChild, float xvel, float yvel) {
               super.onViewReleased(releasedChild, xvel, yvel);
               if (releasedChild == mDragView3) {
                   Log.i(TAG,"指定View被釋放了,位置在:x-> "+mDragView3.getX()+" y-> "+mDragView3.getY());
                   dragHelper.settleCapturedViewAt(backViewPoint.x, backViewPoint.y);//源碼內(nèi)部mScroller.startScroll,配合computeScroll才能實(shí)現(xiàn)invalidate()
                   invalidate();
               }
           }

onEdgeTouched() 前提指定邊緣方向監(jiān)聽(tīng)后汹想,如果觸摸坐標(biāo)達(dá)到邊緣即出發(fā)事件

onEdgeDragStarted() 前提指定邊緣方向監(jiān)聽(tīng)后外邓,觸摸坐標(biāo)達(dá)到邊緣后,當(dāng)觸摸坐標(biāo)移動(dòng)的時(shí)候會(huì)回調(diào)這個(gè)方法古掏。dragHelper.captureChildView(mDragView1, pointerId) 這里是當(dāng)坐標(biāo)在屏幕邊緣的時(shí)候损话,坐標(biāo)移動(dòng)回調(diào)返回的pointerId,直接將制定捕獲的View槽唾,進(jìn)行位移操作丧枪。

onViewReleased() 當(dāng)其中一個(gè)捕獲到的子View光涂,釋放的時(shí)候回調(diào)這個(gè)方法。這里View移動(dòng)后回滾到原來(lái)位置的原理是豪诲,初始化的Onlayout的時(shí)候,先臨時(shí)存儲(chǔ)了原來(lái)的x挂绰,y坐標(biāo)屎篱,當(dāng)捕獲的View釋放的時(shí)候,重新讓它繪制到這個(gè)位置葵蒂。注意:這里需要配合computeScroll方法(我們看settleCapturedViewAt源碼得知通過(guò)mScroller.startScroll滾動(dòng)改變位移)交播。
//設(shè)置使能邊緣

 dragHelper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_LEFT);

指定邊緣方向監(jiān)聽(tīng)。

 @Override
 public void computeScroll() {
 if(dragHelper.continueSettling(true)){
 invalidate();
 } }
(三)注意

如果其中子View有監(jiān)聽(tīng)事件践付,必須重寫(xiě)以下兩個(gè)方法:

 /**添加了clickable = true 秦士,都記得重寫(xiě)下面這兩個(gè)方法**/
 @Override
 public int getViewHorizontalDragRange(View child) {
 return getMeasuredWidth()-child.getMeasuredWidth();
 }
 @Override
 public int getViewVerticalDragRange(View child) {
 return getMeasuredHeight()-child.getMeasuredHeight();
 }

三、簡(jiǎn)單描述未使用接口作用

到此永高,我們列一下所有的Callback方法隧土,看看還有哪些沒(méi)用過(guò)的:

onViewDragStateChanged
當(dāng)ViewDragHelper狀態(tài)發(fā)生變化時(shí)回調(diào)(IDLE,DRAGGING,SETTING[自動(dòng)滾動(dòng)時(shí)])

onViewPositionChanged
當(dāng)captureview的位置發(fā)生改變時(shí)回調(diào)

onViewCaptured
當(dāng)captureview被捕獲時(shí)回調(diào)

onViewReleased 已用

onEdgeTouched
當(dāng)觸摸到邊界時(shí)回調(diào)。

onEdgeLock
true的時(shí)候會(huì)鎖住當(dāng)前的邊界命爬,false則unLock曹傀。

onEdgeDragStarted 已用

getOrderedChildIndex
改變同一個(gè)坐標(biāo)(x,y)去尋找captureView位置的方法。(具體在:findTopChildUnder方法中)

getViewHorizontalDragRange 已用

getViewVerticalDragRange 已用
tryCaptureView 已用
clampViewPositionHorizontal 已用
clampViewPositionVertical 已用

ok饲宛,至此所有的回調(diào)方法都有了一定的認(rèn)識(shí)皆愉。
總結(jié)下,方法的大致的回調(diào)順序:

shouldInterceptTouchEvent:

DOWN:
    getOrderedChildIndex(findTopChildUnder)
    ->onEdgeTouched

MOVE:
    getOrderedChildIndex(findTopChildUnder)
    ->getViewHorizontalDragRange & 
      getViewVerticalDragRange(checkTouchSlop)(MOVE中可能不止一次)
    ->clampViewPositionHorizontal&
      clampViewPositionVertical
    ->onEdgeDragStarted
    ->tryCaptureView
    ->onViewCaptured
    ->onViewDragStateChanged

processTouchEvent:

DOWN:
    getOrderedChildIndex(findTopChildUnder)
    ->tryCaptureView
    ->onViewCaptured
    ->onViewDragStateChanged
    ->onEdgeTouched
MOVE:
    ->STATE==DRAGGING:dragTo
    ->STATE!=DRAGGING:
        onEdgeDragStarted
        ->getOrderedChildIndex(findTopChildUnder)
        ->getViewHorizontalDragRange&
          getViewVerticalDragRange(checkTouchSlop)
        ->tryCaptureView
        ->onViewCaptured
        ->onViewDragStateChanged

ok艇抠,上述是正常情況下大致的流程幕庐,當(dāng)然整個(gè)過(guò)程可能會(huì)存在很多判斷不成立的情況。

四家淤、總結(jié)

http://blog.csdn.net/pi9nc/article/details/39583377
http://blog.csdn.net/lmj623565791/article/details/46858663

傻小孩b mark

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末异剥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子絮重,更是在濱河造成了極大的恐慌届吁,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件绿鸣,死亡現(xiàn)場(chǎng)離奇詭異疚沐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)潮模,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門亮蛔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人擎厢,你說(shuō)我怎么就攤上這事究流±背裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,998評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵芬探,是天一觀的道長(zhǎng)神得。 經(jīng)常有香客問(wèn)我,道長(zhǎng)偷仿,這世上最難降的妖魔是什么哩簿? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,323評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮酝静,結(jié)果婚禮上节榜,老公的妹妹穿的比我還像新娘。我一直安慰自己别智,他們只是感情好宗苍,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著薄榛,像睡著了一般讳窟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敞恋,一...
    開(kāi)封第一講書(shū)人閱讀 49,079評(píng)論 1 285
  • 那天挪钓,我揣著相機(jī)與錄音,去河邊找鬼耳舅。 笑死碌上,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的浦徊。 我是一名探鬼主播馏予,決...
    沈念sama閱讀 38,389評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盔性!你這毒婦竟也來(lái)了霞丧?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,019評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冕香,失蹤者是張志新(化名)和其女友劉穎蛹尝,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體悉尾,經(jīng)...
    沈念sama閱讀 43,519評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡突那,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了构眯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愕难。...
    茶點(diǎn)故事閱讀 38,100評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出猫缭,到底是詐尸還是另有隱情葱弟,我是刑警寧澤,帶...
    沈念sama閱讀 33,738評(píng)論 4 324
  • 正文 年R本政府宣布猜丹,位于F島的核電站芝加,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏射窒。R本人自食惡果不足惜藏杖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轮洋。 院中可真熱鬧制市,春花似錦抬旺、人聲如沸弊予。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,289評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)汉柒。三九已至,卻和暖如春责鳍,著一層夾襖步出監(jiān)牢的瞬間碾褂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,517評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工历葛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留正塌,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評(píng)論 2 354
  • 正文 我出身青樓恤溶,卻偏偏與公主長(zhǎng)得像乓诽,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咒程,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • ViewDragHelper實(shí)例的創(chuàng)建 ViewDragHelper重載了兩個(gè)create()靜態(tài)方法public...
    傀儡世界閱讀 654評(píng)論 0 3
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,516評(píng)論 25 707
  • 內(nèi)容是博主照著書(shū)敲出來(lái)的鸠天,博主碼字挺辛苦的,轉(zhuǎn)載請(qǐng)注明出處帐姻,后序內(nèi)容陸續(xù)會(huì)碼出稠集。 當(dāng)了解了Android坐標(biāo)系和觸...
    Blankj閱讀 6,626評(píng)論 3 61
  • 在日清學(xué)堂的自我挑戰(zhàn)訓(xùn)練營(yíng)中我已經(jīng)連續(xù)兩天發(fā)紅包了,因?yàn)槎际峭砩贤洶l(fā)回顧饥瓷,自己組織的活動(dòng)剥纷,自己都忘記最重要的一個(gè)...
    digman閱讀 279評(píng)論 0 0
  • 上周六參加了河南審協(xié)的二面,這周五出結(jié)果呢铆,希望能夠進(jìn)去筷畦。想回家,感覺(jué)好難,自己也太水了鳖宾。吼砂。。希望這次能夠塵埃落定吧...
    謝滿月閱讀 309評(píng)論 0 1