RecyclerView + NestedScrollView 嵌套收縮動畫問題

作者:Otway
版權(quán):轉(zhuǎn)載請注明出處!

在復雜的業(yè)務場景中,會利用到 NestedScrollView 嵌套好幾個固定的布局來展示內(nèi)容。
在固定的布局中可能存在豎向的列表,并且要求列表完全展開馆截。針對列表中的 Item 還需要賦予位置移動動畫,整個列表收縮動畫及展開動畫蜂莉。

本文針對該場景采取的是嵌套實現(xiàn)蜡娶。

<android.support.v4.widget.NestedScrollView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/nested_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:fillViewport="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <LinearLayout
                android:id="@+id/container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content">
                </android.support.v7.widget.RecyclerView>

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="300dp"
                android:background="@color/colorAccent">
            </LinearLayout>
        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

在上面的布局中,RecyclerView中是一個列表數(shù)據(jù)的展示映穗,其中包含 位置移動窖张,收縮,展開等操作蚁滋。(PS:上述層級較多宿接,只是為了測試層級對RecyclerView的影響,畢竟復雜場景不會只有一個RecyclerView)

val list = ArrayList<Int>()
list += 1..20
recycler_view.layoutManager = LinearLayoutManager(this)
adapter = Adapter<Int>(list)
recycler_view.adapter = adapter
recycler_view.itemAnimator = DefaultItemAnimator()
recycler_view.itemAnimator.addDuration = 1000
recycler_view.itemAnimator.removeDuration = 1000
adapter!!.expand()

這里我們將動畫的時長設置很長辕录,便于觀察睦霎。其中 Adapter 增加了收縮和展開的方法。

    private var maxCount = 0
    
    override fun getItemCount(): Int {
        return minOf(dataList.size, maxCount)
    }
    
    fun expand() {
        val count = itemCount    //  同  getItemCount()
        maxCount = Int.MAX_VALUE
        notifyItemRangeInserted(count, itemCount - count)
    }

    fun collapse() {
        val count = itemCount
        maxCount = 1
        notifyItemRangeRemoved(1, count - itemCount)
    }

簡單的配置之后走诞,我們發(fā)現(xiàn)副女。在展開動畫開啟時,RecyclerView 會伸縮到合適的高度以容納 所有的 Item(這里設置了 android:fillViewport="true" 的屬性)蚣旱,然后我們才會看到默認的 add Item 的動畫碑幅。但是,當我們收縮列表的時候姻锁,并沒有觀察到動畫枕赵,給人的感覺是 直接 調(diào)用了 notifyDataSetChanged() 的方法猜欺。下面我們追蹤一下源碼來看看為什么會出現(xiàn)這種問題位隶。

首先是 notifyItemRangeRemoved() 方法

        public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
            mObservable.notifyItemRangeRemoved(positionStart, itemCount);
        }

根據(jù)該條代碼進行追蹤到最終實現(xiàn)的部分,在 RecyclerViewDataObserver 的實現(xiàn)中开皿,我們找到了具體的實現(xiàn)邏輯涧黄。

       @Override
        public void onItemRangeRemoved(int positionStart, int itemCount) {
            assertNotInLayoutOrScroll(null);
            if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
                triggerUpdateProcessor();
            }
        }

      void triggerUpdateProcessor() {
            if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
                ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
            } else {
                mAdapterUpdateDuringMeasure = true;
                requestLayout();
            }
        }

mHasFixedSize 字段的控制是關(guān)鍵所在,默認是false赋荆,所以直接進行了 requestLayout() 的操作笋妥,導致RecyclerView的高度直接變化到最小。

個人解決方案:

  • 如果列表的初始狀態(tài)為完全展開狀態(tài)窄潭〈盒可以通過測量第一個Item高度,以及總高度
        recycler_view.post {
            val viewFirst = recycler_view.layoutManager.findViewByPosition(0)
            firstItemHeight = viewFirst!!.height
            totalHeight = recycler_view.height
        }
        //調(diào)用 adapter.collapse()或者 expand()的時候 調(diào)用 下面的方法。
       height(recycler_view, totalHeight.toFloat(), firstItemHeight.toFloat(), 1000, null)

只需要在收縮時 調(diào)用 height 的動畫即可月帝,展開也可以調(diào)用躏惋。

fun height(view: View, from: Float, to: Float, duration: Int, animatorListener: Animator.AnimatorListener?): ValueAnimator {
        val animator = ValueAnimator.ofFloat(from, to)
        animator.duration = duration.toLong()
        if (animatorListener != null) {
            animator.addListener(animatorListener)
        }
        animator.addUpdateListener { animation ->
            if (view.layoutParams != null) {
                val lp = view.layoutParams
                val aFloat = animation.animatedValue as Float
                lp.height = aFloat.toInt()
                view.layoutParams = lp
            }
        }
        animator.start()
        return animator
    }
  • 通過設置 mHasFixedSize 屬性 來達到目的
              if (item.itemId == R.id.collapse) {
                    recycler_view.setHasFixedSize(true)
                    it.collapse()
                    recycler_view.postDelayed({
                        recycler_view.setHasFixedSize(false)
                        recycler_view.requestLayout()
                    }, recycler_view.itemAnimator.addDuration)
                } else {
                    recycler_view.setHasFixedSize(false)
                    it.expand()
                }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市嚷辅,隨后出現(xiàn)的幾起案子簿姨,更是在濱河造成了極大的恐慌,老刑警劉巖簸搞,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扁位,死亡現(xiàn)場離奇詭異,居然都是意外死亡趁俊,警方通過查閱死者的電腦和手機域仇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來则酝,“玉大人殉簸,你說我怎么就攤上這事」炼铮” “怎么了般卑?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長爽雄。 經(jīng)常有香客問我蝠检,道長,這世上最難降的妖魔是什么挚瘟? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任叹谁,我火速辦了婚禮,結(jié)果婚禮上乘盖,老公的妹妹穿的比我還像新娘焰檩。我一直安慰自己,他們只是感情好订框,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布析苫。 她就那樣靜靜地躺著,像睡著了一般穿扳。 火紅的嫁衣襯著肌膚如雪衩侥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天矛物,我揣著相機與錄音茫死,去河邊找鬼。 笑死履羞,一個胖子當著我的面吹牛峦萎,可吹牛的內(nèi)容都是我干的屡久。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼爱榔,長吁一口氣:“原來是場噩夢啊……” “哼涂身!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起搓蚪,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蛤售,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后妒潭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悴能,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年雳灾,在試婚紗的時候發(fā)現(xiàn)自己被綠了漠酿。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡谎亩,死狀恐怖炒嘲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匈庭,我是刑警寧澤夫凸,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站阱持,受9級特大地震影響夭拌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衷咽,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一鸽扁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镶骗,春花似錦桶现、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至此蜈,卻和暖如春即横,著一層夾襖步出監(jiān)牢的瞬間噪生,已是汗流浹背裆赵。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留跺嗽,地道東北人战授。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓页藻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親植兰。 傳聞我的和親對象是個殘疾皇子份帐,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

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