Android開發遊記:RecycleView 實現複雜首頁布局三種方式

來源:互聯網
上載者:User

標籤:

做過電商類應用的朋友可能都會遇到一個比較頭疼的問題:複雜的首頁布局如何?。參考百度糯米,美團,bilibili等應用,都會發現其首頁的布局相對複雜,例如bilibili的首頁(第二張是demo實現的),可以看到在同一個頁面中先是有列表布局出現,然後出現了2列的網格布局,接著3列的網格布局,最後還出現了瀑布流式布局:

這樣的效果該怎麼做呢?是使用LinearLayoutManager、GridLayoutManager還是StaggeredGridLayoutManager?還是根本不是使用的RecycleView,是用ScrollView硬布局實現的?或者使用了多個RecycleView進行嵌套,來實現3種混合布局的排版的?

下面我們一條一條進行梳理:

  • 首先,我們發現頁面的長度是無限長度的,可以不斷下拉重新整理,所有排除ScrollView的可能,基本斷定是使用的是RecyclerView
  • 我們注意到同一個頁面中出現了3中混合布局的排版,有可能是使用RecycleView進行了2級嵌套,線上性RecycleView中嵌套了網格和瀑布的RecycleView?
  • 如果沒有進行嵌套的話,有沒有辦法用一種自訂的布局管理器實現這3種效果呢?
  • 會不會是使用了某些3方控制項?

針對以上3點疑問,我分別提供3種對應的解決方案來實現的效果:

方案一:RecyclerView的2級嵌套

看到同一個滾動控制項中出現了3種混合布局,多數人第一映像就是進行嵌套。
如果進行嵌套的話,嵌套什嗎?從來看,的一個欄目中的視圖數量似乎是固定的,這意味著可以使用RelativeLayout等布局進行硬排版。確實如果真是固定的這樣做當然更好,但是注意到點擊每個欄目上的重新整理按鈕的時候,偶爾會出現兩個視圖交換位置的動畫,這是RecyclerView特有的,而且也沒有人告訴我每個欄目中的視圖數量就是固定,萬一哪天又多了一排呢,所以我們還是嵌套RecyclerView,具體嵌套規則如:

其實最外層的RecyclerView1換成ScrollView也可以,只要把內部的RecyclerView依次拼接起來就可以,實現起來也更加簡單,實現方式千千萬,自己選個喜歡的而已,我只是為了便於拓展,萬一需要動態增加欄目呢。

上一篇就說到了RecyclerView的嵌套的問題,給每個RecyclerView設定對應的Fully****LayoutManager就可以了。具體實現參見源碼,我就不貼了,重新getItemViewType方法為每個position位置的item設定不同的type類型,然後在onCreateViewHolder建立對應的Holder,最後在onBindViewHolder為不同類型的item設定不同的Fully布局的子RecyclerView就行了。

上關鍵代碼(有刪減):

    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        if (holder instanceof MyViewHolder1){            holder.child_recyclerView.setLayoutManager(new FullyLinearLayoutManager(context));        }else if (holder instanceof MyViewHolder2){            holder.child_recyclerView.setLayoutManager(new FullyGridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false));        }else if (holder instanceof MyViewHolder3){            holder.child_recyclerView.setLayoutManager(new FullyGridLayoutManager(context, 3, GridLayoutManager.VERTICAL, false));        }else if (holder instanceof MyViewHolder4){            holder.child_recyclerView.setLayoutManager(new FullyGridLayoutManager(context, 3, GridLayoutManager.VERTICAL, false));        }    }

這種方式我不多介紹,因為在前一篇也說過,google不建議對RecyclerView、ListView進行嵌套,嵌套使用的話那麼視圖複用機制就等於白費了,依然會在第一次載入時初始化所有的item,如果資料量過大則會產生效能障礙,所以我也遵循google的建議不推薦使用嵌套解決問題,我更加推薦下面一種方案。

方案二:使用特殊的(自訂)布局管理器

這種方案是我認為最為優秀的做法,它完全符合Google制定的標準:使用布局管理器來管理布局。我們繼續觀察首頁布局的圖示,我們真的要為了實現這種混合布局自己去寫一個布局管理器嗎?我們發現上面出現了列表、網格、瀑布流3種交叉混排的混合布局。我們先把瀑布流放在一邊,仔細想想如果我們把網格的列數設定為1列,那不就是一個列表布局嗎,也就是說我們使用網格布局管理器就可以做出列表的樣式,所以說雖然是說用自訂布局管理器,但實際上不需要我們自訂,GridLayoutManager為我們提供了動態改變每個item所佔列數的方法:

gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {       @Override       public int getSpanSize(int position) {          return gridManager.getSpanCount();       }}

getSpanSize方法,傳回值就表示當前item佔多少列,例如如果我們列數設定的為3列,返回3就表示鋪滿,也就是和列表一樣了。

,我們給RecyclerView設定一個列數為6的GridLayoutManager,然後再動態地為不同部位的item分別設定SpanSize為6(鋪滿)、3(1/2)、2(1/3)就行了

設定一個列數為6的GridLayoutManager:

recyclerView.setLayoutManager(new GridLayoutManager(recyclerView.getContext(), 6, GridLayoutManager.VERTICAL, false));

