Android如何自定義RecyclerView的LayoutManager

如果有需要自定義LayoutManger的同學基本都已經(jīng)能熟悉使用RecyclerView乌妙,在此筆者就不再贅述如何使用RecyclerView了兰伤。

首先筆者編寫了一個簡單的demo用來展示使用RecyclerView包自帶的LinearLayoutManager的效果。

這部分代碼可以在HowToCustomLayoutManager找到,檢出tag為1.0.0的版本運行即可
運行后如下圖

device-2016-01-07-145618.png

可以看到筆者對item的大小進行的修改猫牡,但是仍然每一行只顯示一個item,這是LinearLayoutManager的布局策略燕锥。

@Override
public void onBindViewHolder(DemoViewHolder holder, int position) {
    holder.itemView.getLayoutParams().width = (self.getDemoModels().get(position).getPreferWidth());
    holder.itemView.getLayoutParams().height = (self.getDemoModels().get(position).getPreferHeight());
    holder.setDelegate(self);
    holder.reload(self);
}

接下來我們開始創(chuàng)建一個自定義的CustomLayoutManager,先預設(shè)一下想要的效果悯蝉,為了簡單實現(xiàn)归形,筆者自定義的CustomLayoutManager會進行斜線布局,即從容器左上角開始放置item鼻由,下一個item的左上角坐標對應上一個item的右下角坐標连霉。

public class CustomLayoutManager extends RecyclerView.LayoutManager {
    /** Convenience Var to call this */
    final CustomLayoutManager self = this;

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        detachAndScrapAttachedViews(recycler); // 分離所有的itemView

        int offsetX = 0;
        int offsetY = 0;

        for (int i = 0; i < getItemCount(); i++) {
            View scrap = recycler.getViewForPosition(i); // 根據(jù)position獲取一個碎片view,可以從回收的view中獲取嗡靡,也可能新構(gòu)造一個

            addView(scrap);
            measureChildWithMargins(scrap, 0, 0);  // 計算此碎片view包含邊距的尺寸

            int width = getDecoratedMeasuredWidth(scrap);  // 獲取此碎片view包含邊距和裝飾的寬度width
            int height = getDecoratedMeasuredHeight(scrap); // 獲取此碎片view包含邊距和裝飾的高度height

            layoutDecorated(scrap, offsetX , offsetY, offsetX + width, offsetY + height); // Important!布局到RecyclerView容器中窟感,所有的計算都是為了得出任意position的item的邊界來布局

            offsetX += width;
            offsetY += height;
        }
    }

    @Override
    public RecyclerView.LayoutParams generateDefaultLayoutParams() {
        return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }
}

如以上代碼所示讨彼,繼承LayoutManger必須override方法generateDefaultLayoutParams(),以及為了布局必須實現(xiàn)[onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)](http://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager.html#onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State))柿祈。

在[onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)](http://developer.android.com/reference/android/support/v7/widget/LinearLayoutManager.html#onLayoutChildren(android.support.v7.widget.RecyclerView.Recycler, android.support.v7.widget.RecyclerView.State))筆者計算了所有item的尺寸并將所有item都擺放到了RecyclerView中哈误。
效果圖如下

device-2016-01-08-104053.png

這部分代碼可以在HowToCustomLayoutManager找到,檢出tag為1.0.0的版本運行即可
可以看到這個效果確實實現(xiàn)了預定目標躏嚎,斜線擺放蜜自。
然而這里有兩個問題

沒有實現(xiàn)重用

重用機制是RecyclerView的主要性能提升點,如果沒有實現(xiàn)重用使用RecyclerView就沒有意義了卢佣。
上例中如果有50個item重荠,RecyclerView就會有50個view,其中大部分view的坐標都在屏幕外沒有必要顯示虚茶。

無法滑動

滑動也是RecyclerView在大部分情況下應有的功能戈鲁,因為RecyclerView主要是為了解決在較小容器中展示大量數(shù)據(jù)的問題仇参。
滑動在RecyclerView是比較特別的,RecyclerView本身并不執(zhí)行scroll婆殿,例如一個RecyclerView的高度為100诈乒,如果一個item的坐標為(0,100)婆芦,大小為(100怕磨, 100),這個item將被擺放到RecyclerView容器外部消约。
RecyclerView中若想顯示這個item肠鲫,其流程是RecyclerView獲取用戶滑動手勢,判斷LayoutManger是否支持橫向或縱向滑動荆陆,若支持則傳遞信息給LayoutManger滩届,由LayoutManger對item進行平移(也可能是其他操作),而后按照重用機制應當回收容器外部的item被啼,添加新進入容器的item帜消。

接下來筆者就開始進一步優(yōu)化,實現(xiàn)重用和橫向縱向滑動功能浓体。
優(yōu)化后的代碼比較長泡挺,筆者已經(jīng)在代碼中做好的注釋,讀者可以查看HowToCustomLayoutManager命浴,檢出tag為1.0.2的版本運行娄猫,也可以在github直接閱讀CustomLayoutManager

基本原理是這樣的:

在每一次重新對item布局時(item信息改變時)生闲,計算每個item的坐標尺寸記錄下來媳溺,如果一個item的坐標尺寸與當前顯示區(qū)域矩陣相交就展示這個item,否則回收這個item碍讯。
顯示區(qū)域有滑動偏移量和容器大小決定悬蔽,每次滑動時都要進行重新布局。

感謝閱讀捉兴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蝎困,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子倍啥,更是在濱河造成了極大的恐慌禾乘,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虽缕,死亡現(xiàn)場離奇詭異始藕,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門鳄虱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來弟塞,“玉大人,你說我怎么就攤上這事拙已【黾牵” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵倍踪,是天一觀的道長系宫。 經(jīng)常有香客問我,道長建车,這世上最難降的妖魔是什么扩借? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮缤至,結(jié)果婚禮上潮罪,老公的妹妹穿的比我還像新娘。我一直安慰自己领斥,他們只是感情好嫉到,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著月洛,像睡著了一般何恶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嚼黔,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天细层,我揣著相機與錄音,去河邊找鬼唬涧。 笑死疫赎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的碎节。 我是一名探鬼主播捧搞,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钓株!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起陌僵,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤轴合,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后碗短,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體受葛,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了总滩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纲堵。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闰渔,靈堂內(nèi)的尸體忽然破棺而出席函,到底是詐尸還是另有隱情,我是刑警寧澤冈涧,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布茂附,位于F島的核電站,受9級特大地震影響督弓,放射性物質(zhì)發(fā)生泄漏营曼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一愚隧、第九天 我趴在偏房一處隱蔽的房頂上張望蒂阱。 院中可真熱鬧,春花似錦狂塘、人聲如沸录煤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辐赞。三九已至,卻和暖如春硝训,著一層夾襖步出監(jiān)牢的瞬間响委,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工窖梁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赘风,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓纵刘,卻偏偏與公主長得像邀窃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子假哎,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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