RecyclerView進(jìn)階之層疊列表(下)

前言

昨天寫了RecyclerView進(jìn)階之層疊列表(上)先慷,不過只實現(xiàn)了基本的效果饮笛。今天看到很多人點贊,于是我趁熱打鐵论熙,把這個控件寫完成吧福青。沒看過前篇的同學(xué),先移步熟悉下吧脓诡。下篇的主要內(nèi)容就是實現(xiàn)層疊列表邊緣的層疊動畫和RecyclerView的回收復(fù)用无午,也是這個控件實現(xiàn)的難點所在。

層疊動畫

關(guān)于這個祝谚,先上圖吧:

terrible.gif

對比系統(tǒng)通知欄的滑動效果宪迟,細(xì)心的同學(xué)就發(fā)現(xiàn)了,無論是頂部還是底部交惯,ItemView靠近邊緣的時候并沒有變慢次泽,從而產(chǎn)生一個多層層疊的效果,所以我們先來實現(xiàn)這個效果商玫。
先定義兩個動畫參數(shù):

private @FloatRange(from = 0.01, to = 1.0)
float edgePercent = 0.5f;//觸發(fā)邊緣動畫距離百分比

private @IntRange(from = 1)
int slowTimes = 5;//到達(dá)此距離后放慢倍數(shù)

然后在滾動時重新布局列表各個ItemView位置的方法中處理動畫箕憾,關(guān)鍵點是在ItemView到達(dá)邊界臨界點時,將原本的偏移值除以一個倍數(shù)拳昌,使其移動速度變慢:

   private void addAndLayoutViewVertical(RecyclerView.Recycler recycler, RecyclerView.State state, int offset) {
       //反向遍歷Recycler中保存的View取出來
      for (int i = itemCount - 1; i >= 0; i--) {
        Rect mTmpRect = allItemRects.get(i);
        int bottomOffset = mTmpRect.bottom - offset;
        int topOffset = mTmpRect.top - offset;
        if (i != itemCount - 1) {//除最后一個外的底部慢速動畫
            if (displayHeight - bottomOffset <= height * edgePercent) {
                //到達(dá)邊界觸發(fā)距離
                int edgeDist = (int) (displayHeight - height * edgePercent);
                //到達(dá)邊界后速度放慢到原來5分之一袭异,計算出實際需要的底部位置
                int bottom = edgeDist + (bottomOffset - edgeDist) / slowTimes;
                //當(dāng)然這個位置不能超過底部
                bottom=Math.min(bottom,displayHeight);
                realBottomOffset = bottom;
            }
        } else {
             // 如果是最后一個就不需要動畫了,因為已經(jīng)在底部了
            realBottomOffset = totalHeight > displayHeight ? displayHeight : totalHeight;
        }
    }
   }

上面是底部邊界動畫的處理,頂部邊界動畫的處理也是一樣的【嫣伲現(xiàn)在我們已經(jīng)可以看到有層疊的效果了:

animator.gif

橫向的看起來效果也不錯:


hor.gif

回收復(fù)用功能

這個功能如果沒有實現(xiàn)的話御铃,就對不起RecyclerView的名號了碴里。下面都只貼了核心代碼:

  @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
    //在每次觸發(fā)滾動的時候,把所有View從RecyclerView移除掉上真,這樣recyclerView就沒有itemview了
        detachAndScrapAttachedViews(recycler);
   //從新布局位置咬腋、顯示View
        addAndLayoutViewVertical(recycler, state, verticalScrollOffset); 
        return tempDy;
    }

