RecycleView多種布局顯示

RecycleView多種布局顯示


1.前言

  • 我們知道ListView多種布局顯示用到兩個方法一個getItemViewType和getViewTypeCount方法。
    • getitemViewType方法告訴ListView我在第幾個position展示哪種布局
    • getViewTypeCount方法告訴ListView我有多少種布局
  • 那么RecycleView如何實現(xiàn)多布局顯示呢变汪?其實這個和ListView原理一樣四啰,我們待會說,先來了解下實現(xiàn)RecycleView.Adapter會有哪幾個方法要重寫
    • getItemCount()告訴RecycleView我有多少個item數(shù)量
    • onCreateViewHolder()創(chuàng)建自己的holder并返回
    • onBindViewHolder()綁定holder
    • 創(chuàng)建自己的holder只需繼承成RecycleView.ViewHolder即可(這一句不是重寫adapter的方法杠巡,而是在繼承RecycleView.Adapter的時候需要指定泛型耸三,而這個泛型就是你的ViewHolder)拓轻。

2.多布局顯示

RecycleView.Adapter也提供了getItemViewType方法旬盯,此方法和ListView加載多布局一樣而钞。我們只要在該方法中判斷某個位置下返回某一種類型的布局即可沙廉。比如這樣給RecycleView加頭布局和腳布局(以下代碼是偽代碼):

  • 你得告訴RecycleView你要加載的item數(shù)量

      public int  getItemCount() {
          return mDatas.size()+2;
      }
    
  • 在getItemViewType中根據(jù)不同位置的position返回不同布局類型

    if(position==0){
    return  頭布局類型;
    }else if(position==getitemCount()-1){
    return  腳布局類型;
    }else {
    return  默認布局類型臼节;
    }
  • 在onCreateViewHolder(ViewGroup parent, intviewType)根據(jù)不同類型的type來返回不同的view給自己的ViewHolder撬陵。FooterView和SwitchView如何來的,其實是自己的adapter提供兩個方法setHeaderView()和setFooterView通過外界傳遞進來的官疲。

    public ListHolder onCreateViewHolder(ViewGroup parent, intviewType) {
       View root =null;
       if(viewType ==頭布局類型) {
       root =mHeaderView;
       }else if(viewType==腳布局類型){
        root=mFooterView;
       }else{
        root = View.inflate(context,R.layout.list_item,null);
      }
       return newListHolder(root,viewType);
    }
    
  • 在自己的ViewHolder中進行處理如果是頭布局或者腳布局直接返回

    public static class ListHolderextends RecyclerView.ViewHolder{
       TextView tv;
       publicListHolder(View root, intviewType) {
             super(root);
         if(viewType==頭布局類型){
           return ;
       }
         if(viewType==腳布局類型){
         return ;
       }
       tv= (TextView)root.findViewById(R.id.item);
    }
    
  • 在onBindViewHolder(ListHolder holder, intposition)方法中綁定數(shù)據(jù)

    • 綁定View袱结,這里是根據(jù)返回的這個position的類型,從而進行綁定的途凫,HeaderView和FooterView就不綁定了
        public void  onBindViewHolder(ListHolder holder,  intposition) {
                int  itemViewType = getItemViewType(position);
                if(itemViewType ==頭布局類型) {
                    return;
               }else if(itemViewType ==腳布局類型) {
                  return;
               }else{
               //這里注意因為加了一個頭布局position-1才是正確的數(shù)據(jù)
               holder.tv.setTag(position-1);
               holder.tv.setText(mDatas.get(position -1));
               }
           }

原理講完了垢夹,那么接下來就應該講講實際的東西了。

3.需求

  • 一般情況下维费,RecycleView加載多布局就是頭部一個輪播圖腳部一個加載更多果元,那么今天帶給大家是RecycleView四種布局加載,為什么是四種布局呢犀盟,其實和以上三種布局原理是一樣的而晒,只是為了多說一下,recycleView切換視圖列數(shù)阅畴。
      請自動忽略圖丑
      請自動忽略圖丑
  • 我們分析下上面給出的兩張圖

    • 藍色背景是headerView
    • 紅色背景用來切換列表我們這里就叫做swichView
    • 綠色部分就是我們的數(shù)據(jù)布局了
    • 藍色部分為FooterView
  • 要達到這種布局倡怎,那么首先你得在你的adapter中getitemCount中返回數(shù)據(jù)長度+3

  • 在getItemViewType方法中根據(jù)position返回不同類型布局 ......和上面的三種布局一致這里就不多說了。
    主要講解如何監(jiān)聽RecycleView滑動到底部自定加載數(shù)據(jù)呢贱枣?如何切換item的列數(shù)呢监署?

4.滑動監(jiān)聽

  • 如何監(jiān)聽RecycleView滑動到底部自定加載數(shù)據(jù)?
    • RecycleView有個addOnScrollListener方法纽哥,此方法接受一個OnScrollListener的子類并重寫onScrolled钠乏,當RecycleView滑動的時 候會回調(diào)onScrolled方法
    • onScrolled(RecyclerView recyclerView, int dx, int dy) 參數(shù)二:水平滾動距離,參數(shù)三:豎直滾動距離
  • 那我們?nèi)绾螌崿F(xiàn)呢春塌?
    • 自定義一個類EndLessOnScrollListener 繼承RecyclerView.OnScrollListener重寫onScrolled方法晓避。完整代碼如下:
           public abstract class EndLessOnScrollListener extends  RecyclerView.OnScrollListener{
                public static final String TAG =EndLessOnScrollListener.class.getName();
                private GridLayoutManager gridLayoutManager;

                //已經(jīng)加載出來的Item的數(shù)量
                private int totalItemCount;

                //主要用來存儲上一個totalItemCount
                 private int previousTotal = 0;

                //在屏幕上可見的item數(shù)量
                private int visibleItemCount;

                //在屏幕可見的Item中的第一個
                private int firstVisibleItem;

                //是否正在上拉數(shù)據(jù)
                private boolean loading = true;

                //當前頁簇捍,從1開始
                private int currentPage =1;
            public EndLessOnScrollListener(GridLayoutManager gridLayoutManager) {
                this.gridLayoutManager = gridLayoutManager;
            }

              @Override
              public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
              super.onScrolled(recyclerView, dx, dy);
              visibleItemCount = recyclerView.getChildCount();
              totalItemCount = gridLayoutManager.getItemCount();
               firstVisibleItem = gridLayoutManager.findFirstVisibleItemPosition();
              if(loading){
                  if(totalItemCount > previousTotal){
                    //說明數(shù)據(jù)已經(jīng)加載結束
                    loading = false;
                    previousTotal = totalItemCount;
                }
            }
            /**
             *      當不是正在加載時并且
             *    totalItemCount-visibleItemCount得到的值表示已經(jīng)滾出和未滑入的總數(shù),
             *    firstVisibleItem 也可以表示已經(jīng)滾出屏幕的item個數(shù)
             *    其實也可以寫成這樣
             *     int i = totalItemCount - visibleItemCount; i就是滾出和未滾入的和
             *       i-firstVisibleItem得到的是未滾入的  如果未滾入=0或者小于0說明已經(jīng)滾動到底部了
             *       if(!loading&&i-firstVisibleItem<=0){
             *        currentPage ++;
             *        onLoadMore(currentPage);
             *        loading = true;
             *     }
             */
            if (!loading && totalItemCount-visibleItemCount <= firstVisibleItem){
                currentPage ++;
                onLoadMore(currentPage);
                loading = true;
                }
            }
                /**
                 * 提供一個抽象方法俏拱,在Activity中監(jiān)聽到這個EndLessOnScrollListener
                 * 并且實現(xiàn)這個方法
                 */
                public abstract void onLoadMore(int currentPage);

                }
}
  • 如何使用呢暑塑?

      rec.addOnScrollListener(new EndLessOnScrollListener(gridLayoutManager) {
              @Override
         public void onLoadMore(int currentPage) {
                  footerViewInTextView.setText("正在加載請稍后...");
                  getData(false);
              }
        }); 
    

