快速定制簡單的Item Animation

Item Animation的基本原理

我們都知道环凿,給RecyclerView添加Item Animation的方法是setItemAnimator(ItemAnimator animator)骂蓖。因此我們可以以抽象類ItemAnimator為切入點浑测,看看Recyclerview的Item Animation是怎樣實現(xiàn)的精肃。

首先列舉一下ItemAnimator中幾個比較重要的方法簽名:

    • public ItemHolderInfo recordPreLayoutInformation(State state,ViewHolder viewHolder,int changeFlags,List<Object> payloads)
    • public ItemHolderInfo recordPostLayoutInformation(State state, ViewHolder viewHolder)

    這兩個方法是成對出現(xiàn)的,分別用于記錄同一個ViewHolder在layout執(zhí)行前后view位置等信息,分別對應(yīng)了動畫開始前的view狀態(tài)和動畫結(jié)束后的view狀態(tài)。

    • public abstract boolean animateDisappearance(ViewHolder viewHolder, ItemHolderInfo preLayoutInfo, ItemHolderInfo postLayoutInfo)
    • public abstract boolean animateAppearance(ViewHolder viewHolder, ItemHolderInfo preLayoutInfo, ItemHolderInfo postLayoutInfo)
    • public abstract boolean animatePersistence(ViewHolder viewHolder, ItemHolderInfo preLayoutInfo, ItemHolderInfo postLayoutInfo)
    • public abstract boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, ItemHolderInfo preLayoutInfo, ItemHolderInfo postLayoutInfo)

    這四個方法都是由RecyclerView調(diào)用郎哭,調(diào)用時機分別為在執(zhí)行l(wèi)ayout過程之后viewHolderRecyclerView中移除(從有到無)、出現(xiàn)(從無到有)菇存、不變(同一個viewHolder但是尺寸夸研、位置可能發(fā)生變化)、改變(從oldHolder變?yōu)?code>newHolder撰筷,viewHolder發(fā)生了變化)陈惰。

這四個方法中都包含preLayoutInfopostLayoutInfo畦徘,這兩個參數(shù)就是由前面recordPreLayoutInformationrecordPostLayoutInformation方法計算出來的viewHolder信息毕籽。我們就可以根據(jù)viewHolder的初態(tài)(preLayoutInfo)和終態(tài)(postLayoutInfo)實現(xiàn)自定義的動畫。但是動畫并不是在這個方法中啟動井辆,在這四個方法中我們僅僅是根據(jù)preLayoutInfopostLayoutInfo判斷是否需要動畫和需要什么動畫关筒。如果不需要執(zhí)行動畫,就必須在這四個方法中調(diào)用dispatchAnimationFinished(ViewHolder)方法并且最后的return值必須為false杯缺;反之如果需要執(zhí)行動畫return值就必須為true蒸播,之后系統(tǒng)就會調(diào)用runPendingAnimations()方法,并在runPendingAnimations()中啟動各種動畫萍肆,在動畫執(zhí)行完畢后也必須要調(diào)用dispatchAnimationFinished(ViewHolder)方法袍榆。

  1. abstract public void runPendingAnimations()

如果上面四個方法中有一個的返回值是true,在下一幀的時候就會執(zhí)行此方法塘揣,所有的動畫都可以在此方法中啟動包雀。

    • abstract public void endAnimation(ViewHolder item)
    • abstract public void endAnimations()
      結(jié)束動畫,并確保各個viewHolder都處于最終態(tài)亲铡。

到此為止才写,我們可以縷一下整個過程。

首先RecyclerView在執(zhí)行l(wèi)ayout前后會調(diào)用recordPreLayoutInformationrecordPostLayoutInformation方法奖蔓,并保存layout前后viewHodler的位置等信息赞草;然后根據(jù)layout的情況執(zhí)行animateXXX方法,我們在animateXXX方法中決定是否執(zhí)行動畫和執(zhí)行何種動畫吆鹤;如果需要執(zhí)行動畫厨疙,則可以在runPendingAnimations方法中啟動動畫;最后在endAnimationendAnimations方法中確保viewHolder處于最終態(tài)疑务。

快速實現(xiàn)自定義Item Animation

問當(dāng)今如何code最快轰异,那必然是ctrl-C加上ctrl-V岖沛。(開個玩笑~~~)

不過現(xiàn)在講的是如何快速的自定義Item Animation,基于學(xué)習(xí)的目的搭独,那當(dāng)然還是需要copy一些代碼的嘛婴削。至于以后項目中需要或者興趣使然要深入自定義Item Animation就需要自己參照原有實現(xiàn)進行封裝了。

廢話不多說牙肝,下面進入正題0λ住!配椭!

android support v7包里已經(jīng)包含了一個Item Animation的默認實現(xiàn):android.support.v7.widget.DefaultItemAnimator虫溜,DefaultItemAnimator繼承了SimpleItemAnimator,而SimpleItemAnimator又繼承了RecyclerView.ItemAnimator股缸。DefaultItemAnimator實現(xiàn)了在添加衡楞、移除item時View的透明度發(fā)生變化,在移動item時view的位置發(fā)生變化敦姻。我們要做的僅僅是對DefaultItemAnimator進行局部的修改~~~

