在項目中大量使用了RecyclerView控件來展示復雜的列表而昨,關于RecyclerView寫的最多的就是Adapter了。
對于Adapter一般都繼承RecyclerView.Adapter復寫幾個方法找田,寫下來發(fā)現(xiàn)大部分的代碼基本都是類似的歌憨。
本篇開始一步一步封裝出一個通用的Adapter。
本篇是對單類型Item的Adapter的封裝
RecyclerView 的Adapter分析
要想分裝出一個通用的Adapter午阵,首先需要對Adapter中的代碼進行分析躺孝,再試著進行抽取
public class GankAdapter extends RecyclerView.Adapter<GankAdapter.MyViewHolder> {
private Context mContext;
private List<GankBean> mGankBeanList;
private LayoutInflater mLayoutInflater;
public GankAdapter(Context context, List<GankBean> gankBeanList) {
this.mContext = context;
this.mGankBeanList = gankBeanList;
mLayoutInflater = LayoutInflater.from(mContext);
}
public void addData(int position, List<GankBean> data) {
if (data != null && data.size() > 0) {
mGankBeanList.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List<GankBean> list) {
if (list != null) {
mGankBeanList = list;
notifyDataSetChanged();
}
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View contentView = mLayoutInflater.inflate(R.layout.item_list, parent, false);
return new MyViewHolder(contentView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
GankBean gankBean = mGankBeanList.get(position);
holder.tvTitle.setText(gankBean.getDesc());
holder.tvUrl.setText(gankBean.getUrl());
}
@Override
public int getItemCount() {
return mGankBeanList == null ? 0 : mGankBeanList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView tvTitle;
public TextView tvUrl;
public MyViewHolder(View itemView) {
super(itemView);
tvTitle = (TextView) itemView.findViewById(R.id.tv_title);
tvUrl = (TextView) itemView.findViewById(R.id.tv_url);
}
}
}
繼承與RecyclerView.Adapter一般需要重寫以下方法:
1享扔、構(gòu)造函數(shù)底桂,傳入數(shù)據(jù)和Context對象
2、getItemCount() 返回Item 的個數(shù)
3惧眠、getItemType()返回Item 的類型(本篇只做單類型的封裝下篇在講)
4籽懦、onCreateViewHolder()創(chuàng)建ViewHolder
5、onBindViewHolder()綁定ViewHolder的數(shù)據(jù)
其中有一個很關鍵的角色 就是ViewHolder氛魁,ViewHolder 的作用是什么呢暮顺?
ViewHolder的主要的作用是存儲對應的convertView中需要操作的子View厅篓,避免每次findViewById,從而提升運行的效率捶码;
ViewHolder 的封裝
經(jīng)過以上分析羽氮,ViewHolder 的作用是存儲需要操作的子View,對于不同的ViewHolder我們不知道需要存儲的View 是什么類型,有多少數(shù)量
所以只能通過集合的方式來存儲子View,存儲的容器采用SparseArray容器惫恼,相當于一個Hash<Intager,Object>,可以通過int型key來取出value
而關于子View,我們知道的恰恰是id档押,為int型,剛好符合了需求祈纯。
public class ViewHolder extends RecyclerView.ViewHolder
{
private SparseArray<View> mViews; //存儲子View
private View mConvertView;
private Context mContext;
//構(gòu)造函數(shù) 初始化容器和contvertView
public ViewHolder(Context context, View itemView){
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<>();
}
//根據(jù)ItemView 創(chuàng)建ViewHolder
public static ViewHolder createViewHolder(Context context, View itemView {
return new ViewHolder(context, itemView);
}
//根據(jù)layoutId 創(chuàng)建ViewHolder
public static ViewHolder createViewHolder(Context context,
ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
return new ViewHolder(context, itemView);
}
/**
* 通過viewId獲取控件
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId)
{
//查找集合中是否存在子View
View view = mViews.get(viewId);
//不存在令宿,執(zhí)行findViewById操作
if (view == null)
{
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView()
{
return mConvertView;
}
/****以下為輔助方法,可以快速的使用*****/
public ViewHolder setText(int viewId, String text)
{
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setImageResource(int viewId, int resId)
{
ImageView view = getView(viewId);
view.setImageResource(resId);
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bitmap)
{
ImageView view = getView(viewId);
view.setImageBitmap(bitmap);
return this;
}
public ViewHolder setImageDrawable(int viewId, Drawable drawable)
{
ImageView view = getView(viewId);
view.setImageDrawable(drawable);
return this;
}
/**
* 為ImageView設置網(wǎng)絡url 或者 文件path 圖片
* @param viewId View的id
* @param path url或者 文件path
* @param errorId 錯誤圖片id
* @param placeId 占位圖片id
* @return
*/
public ViewHolder setImageBUrlOrFile(int viewId,String path,int errorId,int placeId){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView,errorId,placeId);
return this;
}
public ViewHolder setImageNoPlace(int viewId,String path){
ImageView imageView = getView(viewId);
MyImageLoader.getInstance().displayImage(mContext,path,imageView);
return this;
}
public ViewHolder setBackgroundColor(int viewId, int color)
{
View view = getView(viewId);
view.setBackgroundColor(color);
return this;
}
public ViewHolder setBackgroundRes(int viewId, int backgroundRes)
{
View view = getView(viewId);
view.setBackgroundResource(backgroundRes);
return this;
}
public ViewHolder setTextColor(int viewId, int textColor)
{
TextView view = getView(viewId);
view.setTextColor(textColor);
return this;
}
public ViewHolder setTextColorRes(int viewId, int textColorRes)
{
TextView view = getView(viewId);
view.setTextColor(mContext.getResources().getColor(textColorRes));
return this;
}
public ViewHolder setVisible(int viewId, boolean visible)
{
View view = getView(viewId);
view.setVisibility(visible ? View.VISIBLE : View.GONE);
return this;
}
public ViewHolder setTag(int viewId, Object tag)
{
View view = getView(viewId);
view.setTag(tag);
return this;
}
public ViewHolder setTag(int viewId, int key, Object tag)
{
View view = getView(viewId);
view.setTag(key, tag);
return this;
}
public ViewHolder setChecked(int viewId, boolean checked)
{
Checkable view = (Checkable) getView(viewId);
view.setChecked(checked);
return this;
}
/****關于點擊事件和長按事件*****/
public ViewHolder setOnClickListener(int viewId,
View.OnClickListener listener)
{
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
public ViewHolder setOnLongClickListener(int viewId,
View.OnLongClickListener listener)
{
View view = getView(viewId);
view.setOnLongClickListener(listener);
return this;
}
}
通過createViewHolder方法來實例化ViewHolder 對象跳座,實現(xiàn)了Context 拱礁、ConvertView唠摹、SparseArray的初始化
在需要操作子View 的時候减俏,通過getView 方法來實例化子View對象
在ViewHolder中還封裝了一些常用控件的操作方法倍试,注意到 很多方法返回的是ViewHolder對象耿芹,使得代碼能夠鏈式調(diào)用峦嗤,類似于Builder模式的代碼
使用起來如下:
holder.setText(R.id.tv_title,"Title")
.setText(R.id.tv_content,"Content");
?
Adapter 的封裝
有了ViewHolder 的封裝嬉荆,回顧需要重寫的幾個方法冕碟,接下來對Adapter進行封裝
public abstract class SingleAdapter<T> extends RecyclerView.Adapter<ViewHolder>{
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
public SingleAdapter(Context context, int layoutId, List<T> datas){
mContext = context;
mLayoutId = layoutId;
mDatas = datas;
}
@Override
public int getItemCount(){
return mDatas==null?0:mDatas.size();
}
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType){
//創(chuàng)建ViewHolder對象
return ViewHolder.get(mContext, parent, mLayoutId);;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position){
//綁定數(shù)據(jù)交由子類來完成
convert(holder, mDatas.get(position));
}
public abstract void convert(ViewHolder holder, T t);
//以下是一些公共方法的封裝
public void clearData() {
mDatas.clear();
notifyItemRangeRemoved(0, mDatas.size());
}
public void removeData(int position) {
mDatas.remove(position);
notifyItemRemoved(position);
if (position != mDatas.size())
notifyItemRangeChanged(position,mDatas.size() - position);
}
public void addData(List<T> list) {
addData(0, list);
}
public void addData(int position, List<T> data) {
if (data != null && data.size() > 0) {
mDatas.addAll(position, data);
notifyItemRangeInserted(position, data.size());
}
}
public void setData(List<T> data) {
if (list != null) {
mDatas = data;
notifyDataSetChanged();
}
}
}
通過構(gòu)造函數(shù) 初始化Conetext ,Data ,layoutId屬性
getItemCount()方法返回Data 的數(shù)量拦惋,并進行了判空
onCreateViewHolder()方法通過ViewHolder 的靜態(tài)方法初始化ViewHolder
onBindViewHolder()綁定數(shù)據(jù)是子類來完成的,所以抽象出convert(ViewHolder holder, T t)方法傳遞參數(shù)
子類通過該方法完成數(shù)據(jù)的綁定
使用
SingleAdapter mSingleAdapter = new SingleAdapter<String>(this,R.layout.item_single){
? @Override
? public void convert(ViewHolder holder, String s){
? holder.setText(R.id.tv_title,s);
}
}