使用 RecyclerView 實(shí)現(xiàn)下面的效果:
- 有兩種布局缰贝,紅色布局和藍(lán)色布局
- 偶數(shù)位置的是紅色布局孔庭,奇數(shù)位置的是藍(lán)色布局
按照一般的方法來實(shí)現(xiàn) Adapter 的話茁彭,代碼如下
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_RED = 0;
private static final int TYPE_BLUE = 1;
private List<String> mDataSource;
public MyAdapter(List<String> dataSource) {
mDataSource = dataSource;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_RED) {
return new RedViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_red, parent, false));
} else {
return new BlueViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_blue, parent, false));
}
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == TYPE_RED) {
RedViewHolder redViewHolder = (RedViewHolder) holder;
redViewHolder.tv.setText(mDataSource.get(position));
} else {
BlueViewHolder blueViewHolder = (BlueViewHolder) holder;
blueViewHolder.tv.setText(mDataSource.get(position));
}
}
@Override
public int getItemCount() {
return mDataSource == null ? 0 : mDataSource.size();
}
@Override
public int getItemViewType(int position) {
if (position % 2 == 0) {
return TYPE_RED;
} else {
return TYPE_BLUE;
}
}
private static class RedViewHolder extends RecyclerView.ViewHolder {
TextView tv;
RedViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
private static class BlueViewHolder extends RecyclerView.ViewHolder {
TextView tv;
BlueViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
因?yàn)橛卸喾N布局熬丧,所以重點(diǎn)關(guān)注的是如何判斷在某一個(gè)位置要如何展示哪一個(gè)布局糕簿,RecyclerView.Adapter 提供了 int getItemViewType(int position)
方法讓我們重寫舵鳞,給每一個(gè)布局指定一個(gè)唯一標(biāo)識(shí) ItemViewType
按照效果圖:偶數(shù)位置的是紅色布局震檩,奇數(shù)位置的是藍(lán)色布局,所以方法重寫如下
// 布局的唯一標(biāo)識(shí)
private static final int TYPE_RED = 0;
private static final int TYPE_BLUE = 1;
// 布局的判斷
@Override
public int getItemViewType(int position) {
if (position % 2 == 0) {
return TYPE_RED;
} else {
return TYPE_BLUE;
}
}
有了布局的唯一標(biāo)識(shí) ItemViewType
之后蜓堕,就可以在
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
和
void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
中根據(jù)這個(gè)唯一標(biāo)識(shí)抛虏,創(chuàng)建和綁定不同的 ViewHolder 了。
缺陷
這個(gè)寫法沒什么毛病套才,但是在維護(hù)上會(huì)有一些問題嘉蕾。如果將來要增加一個(gè)新的布局,假設(shè)位置的尾數(shù)逢 5 的地方需要顯示綠色布局霜旧,就需要改動(dòng)幾個(gè)地方
- 增加或布局的唯一標(biāo)識(shí)错忱,也就是改動(dòng)這個(gè)地方
private static final int TYPE_RED = 0;
private static final int TYPE_BLUE = 1;
private static final int TYPE_GREEN = 2; // 增加一個(gè)標(biāo)識(shí)
- 增加一個(gè) ViewHolder
private static class GreenViewHolder extends RecyclerView.ViewHolder {
TextView tv;
GreenViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
- 修改
int getItemViewType(int position)
的 if-else 判斷
@Override
public int getItemViewType(int position) {
// 這里的 if - else 判斷需要修改
if (position % 2 == 0) {
return TYPE_RED;
} else if (position % 5 == 0){
return TYPE_GREEN;
} else {
return TYPE_BLUE;
}
}
- 修改
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
方法儡率,增加一個(gè) ViewHolder
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_RED) {
return new RedViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_red, parent, false));
} else if (viewType == TYPE_GREEN){
// 增加一個(gè) GreenViewHolder
return new GreenViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_green, parent, false));
} else {
return new BlueViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_blue, parent, false));
}
}
- 修改
void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
方法,處理 GreenViewHolder 的邏輯
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == TYPE_RED) {
RedViewHolder redViewHolder = (RedViewHolder) holder;
redViewHolder.tv.setText(mDataSource.get(position));
} else if (holder.getItemViewType() == TYPE_GREEN){
// 處理 GreenViewHolder 的邏輯
GreenViewHolder greenViewHolder = (GreenViewHolder) holder;
greenViewHolder.tv.setText(mDataSource.get(position));
} else {
BlueViewHolder blueViewHolder = (BlueViewHolder) holder;
blueViewHolder.tv.setText(mDataSource.get(position));
}
}
發(fā)現(xiàn)問題
可以看到增加一個(gè)布局以清,需要改動(dòng)很多地方儿普。問題的根源就在于 if-else 判斷這一塊代碼。如果讓一個(gè)類來幫我們?cè)?int getItemViewType(int position)
方法中返回唯一標(biāo)識(shí)掷倔,同時(shí)在 RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
和
void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
方法中幫我們根據(jù) itemViewType 拿到 ViewHolder眉孩,這樣就能大幅度地減少代碼。
實(shí)現(xiàn)
可以看到 itemViewType 和 ViewHolder 是一一對(duì)應(yīng)的關(guān)系勒葱,那么就可以使用 HashMap<Integer, RecyclerView.ViewHolder>
來存放 itemViewType 和 ViewHolder浪汪。
接著再定義一個(gè)接口
public interface AdapterDelegate<T> {
/**
* 確定唯一標(biāo)識(shí)
*/
boolean isForViewType(List<T> items, int position);
/**
* 創(chuàng)建 ViewHolder
*/
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
/**
* 綁定 ViewHolder
*/
void onBindViewHolder(List<T> items, int position, RecyclerView.ViewHolder holder);
}
把確定唯一標(biāo)識(shí)、創(chuàng)建 ViewHolder 和 綁定 ViewHolder 的工作交給實(shí)現(xiàn)接口的具體的 ViewHolder 來實(shí)現(xiàn)凛虽。
最終效果
MyAdapter 現(xiàn)在只有這些代碼
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private AdapterDelegatesManager<String> mAdapterDelegatesManager;
private List<String> mDataSource;
public MyAdapter(List<String> dataSource) {
mDataSource = dataSource;
mAdapterDelegatesManager = new AdapterDelegatesManager<>();
mAdapterDelegatesManager // 在這里綁定 ViewHolder
.addDelegate(new RedViewHolder())
.addDelegate(new GreenViewHolder())
.addDelegate(new BlueViewHolder());
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return mAdapterDelegatesManager.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
mAdapterDelegatesManager.onBindViewHolder(mDataSource, position, holder);
}
@Override
public int getItemCount() {
return mDataSource == null ? 0 : mDataSource.size();
}
@Override
public int getItemViewType(int position) {
return mAdapterDelegatesManager.getItemViewType(mDataSource, position);
}
}
剩下的就是定義 ViewHolder 實(shí)現(xiàn) AdapterDelegate<T> 接口就行
/**
* RedViewHolder
*/
private static class RedViewHolder implements AdapterDelegate<String> {
@Override
public boolean isForViewType(List<String> items, int position) {
return position % 2 == 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_red, parent, false));
}
@Override
public void onBindViewHolder(List<String> items, int position, RecyclerView.ViewHolder holder) {
ViewHolder redViewHolder = (ViewHolder) holder;
redViewHolder.tv.setText(items.get(position));
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView tv;
ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
/**
* BlueViewHolder
*/
private static class BlueViewHolder implements AdapterDelegate<String> {
@Override
public boolean isForViewType(List<String> items, int position) {
return position % 2 != 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_blue, parent, false));
}
@Override
public void onBindViewHolder(List<String> items, int position, RecyclerView.ViewHolder holder) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.tv.setText(items.get(position));
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView tv;
ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
/**
* GreenViewHolder
*/
private static class GreenViewHolder implements AdapterDelegate<String> {
@Override
public boolean isForViewType(List<String> items, int position) {
return position % 5 == 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_green, parent, false));
}
@Override
public void onBindViewHolder(List<String> items, int position, RecyclerView.ViewHolder holder) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.tv.setText(items.get(position));
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView tv;
ViewHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.tv);
}
}
}
核心代碼的實(shí)現(xiàn)
這個(gè)類是按照 JOE'S GREAT ADAPTER HELL ESCAPE 介紹的思想來實(shí)現(xiàn)的死遭,核心指出就在于 itemViewType 和 ViewHolder 是一一對(duì)應(yīng)的關(guān)系。
AdapterDelegate 接口
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import java.util.List;
public interface AdapterDelegate<T> {
boolean isForViewType(List<T> items, int position);
RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent);
void onBindViewHolder(List<T> items, int position, RecyclerView.ViewHolder holder);
}
AdapterDelegatesManager 的實(shí)現(xiàn)
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.ViewGroup;
import java.util.List;
public class AdapterDelegatesManager<T> {
/**
* Key: ItemViewType
* Value: AdapterDelegate
*/
private SparseArray<AdapterDelegate<T>> mDelegates = new SparseArray<>();
public AdapterDelegatesManager<T> addDelegate(AdapterDelegate<T> delegate) {
if (delegate == null) {
throw new NullPointerException("AdapterDelegate of item can not be null");
} else {
int newItemViewType = mDelegates.size();
mDelegates.put(newItemViewType, delegate);
}
return this;
}
public int getItemViewType(List<T> items, int position) {
int delegateCount = mDelegates.size();
for (int itemViewType = 0; itemViewType < delegateCount; itemViewType++) {
AdapterDelegate<T> delegate = mDelegates.valueAt(itemViewType);
if (delegate.isForViewType(items, position)) {
return itemViewType;
}
}
throw new IllegalArgumentException(
"No AdapterDelegate added that matches position=" + position + " in data source");
}
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
AdapterDelegate delegate = mDelegates.get(viewType);
if (delegate == null) {
throw new NullPointerException("No AdapterDelegate added for ViewType " + viewType);
} else {
return delegate.onCreateViewHolder(parent);
}
}
public void onBindViewHolder(List<T> items, int position, RecyclerView.ViewHolder viewHolder) {
AdapterDelegate<T> delegate = mDelegates.get(viewHolder.getItemViewType());
if (delegate == null) {
throw new NullPointerException(
"No AdapterDelegate added for ViewType " + viewHolder.getItemViewType());
} else {
delegate.onBindViewHolder(items, position, viewHolder);
}
}
}
參考來源
http://hannesdorfmann.com/android/adapter-delegates
https://github.com/sockeqwe/AdapterDelegates