Android中神奇的ViewDragHelper

XSize的主頁(yè)

參考文獻(xiàn):
https://blog.csdn.net/briblue/article/details/73730386
http://www.reibang.com/p/111a7bc76a0e

ViewDragHelper是針對(duì) ViewGroup 中的拖拽和重新定位 views 操作時(shí)提供了一系列非常有用的方法和狀態(tài)追蹤。基本上使用在自定義ViewGroup處理拖拽中煤杀!

所謂無(wú)圖無(wú)真相欺嗤。交掏。沈条。


終極目標(biāo)回彈效果

本文知識(shí)點(diǎn):

  • 常用API的說(shuō)明
  • ViewDragHelper的使用案例
  • 常見(jiàn)的使用案例

1.常用API說(shuō)明

這里為什么先介紹API呢酣难?因?yàn)樯蟻?lái)我貼出來(lái)一堆代碼你都不知道每個(gè)方法什么意思让虐,會(huì)很痛苦的,而且還要跳著看罢荡。所以這里決定先介紹一下API大家先有個(gè)印象赡突,后面說(shuō)到的時(shí)候也不會(huì)那么陌生,好了廢話不多說(shuō)了区赵!

ViewDragHelper的API

  • ViewDragHelper create(ViewGroup forParent, Callback cb)惭缰;一個(gè)靜態(tài)的創(chuàng)建方法,
    • 參數(shù)1:出入的是相應(yīng)的ViewGroup
    • 參數(shù)2:是一個(gè)回掉(其實(shí)這個(gè)回掉你可以自己在外面實(shí)現(xiàn)笼才,后面在細(xì)說(shuō))
  • shouldInterceptTouchEvent(MotionEvent ev) 處理事件分發(fā)的(怎么說(shuō)這個(gè)方法呢漱受?主要是將ViewGroup的事件分發(fā),委托給ViewDragHelper進(jìn)行處理)
    • 參數(shù)1:MotionEvent ev 主要是ViewGroup的事件
  • processTouchEvent(MotionEvent event) 處理相應(yīng)TouchEvent的方法骡送,這里要注意一個(gè)問(wèn)題昂羡,處理相應(yīng)的TouchEvent的時(shí)候要將結(jié)果返回為true,消費(fèi)本次事件摔踱!否則將無(wú)法使用ViewDragHelper處理相應(yīng)的拖拽事件虐先!

基本上使用到的就這三個(gè)API就能實(shí)現(xiàn)相應(yīng)的拖拽效果了!(有的API我沒(méi)有查看派敷,好像還有很多API但是有的真的不怎么理解蛹批!原諒我的放蕩不羈。篮愉。腐芍。)

ViewDragHelper.Callback的API(也就是創(chuàng)建ViewDragHelper傳入的回調(diào)方法)

其實(shí)這個(gè)回掉主要是用來(lái)監(jiān)聽(tīng)一些內(nèi)容的,其實(shí)你可以這樣试躏,自己實(shí)現(xiàn)一個(gè)類猪勇,繼承這個(gè)類,然后在里面寫相應(yīng)的邏輯冗酿,這樣代碼能比較整潔埠对!也便于其他人的觀看

  • tryCaptureView(View child, int pointerId) 這是一個(gè)抽象類,必須去實(shí)現(xiàn)裁替,也只有在這個(gè)方法返回true的時(shí)候下面的方法才會(huì)生效项玛;

    • 參數(shù)1:捕獲的View(也就是你拖動(dòng)的這個(gè)View)
    • 參數(shù)2:這個(gè)參數(shù)我也不知道什么意思API中寫的一個(gè)什么指針,這里沒(méi)有到也沒(méi)有注意
  • onViewDragStateChanged(int state) 當(dāng)狀態(tài)改變的時(shí)候回調(diào)弱判,返回相應(yīng)的狀態(tài)(這里有三種狀態(tài))

    • STATE_IDLE 閑置狀態(tài)
    • STATE_DRAGGING 正在拖動(dòng)
    • STATE_SETTLING 放置到某個(gè)位置
  • onViewPositionChanged(View changedView, int left, int top, int dx, int dy) 當(dāng)你拖動(dòng)的View位置發(fā)生改變的時(shí)候回調(diào)

    • 參數(shù)1:你當(dāng)前拖動(dòng)的這個(gè)View
    • 參數(shù)2:距離左邊的距離
    • 參數(shù)3:距離右邊的距離
    • 參數(shù)4:x軸的變化量
    • 參數(shù)5:y軸的變化量
  • onViewCaptured(View capturedChild, int activePointerId)捕獲View的時(shí)候調(diào)用的方法

    • 參數(shù)1:捕獲的View(也就是你拖動(dòng)的這個(gè)View)
    • 參數(shù)2:這個(gè)參數(shù)我也不知道什么意思API中寫的一個(gè)什么指針襟沮,這里沒(méi)有到也沒(méi)有注意
  • onViewReleased(View releasedChild, float xvel, float yvel) 當(dāng)View停止拖拽的時(shí)候調(diào)用的方法,一般在這個(gè)方法中重置一些參數(shù),比如回彈什么的开伏。膀跌。。

    • 參數(shù)1:你拖拽的這個(gè)View
    • 參數(shù)2:x軸的速率
    • 參數(shù)3:y軸的速率
  • clampViewPositionVertical(View child, int top, int dy) 豎直拖拽的時(shí)候回調(diào)的方法

    • 參數(shù)1:拖拽的View
    • 參數(shù)2:距離頂部的距離
    • 參數(shù)3:變化量
  • clampViewPositionHorizontal(View child, int left, int dx) 水平拖拽的時(shí)候回調(diào)的方法

    • 參數(shù)1:拖拽的View
    • 參數(shù)2:距離左邊的距離
    • 參數(shù)3:變化量