因為要模擬網(wǎng)絡請求,這里用的是rxjava timer操作符來模擬耗時锅必,count用來模擬數(shù)據(jù)加載完畢

    private void getData(final boolean falg) {
    if(refreshSubscribe!=null&&!refreshSubscribe.isUnsubscribed()){
        refreshSubscribe.unsubscribe();
    }
    if(count==3){
        footerViewInTextView.setText("沒有更多數(shù)據(jù)了");
        return ;
    }
    if(falg){
        swRefresh.setRefreshing(true);
    }
    refreshSubscribe = Observable.timer(3, TimeUnit.SECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Long>() {
        @Override
        public void onCompleted() {
            for (int x = 30*count; x < 30*(1+count); x++) {
                 list.add("item" + x);
            }
            if(falg){
                swRefresh.setRefreshing(false);
            }
            count++;
            notifyData();
        }

        @Override
        public void onError(Throwable e) {
        }
        @Override
        public void onNext(Long aLong) {

        }
    });
}

5.更改布局列數(shù)

寫之前先來看一下動圖:

實現(xiàn)原理很簡單:

  • adapter定義兩種類型

    • 一列類型
    • 三列類型
  • 默認加載三列類型那么可以提供一個變量來默認加載三列類型,提供外界一個可以設置類型的方法梯投。用來更改列數(shù)

  • 在getItemViewType中在返回默認布局的時候再次判斷下是三列還是一列

    public int getItemViewType(int position) {
      if (position == 0){
      //第一個item應該加載Header
      return TYPE_HEADER;
      //第二個選擇切換布局
      }else if(position==1){
        return TYPE_SWITCHER;
      }if (position == getItemCount()-1){
       //最后一個,應該加載Footer
      return TYPE_FOOTER;
      }else {
         //如果是三列 
         if(showOneOrThree==TYPE_SHOW_THREE){
             return  TYPE_SHOW_THREE;
         }else {
         //一列    
             return  TYPE_SHOW_ONE;
         }
      }
    }
    
  • 同樣在onBindViewHolder中判斷類型,測試點擊的是哪一種列數(shù)

      public void onBindViewHolder(ListHolder holder,  int position) {
      int itemViewType = getItemViewType(position);
      if (itemViewType == TYPE_HEADER) {
          return;
      } else if (itemViewType == TYPE_SWITCHER) {
          return;
      } else if (itemViewType == TYPE_FOOTER) {
          return;
      } else {
          holder.tv.setTag(position-2);
          holder.tv.setText(mDatas.get(position - 2));
          if(itemViewType==TYPE_SHOW_ONE){
              holder.tv.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      Integer tag = (Integer) v.getTag();
                      Toast.makeText(context,"一行單列:"+mDatas.get(tag),Toast.LENGTH_SHORT).show();
                  }
              });
          }else {
              holder.tv.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      Integer tag = (Integer) v.getTag();
                      Toast.makeText(context,"一行三列:"+mDatas.get(tag),Toast.LENGTH_SHORT).show();
                      }
                  });
              }
          }
      }
    
  • 外界更改并刷新

    //三列
    to_gridview.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (myAdapter != null) {
                int spanSize = myAdapter.getShowOneOrThree();
                //一列變?nèi)?三列就不管
                if (spanSize == RECYCLEVIEW_SHOW_ONE) {
                    gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                        @Override
                        public int getSpanSize(int position) {
                            if (position == 0) {
                                return 3;
                            } else if (position == 1) {
                                return 3;
                            } else if (myAdapter.getItemCount() - 1 == position) {
                                return 3;
                            } else {
                                return 1;
                            }
                        }
                    });
                    //更改狀態(tài)
                    myAdapter.setShowOneOrThree(RECYCLEVIEW_SHOW_THREE);
                    //刷新視圖
                    myAdapter.notifyItemRangeChanged(3, myAdapter.getItemCount());
                }
            }
        }
    });
    //一列
    to_listview.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (myAdapter != null) {
                int spanSize = myAdapter.getShowOneOrThree();
                //三列變一列况毅,一列就不管
                if (spanSize == RECYCLEVIEW_SHOW_THREE) {
                    gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                        @Override
                        public int getSpanSize(int position) {
                            if (position == 0) {
                                return 3;
                            } else if (position == 1) {
                                return 3;
                            } else if (myAdapter.getItemCount() - 1 == position) {
                                return 3;
                            } else {
                                return 3;
                            }
                        }
                    });
                    //更改狀態(tài)
                    myAdapter.setShowOneOrThree(RECYCLEVIEW_SHOW_ONE);
                    //刷新視圖
                    myAdapter.notifyItemRangeChanged(3, myAdapter.getItemCount());
                }
            }
        }
    });

