原来的Adapter写法
还记得刚学习Android时,刚写Adapter很直白,利用一个ViewHolder的缓存和convertview的复用来实现,当时感觉那种方式蛮不错的,最近准备中软杯项目的时候感觉总是要重复写getView里的方法真的是很不方便,我觉得还是可以进一步抽象的,这里参考了鸿洋大神的博客,博客地址http://blog.csdn.net/lmj623565791/article/details/38902805
package com.tmall.adapter;
import java.util.List;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.tmall.R;
import com.tmall.bean.CenterItem;
import com.tmall.view.base.BaseApplication;
public class CenterListAdapter extends MyBaseAdapter<CenterItem> {
public CenterListAdapter(List<CenterItem> list) {
super(list);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = new ViewHolder();
if (convertView == null) {
convertView = View.inflate(BaseApplication.context,
R.layout.center_list_item, null);
holder = new ViewHolder();
holder.ivLeft = (ImageView) convertView
.findViewById(R.id.center_item_iv_left);
holder.tv = (TextView) convertView
.findViewById(R.id.center_item_tv);
holder.ivRight = (ImageView) convertView
.findViewById(R.id.center_item_iv_right);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
CenterItem item = list.get(position);
holder.ivLeft.setBackgroundResource(item.getIvLeftId());
holder.tv.setText(item.getText());
holder.ivRight.setBackgroundResource(item.getIvRightId());
return convertView;
}
class ViewHolder {
ImageView ivLeft;
TextView tv;
ImageView ivRight;
}
}
就是像上面的那样,每个adapter要写一个ViewHolder来缓存一个View,并且每次判断convertView是否为空决定是否复用。这样性能是没问题的,其实也不完全是,如果为了某些业务(比如之前我为了实现一个ScrollView中嵌套ListView来实现滑动效果时,由于设置高度自己测量,因而list里有多少数据就会加载多少数据而且缓存住,不会被回收,这样如果数据少还看不太出来,数据量一大就oom了),当然这个问题这里先不探讨,我们现在先讨论怎么对这段代码进行简化。
打造通用的ViewHolder
其实如果ViewHolder能通用的话,getView里的代码都可以抽出去了,之前单独思考的时候就是没想到这个ViewHolder怎么抽出去,参考了鸿洋大神的博客之后,毛瑟顿开,后面的也就一股脑的写了。Android里有个SparseArray的类,只能存储int类型的数据,但是性能要比HashMap好很多。刚好在Android里布局文件id等都是以int的格式,方便存储。
public class ListViewHolder {
private SparseArray<View> myViews;//view的缓存数组,性能比hashmap要好
private View mContextView;//缓存的父类view
private ListViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
this.myViews = new SparseArray<>();
this.mContextView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mContextView.setTag(this);
}
public static ListViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new ListViewHolder(context, parent, layoutId, position);
}
return (ListViewHolder) convertView.getTag();
}
}
这样adapter就可以调用getHolder方法成功的拿到通用的ViewHolder了,同时给它添加通用的getView方法让外部可以直接根据id拿到指定类型的view,由于类型不确定这里用泛型表示。
public class ListViewHolder {
private SparseArray<View> myViews;//view的缓存数组,性能比hashmap要好
private View mContextView;//缓存的父类view
private ListViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
this.myViews = new SparseArray<>();
this.mContextView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mContextView.setTag(this);
}
public static ListViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new ListViewHolder(context, parent, layoutId, position);
}
return (ListViewHolder) convertView.getTag();
}
/**
* 通过控件的id来获取view
*
* @param viewId 控件的id
* @param <T> view的类型
* @return view
*/
public <T extends View> T getView(int viewId) {
//先检测缓存view里是否存在该view,不存在则find出来添加进去
View view = myViews.get(viewId);
if (view == null) {
view = mContextView.findViewById(viewId);
myViews.put(viewId, view);
}
return (T) view;
}
}
然后adapter里就好写啦,直接调用getHolder方法拿到返回的ViewHolder,就不用再adapter里写啦,剩下的adapter的通用代码就可以抽出来了,依赖子类实现的就给抽象方法交给子类。那么父类adapter的写法如下:
package com.we.piccategory.adapter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import com.we.piccategory.ui.base.BaseApp;
import java.util.List;
/**
* Created with Android Studio
* User: 潘浩
* School 南华大学
* Date: 2017/5/20
* Time: 13:30
* Description:
*/
public abstract class AutoAdapter<T> extends BaseAdapter {
protected List<T> list;
public AutoAdapter(List<T> list) {
this.list = list;
}
@Override
public int getCount() {
return list == null ? 0 : list.size();
}
@Override
public Object getItem(int position) {
return list.get(position) == null ? null : list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ListViewHolder holder = ListViewHolder.getHolder(BaseApp.context, convertView, parent, getLayoutId(), position);
convert(holder, (T) getItem(position));
return holder.getConvertView();
}
protected abstract void convert(ListViewHolder holder, T item);
public abstract int getLayoutId();
}
因为getview方法里对convertview的服用判断做了实现,这里就不需要再那样写了,而剩下的不同的组件设置不同的内容全都交给子类实现就好了,这样子类只要实现这个父类就可以了。当然这里是可以给一些通用的方法实现,比如对textView操作比较多,就写一个供外部调用的功能出来,修正完毕的通用VIewHolder如下:
package com.we.piccategory.adapter;
import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import static com.we.piccategory.ui.base.BaseApp.context;
/**
* Created with Android Studio
* User: 潘浩
* School 南华大学
* Date: 2017/5/20
* Time: 13:35
* Description:
*/
public class ListViewHolder {
private SparseArray<View> myViews;//view的缓存数组,性能比hashmap要好
private View mContextView;//缓存的父类view
private ListViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
this.myViews = new SparseArray<>();
this.mContextView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mContextView.setTag(this);
}
public static ListViewHolder getHolder(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new ListViewHolder(context, parent, layoutId, position);
}
return (ListViewHolder) convertView.getTag();
}
/**
* 通过控件的id来获取view
*
* @param viewId 控件的id
* @param <T> view的类型
* @return view
*/
public <T extends View> T getView(int viewId) {
//先检测缓存view里是否存在该view,不存在则find出来添加进去
View view = myViews.get(viewId);
if (view == null) {
view = mContextView.findViewById(viewId);
myViews.put(viewId, view);
}
return (T) view;
}
public ListViewHolder setText(int viewId, String text) {
TextView view = getView(viewId);
view.setText(text);
return this;
}
public ListViewHolder setImageRes(int viewId, int resId) {
ImageView iv = getView(viewId);
Glide.with(context).load(resId).fitCenter().centerCrop().into(iv);
return this;
}
public ListViewHolder setImgUrl(int viewId, String url) {
ImageView iv = getView(viewId);
Glide.with(context).load(url).fitCenter().centerCrop().into(iv);
return this;
}
public View getConvertView() {
return mContextView;
}
}
然后子类里就非常代码量就非常好写啦,甚至可以写成内部类都可以,当然我一般是不会这么写的,因为我觉得能跟activity解耦的东西就要解耦,除了一些监听器之类的写在activity内部类,别的尽量还是抽出去保持adapter代码的纯洁性。实现的adapter代码如下:
package com.we.piccategory.adapter;
import com.we.piccategory.R;
import com.we.piccategory.bean.Pic;
import java.util.List;
/**
* Created with Android Studio
* User: 潘浩
* School 南华大学
* Date: 2017/5/22
* Time: 14:42
* Description:
*/
public class TestAdapter extends BaseRecycleAdapter<Pic> {
public TestAdapter(List<Pic> list, int layoutId) {
super(list, layoutId);
}
public void setList(List<Pic> list) {
this.list = list;
this.notifyDataSetChanged();
}
@Override
protected void convert(RecycleViewHolder holder, Pic pic) {
holder.setText(R.id.home_tv, pic.getName());
holder.setImageRes(R.id.home_iv, pic.getResId());
holder.buildHolder(R.id.home_iv);
}
}
怎么样,是不是比原来那种写法好了很多呢?至少我是更情愿这样的写法,尤其是在做项目中感受尤为真切,这次中软杯客户端android基本我一个人纯手撸,说实话真的是相同的代码写到要吐,所以说还是不要重复造轮子的好。有人说大神都是写了几十万行代码堆出来的,这个怎么说呢,这种大神的几十万行代码,肯定是质量越来越高的,而不是无用代码堆出来的,所以说还是要掌握方式和方法才对。这学期算是真的蛮忙的,连博客都荒废了,不知道为啥自己之前不太喜欢写博客,但最近感觉不写博客会忘记一些东西,之前可能觉得天天自学想要学的更多更快,但其实学过的知识要是不巩固不复习的话,很容易忘记的,虽然有时候翻翻代码又会找出来,但是很难在原来的基础上有提升,不管再忙还是还是坚持吧。