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ù)來返回。