基本上常用的API就這么多固灵,也都解釋了相應(yīng)參數(shù)代表的意思捅伤,可能有些不是那么準(zhǔn)確,還請(qǐng)指正巫玻。丛忆。。

2. ViewDragHelper的使用案例

2.1案例一:

自定義一個(gè)LinearLayout使其內(nèi)部的View可以進(jìn)行相應(yīng)的拖動(dòng)

2.1.1 創(chuàng)建相應(yīng)的回調(diào)CallBack(其實(shí)就是創(chuàng)建一個(gè)相應(yīng)的ViewDragHelper.Callback對(duì)象)這個(gè)主要是用于把代碼拆分到外面去仍秤。熄诡。。代碼如下

public class MyDragHelper extends ViewDragHelper.Callback {
    @Override
    public boolean tryCaptureView(View child, int pointerId) {
        //注意這里一定要返回true诗力,否則后續(xù)的拖拽回調(diào)是不會(huì)生效的
        return true;
    }

    @Override
    public int clampViewPositionHorizontal(View child, int left, int dx) {
        return left;
    }

    @Override
    public int clampViewPositionVertical(View child, int top, int dy) {
        return top;
    }
}

這里主要處理了內(nèi)部控件的拖拽問(wèn)題凰浮;

2.1.2自定義一個(gè)LinearLayout,授權(quán)相應(yīng)的拖拽苇本。代碼如下:

public class MyDragLinearLayout extends LinearLayout {


    private ViewDragHelper mViewDragHelper;

    public MyDragLinearLayout(Context context) {
        this(context, null);
    }

    public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        MyDragHelper dragHelper = new MyDragHelper();
        mViewDragHelper = ViewDragHelper.create(this, dragHelper);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }
}