這里要說下gridLayoutManager.setSpanSizeLookup方法,此方法的作用是用來確定一個item占用幾列尔艇。因為我們默認用的是gridViewManager
來加載三列的尔许,所以原始視圖是三列。

  • 當position==0時 是頭布局终娃,頭布局寬度是和屏幕一樣寬的味廊,占用三列
  • position==1時,是切換列數(shù)布局棠耕。同樣占三列
  • position==myAdapter.getItemCount() - 1 腳布局余佛,占三列
  • 其余根據(jù)所要切換的列數(shù)來返回。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窍荧,一起剝皮案震驚了整個濱河市辉巡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蕊退,老刑警劉巖郊楣,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓤荔,居然都是意外死亡净蚤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門输硝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來今瀑,“玉大人,你說我怎么就攤上這事点把¢佘” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵愉粤,是天一觀的道長砾医。 經(jīng)常有香客問我,道長衣厘,這世上最難降的妖魔是什么如蚜? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任压恒,我火速辦了婚禮,結果婚禮上错邦,老公的妹妹穿的比我還像新娘探赫。我一直安慰自己,他們只是感情好撬呢,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布伦吠。 她就那樣靜靜地躺著,像睡著了一般魂拦。 火紅的嫁衣襯著肌膚如雪毛仪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天芯勘,我揣著相機與錄音箱靴,去河邊找鬼。 笑死荷愕,一個胖子當著我的面吹牛衡怀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播安疗,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼抛杨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荐类?” 一聲冷哼從身側響起怖现,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掉冶,沒想到半個月后真竖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡厌小,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年恢共,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片璧亚。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡讨韭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癣蟋,到底是詐尸還是另有隱情透硝,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布疯搅,位于F島的核電站濒生,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏幔欧。R本人自食惡果不足惜罪治,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一丽声、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧觉义,春花似錦雁社、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至洪囤,卻和暖如春徒坡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘤缩。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工崭参, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人款咖。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像奄喂,于是被迫代替她去往敵國和親铐殃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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