recordPreLayoutInformation和recordPostLayoutInformationItemAnimator中已經(jīng)實現(xiàn)瘾境,animateDisappearanceanimateAppearance镰惦、animatePersistenceanimateChangeSimpleItemAnimator中也已實現(xiàn)迷守,runPendingAnimationsDefaultItemAnimator中已經(jīng)實現(xiàn),我們用這些默認實現(xiàn)就可以了旺入。以添加Item動畫為例兑凿,我們只要修改如下3個地方就可以實現(xiàn)Item旋轉(zhuǎn)進入動畫(僅列出部分關(guān)鍵代碼,注釋為原代碼茵瘾,////中間為修改后的代碼):

  1. 設(shè)置動畫的初始值礼华,將view設(shè)置為旋轉(zhuǎn)180度

    @Override
    public boolean animateAdd(final ViewHolder holder) {
        resetAnimation(holder);
        /**ViewCompat.setAlpha(holder.itemView, 0);**/
        //
        ViewCompat.setRotation(holder.itemView, 180);
        //
        mPendingAdditions.add(holder);
        return true;
    }
    

2. 啟動動畫,讓view再轉(zhuǎn)180度拗秘。并在動畫取消時將view設(shè)置為終態(tài)圣絮,也就是旋轉(zhuǎn)0度

    ```
    private void animateAddImpl(final RecyclerView.ViewHolder holder) {
        ...
        /**animation.alpha(1)**/
        //
        animation.rotationBy(180)
        //
                .setDuration(getAddDuration()).
                setListener(new VpaListenerAdapter() {
                    ...
                    @Override
                    public void onAnimationCancel(View view) {
                        /**ViewCompat.setAlpha(view, 1);**/
                        //
                        ViewCompat.setRotation(view, 0);
                        //
                    }
                    ...
                }).start();
    }
  1. 結(jié)束動畫,確保view在動畫結(jié)束或者取消后能回到終態(tài)聘殖,也就是旋轉(zhuǎn)0度晨雳。(這里省略了endAnimations()方法的修改)

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        ...
        if (mPendingAdditions.remove(item)) {
            /**ViewCompat.setAlpha(view, 1);**/
            //
            ViewCompat.setRotation(view, 0);
            //
            dispatchAddFinished(item);
        }
        ...
        for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
            ArrayList<RecyclerView.ViewHolder> additions = mAdditionsList.get(i);
            if (additions.remove(item)) {
                /**ViewCompat.setAlpha(view, 1);**/
                //
                ViewCompat.setRotation(view, 0);
                //
                dispatchAddFinished(item);
                if (additions.isEmpty()) {
                    mAdditionsList.remove(i);
                }
            }
        }
        ...
    }
    

通過以上3步,我們就是實現(xiàn)了Item旋轉(zhuǎn)進入的動畫奸腺,以下是演示效果餐禁。

![Demo 演示效果](http://upload-images.jianshu.io/upload_images/289461-7b20ae37379c1199.gif?imageMogr2/auto-orient/strip)

>[Demo地址](https://github.com/lileibuaa/CustomItemAnimator)

**再次強調(diào),以上步驟僅適用于學(xué)習(xí)突照。在項目中使用還需自己參照實現(xiàn)自己想要的效果帮非。**

PS:順帶說下如何解決調(diào)用`notifyDataSetChanged`后,沒有觸發(fā)Item Animation的問題。末盔。

要想在調(diào)用`notifyDataSetChanged`之后觸發(fā)Item Animation筑舅,必須要在自定義adapter中調(diào)用方法`setHasStableIds(true)`;并實現(xiàn)`public long getItemId(int position)`方法返回各Item的id。陨舱。至于為啥翠拣,留待以后再探究吧。游盲。误墓。請原諒我這個拖延癥重度患者。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末益缎,一起剝皮案震驚了整個濱河市谜慌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌莺奔,老刑警劉巖欣范,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異令哟,居然都是意外死亡恼琼,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門励饵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驳癌,“玉大人滑燃,你說我怎么就攤上這事役听。” “怎么了表窘?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵典予,是天一觀的道長。 經(jīng)常有香客問我乐严,道長瘤袖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任昂验,我火速辦了婚禮捂敌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘既琴。我一直安慰自己占婉,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布甫恩。 她就那樣靜靜地躺著逆济,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奖慌,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天抛虫,我揣著相機與錄音,去河邊找鬼简僧。 笑死建椰,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的岛马。 我是一名探鬼主播广凸,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛛枚!你這毒婦竟也來了谅海?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蹦浦,失蹤者是張志新(化名)和其女友劉穎扭吁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盲镶,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡侥袜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了溉贿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枫吧。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宇色,靈堂內(nèi)的尸體忽然破棺而出九杂,到底是詐尸還是另有隱情,我是刑警寧澤宣蠕,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布例隆,位于F島的核電站,受9級特大地震影響抢蚀,放射性物質(zhì)發(fā)生泄漏镀层。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一皿曲、第九天 我趴在偏房一處隱蔽的房頂上張望唱逢。 院中可真熱鬧,春花似錦屋休、人聲如沸坞古。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绸贡。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間听怕,已是汗流浹背捧挺。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留尿瞭,地道東北人闽烙。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像声搁,于是被迫代替她去往敵國和親黑竞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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