看了翔哥的自定义万能的adapter,自己也做下笔记,分析一下高手的思维方式,让我们一起进入变态程序员的内心世界。
分析万能的adapter之前,我们先分析一下普通的adapter
public class ReportSpinnerAdapter extends BaseAdapter {
private Context context;
private List<String> str;
public ReportSpinnerAdapter(Context context, List<String> str, int textWidth) {
this.context = context;
this.str = str;
this.textWidth = textWidth;
}
@Override
public int getCount() {
return str.size();
}
@Override
public Object getItem(int position) {
return str.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Viewholder hold;
if (convertView==null){
convertView = LayoutInflater.from(context).inflate(R.layout.spinner_layout,parent,false);
hold = new ViewHold();
hold.textView = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(hold);
}else{
hold = (ViewHold) convertView.getTag();
}
hold.textView.setText(str.get(position));
return convertView;
}
static class ViewHolder{
TextView textView;
}
普通的adapter大致就分为两点,getView和ViewHolder,一个类就解决问题,涉及到控件的复用,就用到了ViewHolder,主要逻辑还是都在getView中,,如果对这个不理解,可以先百度ViewHolder,getView中的逻辑就是如果convertView第一次被使用的时候,初始化绑定布局,并设置标记,否则,就从convertView取出viewHolder,如果要更改布局中控件的参数就从viewHolder中取出控件设置参数,因为这个控件在convertView初始化的时候就一起初始化了,所以直接设置参数就ok了。这是一个标准程序员的通常做法,但是,这样如果我们程序中要经常使用adapter,每个Listview或GridView都要去弄一个adapter,就白白浪费了大量的时间,作为一个变态程序员,翔哥怎么能忍,所以就出了这么一篇博客翔哥博客,让我们也能从中获利,收获了新技术也收获了新的思维方式。
总结自定义万能的adapter,或者叫做可重用的adapter,分拆开来看,就是三点:
1.封装一个特殊的ViewHolder,可以传入参数就直接得到viewHolder,把这步干掉:
if (convertView==null){
convertView = LayoutInflater.from(context).inflate(R.layout.spinner_layout,parent,false);
hold = new ViewHold();
hold.textView = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(hold);
}else{
hold = (ViewHold) convertView.getTag();
}
想要干掉这一步,就要想到,传入什么样的参数,才能直接得到 viewHolder 呢,就是后面这几个, 1. LayoutInflater.from(context) 2. R.layout.spinner_layout 3. parent 4. convertView
知道了需要的条件就好说了,直接写一个
public class ViewHolder {
private SparseArray<View> views;
private View convertView;
public ViewHolder(ViewGroup parent,LayoutInflater inflater,int layoutId) {
this.views = new SparseArray<View>();
this.convertView = inflater.inflate(layoutId,parent,false);
this.convertView.setTag(this);
}
/**
* 得到viewHolder
* @param parent
* @param convertView
* @param inflater
* @param layoutId
* @return
*/
public static ViewHolder getViewHolder(ViewGroup parent,View convertView,LayoutInflater inflater,int layoutId){
if (convertView==null){
return new ViewHolder(parent,inflater,layoutId);
}
return (ViewHolder) convertView.getTag();
}
这个 SparseArray<View> views实际上就是一个键值对,用来保存listview或者gridview上每一行或者每一列上的所有控件的,效率上要高于hashmap,用法就是
/**
* 根据Id得到view
* @param viewId
* @param <T>
* @return
*/
public <T extends View>T getView(int viewId){
View view = views.get(viewId);
if (view==null){
view = convertView.findViewById(viewId);
views.put(viewId,view);
}
return (T) view;
}
这样,一个ViewHolder就搞定了。
2.编写一个通用的adapter,通用adapter,基本无法实现,那怎么办呢,做一个半通用的adapter,既然要半通用,就是大体相同,少数差异,那用抽象类继承的方式就最好了
public abstract class CommonAdapter<T> extends BaseAdapter {
private List<T> list;
private LayoutInflater inflater;
private int layoutId;
public CommonAdapter(List<T> list, Context context,int layoutId) {
this.list = list;
this.inflater = LayoutInflater.from(context);
this.layoutId = layoutId;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = ViewHolder.getViewHolder(parent,convertView,inflater,layoutId);
convert(viewHolder,list.get(position));
return viewHolder.getConvertView();
}
public abstract void convert(ViewHolder viewHolder,T t);
}
首先,我们绑定adapter的时候不确定数据源,就得用泛型类和泛型集合了,再就是传入布局id和context上下文,把需要给控件赋值的地方设置为抽象方法,给子类继承,这样封装好了之后,我们需要做的事情就是少之又少了,只需要重写convert方法和构造方法了
public class MyAdapter extends CommonAdapter<Bean> {
public MyAdapter(List<Bean> list, Context context, int layoutId) {
super(list, context, layoutId);
}
@Override
public void convert(ViewHolder viewHolder, Bean bean) {
((TextView)viewHolder.getView(R.id.text)).setText("这是测试数据1");
((TextView)viewHolder.getView(R.id.text)).setText("这是测试数据1"); }
}
这样,只需要在activity或者fragment中进行绑定MyAdapter,把数据和布局id和content传入就搞定了。只不过(viewHolder.getView(R.id.text)).setText这种写法很蛋疼,代码中写强转的话,还要把光标切回来,反正我个人是很不习惯的,那怎么办呢,可以这样,我们既然有了viewholder对象,我们就改造一下viewHolder对象,让公共代码多一点,我们以后重复写的地方就少一点。在ViewHolder中加入如下方法
/**
* 根据id得到TextView
* @return
*/
public TextView getTextView(int viewId){
return getView(viewId);
}
/**
* 根据id得到ImageView
* @return
*/
public ImageView getImageView(int viewId){
return getView(viewId);
}
/**
* 根据id得到Button
* @return
*/
public Button getButton(int viewId){
return getView(viewId);
}
/**
* 根据id得到RadioButton
* @return
*/
public RadioButton getRadioButton(int viewId){
return getView(viewId);
}
/**
* 根据id得到CheckBox
* @return
*/
public CheckBox getCheckBox(int viewId){
return getView(viewId);
}
/**
* 根据id得到ImageButton
* @return
*/
public ImageButton getImageButton(int viewId){
return getView(viewId);
}
/**
* 根据id得到ImageButton
* @return
*/
public EditText getEditText(int viewId){
return getView(viewId);
}
那么我们的Myadapter中的convert方法就可以这样写了
@Override
public void convert(ViewHolder viewHolder, Bean bean) {
viewHolder.getTextView(R.id.text).setText("这是测试数据1");
viewHolder.getTextView(R.id.text).setText("这是测试数据2");
}
这样书写起来就会流畅一点,比较跟手,好了,这基本就是翔哥万能的自定义adapter的主要内容,下面图解一下核心思想
最后,源码: 点击打开链接