其實RecycleView已經出來很長時間了,對RecycleView的用法網上也有很多教程了。本篇文章不講解RecycleView的用法,不講解LayoutManager的用法也不講解ItemDecoration的用法,我們只關注Adapter的用法以及如何封裝成一個通用的Adapter
Adapter的正常使用方法
其實很簡單,只需要繼承RecyclerView.Adapter<VH extends ViewHolder>傳入范型類型為ViewHolder的子類就可以,代碼演示如下:
MainActivity.java
public class MainActivity extends AppCompatActivity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
List<String> datas = new ArrayList<>();
for (int i = 0; i < 20; ++i) {
datas.add("item:" + (i + 1));
}
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
MyAdapter myAdapter = new MyAdapter(datas);
myAdapter.setItemClickListener(new MyAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
recyclerView.setAdapter(myAdapter);
}
private static class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
private List<String> dataList;
private ItemClickListener itemClickListener;
public MyAdapter(List<String> dataList){
this.dataList = dataList;
}
public interface ItemClickListener {
void onItemClicked(View view,int position);
}
//設置點擊回調接口
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
//生成ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view_main1, parent, false);
return new ViewHolder(itemView);
}
private String getItem(int position){
return dataList.get(position);
}
//更新列表Item視圖(根據需要綁定click事件)
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
String str = getItem(position);
// holder.icon.setImageDrawable(xxx);
holder.name.setText(str);
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(itemClickListener != null)
itemClickListener.onItemClicked(v,position);
}
});
}
@Override
public int getItemCount() {
return dataList.size();
}
//ViewHolder保存每個item視圖
public class ViewHolder extends RecyclerView.ViewHolder{
private ImageView icon;
private TextView name;
private View root;
public ViewHolder(View itemView) {
super(itemView);
icon = (ImageView)itemView.findViewById(R.id.icon);
name = (TextView)itemView.findViewById(R.id.id_text);
root = itemView.findViewById(R.id.root);
}
}
}
}
通過以上代碼演示,我們可以得出結論:構造一個比較完整的Adapter至少需要完成以下三件事情
-
onCreateViewHolder
通過視圖Id加載不同Item視圖并生成ViewHolder用來保存每個列表Item視圖 -
onBindViewHolder
更新列表Item視圖(填充model數據) - 新建ViewHolder類來存儲Item視圖及其子視圖
如果需要實現點擊事件,需要在onBindViewHolder中適當綁定點擊事件,比如在以上代碼中綁定了點擊列表視圖中的Item根視圖的點擊事件如下:
holder.root.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(itemClickListener != null)
itemClickListener.onItemClicked(v,position);
}
});
在需要實現具體的點擊事件時調用如下代碼:
myAdapter.setItemClickListener(new MyAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
問題
- 如果需要實現在同一個Item視圖中點擊不同view要實現不同功能
比如以上代碼中處理了點擊Item跟視圖的點擊事件,如果需要點擊根視圖中的icon子視圖如何實現?
是不是繼續寫setOnXXXListener,麻煩也不太現實 - 如果要實現列表中多視圖展示如何實現?正常如果需要支持多視圖咱們是在
onCreateViewHolder
方法中根據不同的ViewType來加載不同的Item視圖代碼演示如下:
//重寫getItemViewType函數,更具需要返回不同的viewType
@Override
public int getItemViewType(int position) {
String model = getItem();
if(...){
return 1;
}else if(...){
return 2;
}
return super.getItemViewType(position);
}
//生成ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int itemViewId = -1;
if(viewType == 1){
itemViewId = R.layout.item_view_main1;
}else if(viewType == 2){
itemViewId = R.layout.item_view_main2;
}
View itemView = LayoutInflater.from(parent.getContext()).inflate(itemViewId, parent, false);
return new ViewHolder(itemView);
}
以上方案純屬針對某種列表來實現的,如果換了一個列表視圖咱們就需要重現創建adapter并分別在onCreateViewHolder
,onBindViewHolder
方法中實現不同邏輯并重新創建xxxViewHolder繼承ViewHolder來保存不同的Item視圖
解決方案
基于以上問題,給出如下方案
LGViewHolder.java
用來設計通用的ViewHolder
public class LGViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mConvertView;//緩存itemView內部的子View
public LGViewHolder(View itemView) {
super(itemView);
mConvertView = itemView;
mViews = new SparseArray<>();
}
/**
* 加載layoutId視圖并用LGViewHolder保存
* @param parent
* @param layoutId
* @return
*/
protected static LGViewHolder getViewHolder(ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
return new LGViewHolder(itemView);
}
/**
* 根據ItemView的id獲取子視圖View
* @param viewId
* @return
*/
public View getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return view;
}
}
LGRecycleViewAdapter.java
public abstract class LGRecycleViewAdapter<T> extends RecyclerView.Adapter<LGViewHolder> {
private final String TAG = "LGRecycleViewAdapter";
//存儲監聽回調
private SparseArray<ItemClickListener> onClickListeners;
private List<T> dataList;
public interface ItemClickListener {
void onItemClicked(View view,int position);
}
public LGRecycleViewAdapter(List<T> dataList) {
this.dataList = dataList;
onClickListeners = new SparseArray<>();
}
/**
* 存儲viewId對應的回調監聽實例listener
* @param viewId
* @param listener
*/
public void setOnItemClickListener(int viewId,ItemClickListener listener) {
ItemClickListener listener_ = onClickListeners.get(viewId);
if(listener_ == null){
onClickListeners.put(viewId,listener);
}
}
/**
* 獲取列表控件的視圖id(由子類負責完成)
* @param viewType
* @return
*/
public abstract int getLayoutId(int viewType);
//更新itemView視圖(由子類負責完成)
public abstract void convert(LGViewHolder holder, T t, int position);
public T getItem(final int position){
if(dataList == null)
return null;
return dataList.get(position);
}
@Override
public LGViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
int layoutId = getLayoutId(viewType);
LGViewHolder viewHolder = LGViewHolder.getViewHolder(parent, layoutId);
return viewHolder;
}
@Override
public void onBindViewHolder(LGViewHolder holder, final int position) {
T itemModel = dataList.get(position);
convert(holder, itemModel, position);//更新itemView視圖
//設置點擊監聽
for (int i = 0; i < onClickListeners.size(); ++i){
int id = onClickListeners.keyAt(i);
View view = holder.getView(id);
if(view == null)
continue;
final ItemClickListener listener = onClickListeners.get(id);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(listener != null){
listener.onItemClicked(v,position);
}
}
});
}
}
@Override
public int getItemCount() {
if (dataList == null)
return 0;
return dataList.size();
}
public void destroyAdapter(){
if(onClickListeners != null)
onClickListeners.clear();
onClickListeners = null;
if(dataList != null)
dataList.clear();
dataList = null;
}
}
使用方法如下:
單視圖方式:
定義adapter
private static class MainAdapter extends LGRecycleViewAdapter<String> {
...
@Override
public int getLayoutId(int viewType) {
return R.layout.item_view_main1;
}
@Override
public void convert(LGViewHolder holder, String s, final int position) {
TextView textView = (TextView) holder.getView(R.id.id_text);
textView.setText(s);
}
}
在需要時使用:
mainAdapter = new MainAdapter(datas);
mainAdapter.setOnItemClickListener(R.id.root, new LGRecycleViewAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"root clicked..." + position);
}
});
mainAdapter.setOnItemClickListener(R.id.icon, new LGRecycleViewAdapter.ItemClickListener() {
@Override
public void onItemClicked(View view, int position) {
Log.d(TAG,"icon clicked..." + position);
}
});
recyclerView.setAdapter(mainAdapter);
可以看出我們輕松的實現了同一Item視圖不同子視圖的點擊監聽,并且在MainAdapter子類中只需通過getLayoutId告訴父類Item視圖對應的視圖id,并在convert方法中只更新視圖即可
如果需要支持多視圖模式則只需在子類中重現getViewType即可,代碼如下:
MainAdapter.java
@Override
public int getLayoutId(int viewType) {
if(viewType == 1)
return R.layout.item_view_main1;
return R.layout.item_view_main2;
}
//支持不同viewType視圖
@Override
public int getItemViewType(int position) {
String model = getItem(position);//實際開發中可以通過model的屬性來決定返回viewType類型
if(position % 2 == 0)
return 1;
return 2;
}
寫在最后
為了簡單起見,本篇文章設計的adapter適合沒有header和footer視圖的RecycleView,至于這方面的功能打算在github上更新
完整的代碼及案例可以到我的github下載
demo開源github地址如下:
LGRecycleViewAdapter
歡迎大家訪問并star,如果有任何問題可以在評論中加以提問,謝謝~~