上面這段代碼就是在View初始話的時(shí)候袜茧,創(chuàng)建了一個(gè)回調(diào)和一個(gè)ViewDragHelper對(duì)象,在onInterceptTouchEvent和onTouchEvent處理一下相應(yīng)的邏輯(這里有一點(diǎn)千萬(wàn)要注意就是在onTouchEvent的時(shí)候一定要返回true圈澈,切記1怪堋!康栈!

然后直接在布局文件中使用這個(gè)自定義的ViewGroup就可以了递递。

3.常見(jiàn)的使用案例

1.回彈效果

回彈效果是最常見(jiàn)的了,也是最主要的內(nèi)容就是回彈的效果I睹础5俏琛!其實(shí)使用方法和上面是類似的悬荣!

1.1委托相應(yīng)的服務(wù)是一樣的菠秒,這里就直接貼上相應(yīng)的代碼了。

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

1.2獲取捕獲的View氯迂,并獲取捕獲時(shí)候的位置信息

   @Override
   public void onViewCaptured(View capturedChild, int activePointerId) {
         super.onViewCaptured(capturedChild, activePointerId);
         mLeft = capturedChild.getLeft();
         mTop = capturedChild.getTop();
   }

這里主要是通過(guò)相應(yīng)的捕獲的回調(diào)獲取相應(yīng)的位置信息践叠,主要是作用是為了之后回彈到原始位置提供相應(yīng)的位置信息;

1.3在釋放的時(shí)候重置這個(gè)位置信息

    @Override
    public void onViewReleased(View releasedChild, float xvel, float yvel) {
        super.onViewReleased(releasedChild, xvel, yvel);
        mViewDragHelper.settleCapturedViewAt(mLeft, mTop);
        invalidate();
    }

這里主要是通過(guò)ViewDragHelper的settleCapturedViewAt(int finalLeft, int finalTop)方法使你拖拽的View回到上面獲取的原始位置了嚼蚀。禁灼。。但是其實(shí)這樣是不行的轿曙,為什么呢弄捕?僻孝??上面這個(gè)確實(shí)是設(shè)置拖拽View位置的守谓,但是查看API的時(shí)候有這樣一段描述信息

If this method returns true, the caller should invoke {@link #continueSettling(boolean)}
on each subsequent frame to continue the motion until it returns false. If this method
returns false there is no further work to do to complete the movement.

簡(jiǎn)單的翻譯一下:
如果這個(gè)方法返回true,調(diào)用者應(yīng)該調(diào)用{ @link #continueSettling(boolean)}在每個(gè)后續(xù)幀繼續(xù)運(yùn)動(dòng),直到返回false穿铆。如果這個(gè)方法返回false,沒(méi)有進(jìn)一步的工作來(lái)完成運(yùn)動(dòng)。

也就是說(shuō)這里你要通過(guò)這個(gè)方法進(jìn)行相應(yīng)的處理斋荞,但是怎么處理呢荞雏?請(qǐng)看下面這段代碼:

    @Override
    public void computeScroll() {
        if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
            invalidate();
        }
    }

這個(gè)是重寫的ViewGroup的方法,主要是用于ViewGroup中更新相應(yīng)View的(通過(guò)ScrollX
和ScrollY)通過(guò)上面的代碼就能實(shí)現(xiàn)回彈了平酿。整體代碼如下:

public class MyDragLinearLayout extends LinearLayout {


    private ViewDragHelper mViewDragHelper;

    public MyDragLinearLayout(Context context) {
        this(context, null);
    }

    public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyDragLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        mViewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {

            private int mLeft;
            private int mTop;

            @Override
            public boolean tryCaptureView(View child, int pointerId) {
                return true;
            }

            @Override
            public void onViewCaptured(View capturedChild, int activePointerId) {
                super.onViewCaptured(capturedChild, activePointerId);
                mLeft = capturedChild.getLeft();
                mTop = capturedChild.getTop();
            }

            @Override
            public int clampViewPositionVertical(View child, int top, int dy) {
                return top;
            }

            @Override
            public int clampViewPositionHorizontal(View child, int left, int dx) {
                return left;

            }

            @Override
            public void onViewReleased(View releasedChild, float xvel, float yvel) {
                super.onViewReleased(releasedChild, xvel, yvel);
                mViewDragHelper.settleCapturedViewAt(mLeft, mTop);
                invalidate();
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        return true;
    }


    @Override
    public void computeScroll() {
        if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
            invalidate();
        }
    }
}

其實(shí)對(duì)這個(gè)API也只是表層的理解讯檐,如有什么不對(duì)的地方請(qǐng)指出。染服。。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末叨恨,一起剝皮案震驚了整個(gè)濱河市柳刮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痒钝,老刑警劉巖秉颗,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異送矩,居然都是意外死亡蚕甥,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門栋荸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)菇怀,“玉大人,你說(shuō)我怎么就攤上這事晌块“担” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵匆背,是天一觀的道長(zhǎng)呼伸。 經(jīng)常有香客問(wèn)我,道長(zhǎng)钝尸,這世上最難降的妖魔是什么括享? 我笑而不...
    開(kāi)封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮珍促,結(jié)果婚禮上铃辖,老公的妹妹穿的比我還像新娘。我一直安慰自己踢星,他們只是感情好澳叉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布隙咸。 她就那樣靜靜地躺著,像睡著了一般成洗。 火紅的嫁衣襯著肌膚如雪五督。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天瓶殃,我揣著相機(jī)與錄音充包,去河邊找鬼。 笑死遥椿,一個(gè)胖子當(dāng)著我的面吹牛基矮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播冠场,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼家浇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了碴裙?” 一聲冷哼從身側(cè)響起钢悲,我...
    開(kāi)封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舔株,沒(méi)想到半個(gè)月后莺琳,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡载慈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年惭等,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片办铡。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡辞做,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寡具,到底是詐尸還是另有隱情凭豪,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布晒杈,位于F島的核電站嫂伞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拯钻。R本人自食惡果不足惜帖努,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望粪般。 院中可真熱鬧拼余,春花似錦、人聲如沸亩歹。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至亭姥,卻和暖如春稼钩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背达罗。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工坝撑, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粮揉。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓巡李,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親扶认。 傳聞我的和親對(duì)象是個(gè)殘疾皇子侨拦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評(píng)論 25 707
  • 內(nèi)容是博主照著書敲出來(lái)的,博主碼字挺辛苦的辐宾,轉(zhuǎn)載請(qǐng)注明出處阳谍,后序內(nèi)容陸續(xù)會(huì)碼出。 當(dāng)了解了Android坐標(biāo)系和觸...
    Blankj閱讀 6,641評(píng)論 3 61
  • 過(guò)程不重要螃概,結(jié)局最終零點(diǎn)零…………………………………………
    與遇閱讀 123評(píng)論 0 0
  • 這本超級(jí)長(zhǎng)的小說(shuō)看到一半,所以只能說(shuō)是半解鸽疾。從作者:灰熊貓的筆名來(lái)看吊洼,應(yīng)該是定期更新的網(wǎng)絡(luò)小說(shuō)。這一類新興的作品倒...
    geoeee閱讀 279評(píng)論 0 2