在onAttachedToRecyclerView方法中動態為不同position設定不同的SpanSize:

    @Override    public void onAttachedToRecyclerView(final RecyclerView recyclerView) {        super.onAttachedToRecyclerView(recyclerView);        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();        if(manager instanceof GridLayoutManager) {            final GridLayoutManager gridManager = ((GridLayoutManager) manager);            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {                @Override                public int getSpanSize(int position) {                    int type = getItemViewType(position);                    switch (type){                        case TYPE_SLIDER:                        case TYPE_TYPE2_HEAD:                        case TYPE_TYPE3_HEAD:                            return 6;                        case TYPE_TYPE2:                            return 3;                        case TYPE_TYPE3:                            return 2;                        default:                            return 3;                    }                }            });        }    }

那麼有朋友就要問了,說好的瀑布流呢?
我查閱了StaggeredGridLayoutManager,發現它並沒有提供動態設定所佔列的方法,只是在StaggeredGridLayoutManager.LayoutParams中提供了這樣一個方法:

LayoutParams.setFullSpan(true);

作用是把當前item的寬度設為full(填滿),也就是說如果使用StaggeredGridLayoutManager要麼不設定,要麼就只能填滿,所以無法完成圖上的效果,我們也並不是非要完全仿照它,bilibili在最近一次更新後也放棄使用瀑布流式的布局了,統一為列表和網格式混排。

當然如果要實現圖上的效果也不是沒有辦法,只需要換一種方式,改一下item,把設定為FullSpan的item設定為一個多個視圖組合的複合item就行了,放個圖,代碼就不上了:

方案三:使用第三方控制項實現

我幾乎翻遍了github上的三方RecyclerView,最後發現一款挺有意思的twoway-view

TwowayView是繼承自RecyclerView在其功能上拓展的一個特殊TwowayView,其實現了可動態進行自由排列的布局管理器,可實現列表,網格,瀑布,表格版面配置。功能比較強大,這裡主要使用其瀑布流來實現效果:

添加依賴:

repositories {    maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }    maven { url "https://jitpack.io" }}dependencies {    compile ‘org.lucasr.twowayview:core:1.0.0-SNAPSHOT@aar‘    compile ‘org.lucasr.twowayview:layouts:1.0.0-SNAPSHOT@aar‘}

添加布局:

<org.lucasr.twowayview.widget.TwoWayView        android:id="@+id/twowayView"        android:scrollbars="vertical"        android:layout_width="match_parent"        android:layout_height="match_parent"        app:twowayview_layoutManager="StaggeredGridLayoutManager"        app:twowayview_numColumns="6" />

TwoWayView支援在屬性中設定布局管理器,這樣就不必在代碼中設定了,設定列數為6列

設定適配器:

public class TwowayRecycleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        ...    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        ...    }    @Override    public int getItemCount() {        ...    }    @Override    public int getItemViewType(int position) {        ...    }}

具體代碼參見demo,只說下重點:
實現getItemViewType方法,劃分不同的item類型:

    @Override    public int getItemViewType(int position) {        if (position == 0){            return TYPE_SLIDER;        }else if (position == 1){            return TYPE_TYPE2_HEAD;        }else if (2<=position && position <= 7){            return TYPE_TYPE2;        }else if (position == 8){            return TYPE_TYPE3_HEAD;        }else if (9<=position && position <= 14){            return TYPE_TYPE3;        }else if (15<=position && position <= 18){            return TYPE_TYPE4;        }else {            return TYPE_TYPE5;        }    }
    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        View itemView = holder.itemView;        final StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) itemView.getLayoutParams();        switch (getItemViewType(position)) {            case TYPE_SLIDER:            case TYPE_TYPE2_HEAD:            case TYPE_TYPE3_HEAD:            case TYPE_TYPE4:                lp.span = 6;                break;            case TYPE_TYPE2:                lp.span = 3;                break;            case TYPE_TYPE3:                lp.span = 2;                break;            case TYPE_TYPE5:                lp.span = 3;                if (position % 3 == 0) {                    lp.height = 200;                } else if (position % 5 == 0) {                    lp.height = 300;                } else if (position % 7 == 0) {                    lp.height = 500;                } else {                    lp.height = 400;                }                break;        }        itemView.setLayoutParams(lp);        ...    }

在onBindViewHolder中通過item的position判斷其類型,然後設定item的span(及所佔幾列),height(高度,不設定及為item本身高度)

哪種方案最好?

上面的3種方案中,無疑第二種是最佳的解決方案,沒有引入過多的依賴,完全使用RecyclerView布局管理器的特性實現,從效能上來說也最佳。第三種方案其實也是實現的一種特殊的布局管理器來實現特殊排版的和第二種原理上是一樣的,只不過第二種方案使用的是google提供的內建的工具,而後者是重新實現的。但是TwowayView能做到更多其他的支援,比如網格布局等(具體參見github : twoway-view),也不失為一種好的方式,而第一種方案我也就不多說了,不推薦,這種嵌套不僅讓代碼邏輯變得複雜淩亂(demo裡寫嵌套的時候我差點沒寫吐),同時還有效能障礙,所以給出一個結論:2>3>1

下載

最後,附上demo下載:
demo

Android開發遊記:RecycleView 實現複雜首頁布局三種方式

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.