上面已經(jīng)吧所有的itemView移除了,現(xiàn)在遍歷所有的itemView睡互,判斷是否在屏幕中顯示根竿,是的話就重新添加進(jìn)去,不是的話就跳過就珠。因為有邊緣層疊動畫寇壳,我們放慢了速度,但是比較的仍是原來滾動的距離妻怎,overFlyingDist= slowTimes * height;

 private void addAndLayoutViewVertical(RecyclerView.Recycler recycler, RecyclerView.State state, int offset) {
        int itemCount = getItemCount();
        if (itemCount <= 0 || state.isPreLayout()) {
            return;
        }
        int displayHeight = getVerticalSpace();
        for (int i = itemCount - 1; i >= 0; i--) {
            Rect mTmpRect = allItemRects.get(i);
            // 遍歷Recycler中保存的View取出來
            int bottomOffset = mTmpRect.bottom - offset;
            int topOffset = mTmpRect.top - offset;
            boolean needAdd = true;//是否
            if (bottomOffset - displayHeight >= overFlyingDist) {
            //超過了底部最大距離
                needAdd = false;
            }
            if (topOffset < -overFlyingDist && i != 0 && topOverFlying
                    || topOffset < -overFlyingDist && !topOverFlying) {
             //超過了頂部最大距離
                needAdd = false;
            }
            if (needAdd) {
            //開始布局
           ..........
           }
    Log.d(TAG, "childCount = " + getChildCount() + "  itemCount= " + itemCount);
    }

打印日志看下結(jié)果壳炎,即使把列表總數(shù)加到1000項,recyclerView中的itemView數(shù)量也始終維持在實際顯示數(shù)量附近逼侦,順便也解決了因為Itemview沒有回收匿辩,兩邊陰影層疊在一起形成黑邊的問題:

QQ截圖20171213202608.png

完善功能

到這已經(jīng)基本實現(xiàn)了我們所期望的功能。但是和系統(tǒng)自帶的三個layoutManager相比榛丢,還欠缺很多基本的對外操作方法铲球,所以我們來依照LinearLayoutManager實現(xiàn)幾個同名方法:

findViewByPosition(int position)

@Override
    public View findViewByPosition(int position) {
        final int childCount = getChildCount();
        if (childCount == 0) {
            return null;
        }
        final int firstChild = getPosition(getChildAt(0));
        final int viewPosition = position - firstChild;
        if (viewPosition >= 0 && viewPosition < childCount) {
            final View child = getChildAt(viewPosition);
            if (getPosition(child) == position) {
                return child; // in pre-layout, this may not match
            }
        }
        return super.findViewByPosition(position);
    }

findViewByPosition(int position)

requestLayout()方法會調(diào)用onLayoutChildren(),offsetUseful 用來標(biāo)識onLayoutChildren()時是否將參數(shù)重置

 @Override
    public void scrollToPosition(int position) {
        Rect mTmpRect = allItemRects.get(position);
        if (mTmpRect != null) {
            offsetUseful = true;
            if (orientation == OrientationHelper.VERTICAL) {
                verticalScrollOffset = mTmpRect.top;
            } else {
                horizontalScrollOffset = mTmpRect.left;
            }
        }
        requestLayout();
    }

scroll.gif

好了至于別的方法,大家如果感興趣的話自己去看源碼吧晰赞!

Github地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睬辐,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子宾肺,更是在濱河造成了極大的恐慌,老刑警劉巖侵俗,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锨用,死亡現(xiàn)場離奇詭異,居然都是意外死亡隘谣,警方通過查閱死者的電腦和手機增拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寻歧,“玉大人掌栅,你說我怎么就攤上這事÷敕海” “怎么了猾封?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長噪珊。 經(jīng)常有香客問我晌缘,道長齐莲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任磷箕,我火速辦了婚禮选酗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘岳枷。我一直安慰自己芒填,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布空繁。 她就那樣靜靜地躺著殿衰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪家厌。 梳的紋絲不亂的頭發(fā)上播玖,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音饭于,去河邊找鬼蜀踏。 笑死,一個胖子當(dāng)著我的面吹牛掰吕,可吹牛的內(nèi)容都是我干的果覆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼殖熟,長吁一口氣:“原來是場噩夢啊……” “哼局待!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起菱属,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤钳榨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后纽门,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體薛耻,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年赏陵,在試婚紗的時候發(fā)現(xiàn)自己被綠了饼齿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝙搔,死狀恐怖缕溉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吃型,我是刑警寧澤证鸥,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響敌土,放射性物質(zhì)發(fā)生泄漏镜硕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一返干、第九天 我趴在偏房一處隱蔽的房頂上張望兴枯。 院中可真熱鬧,春花似錦矩欠、人聲如沸财剖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽躺坟。三九已至,卻和暖如春乳蓄,著一層夾襖步出監(jiān)牢的瞬間咪橙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工虚倒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留美侦,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓魂奥,卻偏偏與公主長得像菠剩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子耻煤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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