回顾传统的Listview编写
1.Activity_main 的编写
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/id_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
2.每个Item布局文件的编写
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/id_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:singleLine="true"
android:text="Android新技能"
android:textColor="#444"/>
<TextView
android:id="@+id/id_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/id_title"
android:layout_marginTop="10dp"
android:minLines="1"
android:maxLines="2"
android:text="Android打造万能的Listview和Gridview适配器"
android:textColor="#898989"
android:textSize="16sp"/>
<TextView
android:id="@+id/id_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/id_desc"
android:layout_marginTop="10dp"
android:minLines="1"
android:maxLines="2"
android:text="2014-12-12"
android:textColor="#898989"
android:textSize="12sp"/>
<TextView
android:id="@+id/id_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/id_desc"
android:layout_marginTop="10dp"
android:drawableLeft="@drawable/icon_photo"
android:drawablePadding="5dp"
android:padding="3dp"
android:layout_alignParentRight="true"
android:background="#2CEA6C"
android:text="10086"
android:textColor="#fff"
android:textSize="12sp"/>
</RelativeLayout>
3.listview的适配器编写
public class MyAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;
private List<Bean> mDatas;
public MyAdapter(List<Bean> mDatas, Context mContext) {
mInflater = LayoutInflater.from(mContext);
this.mDatas = mDatas;
this.mContext = mContext;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mDatas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater
.inflate(R.layout.item_layout, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.id_title);
holder.mTime = (TextView) convertView.findViewById(R.id.id_time);
holder.mDec = (TextView) convertView.findViewById(R.id.id_desc);
holder.mPhone = (TextView) convertView.findViewById(R.id.id_phone);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Bean bean = mDatas.get(position);
holder.mTitle.setText(bean.getTitle());
holder.mTime.setText(bean.getTime());
holder.mDec.setText(bean.getDesc());
holder.mPhone.setText(bean.getPhone());
return convertView;
}
public class ViewHolder {
TextView mTitle;
TextView mTime;
TextView mDec;
TextView mPhone;
}
}
4. MainActivity的编写
public class MainActivity extends Activity {
private ListView mListView;
private MyAdapter mAdapter;
private List<Bean> mDates;;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDates();
initView();
}
private void initDates() {
// TODO Auto-generated method stub
mDates = new ArrayList<Bean>();
Bean bean = new Bean("南村群童欺我老无力", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("公然抱我入竹去", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("唇焦口燥呼不得", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("啪啪啪啪啪啪啪", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("归来倚床自叹息", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
mAdapter = new MyAdapter(this, mDates);
}
private void initView() {
// TODO Auto-generated method stub
mListView = (ListView) findViewById(R.id.id_listview);
mListView.setAdapter(mAdapter);
}
}
效果图
如果多个Listview都重用一个适配器,那么我们应该怎么去修改我们的这个万能适配器呢?
思考:让我们回到代码行最多的Myadapter中。发现其中的方法,比如getCount(),getItem(int position),getItemId(int position)这些都是类似的,不需要我们再去实现它,需要我们去重写的只是getView方法而已。
所以我们可以写一个通用的adpter,在这个通用的adpter里面实现,构造函数,getCount(),getItem(int position),getItemId(int position)这些方法,然后我们把getView这个方法抽象化,让用户去自己去实现它,然后我们的Myadpter去继承我们这个通用adpter,只需要去重写一下getView这一个方法就可以了
注意我们这个通用的adpter,因为要去适应不同的Bean类,所以要使用泛型
public abstract class CommonAdapter<T> extends BaseAdapter {
private List<T> mDatas;
private Context mContext;
private LayoutInflater mInflater;
public CommonAdapter(Context mContext,List<T> mDatas) {
this.mDatas = mDatas;
this.mContext = mContext;
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mDatas.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public abstract View getView(int position, View convertView, ViewGroup parent);
}
好的,我们的通用的Adapter已经编写完成。我们现在让Myadpter不要再继承BaseAdapter,而是去继承CommonAdapter
public class MyAdapter extends CommonAdapter<Bean> {
private Context mContext;
private LayoutInflater mInflater;
private List<Bean> mDatas;
public MyAdapter(Context mContext, List<Bean> mDatas) {
super(mContext, mDatas);
mInflater = LayoutInflater.from(mContext);
this.mDatas = mDatas;
this.mContext = mContext;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater
.inflate(R.layout.item_layout, parent, false);
holder = new ViewHolder();
holder.mTitle = (TextView) convertView.findViewById(R.id.id_title);
holder.mTime = (TextView) convertView.findViewById(R.id.id_time);
holder.mDec = (TextView) convertView.findViewById(R.id.id_desc);
holder.mPhone = (TextView) convertView.findViewById(R.id.id_phone);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Bean bean = mDatas.get(position);
holder.mTitle.setText(bean.getTitle());
holder.mTime.setText(bean.getTime());
holder.mDec.setText(bean.getDesc());
holder.mPhone.setText(bean.getPhone());
return convertView;
}
public class ViewHolder {
TextView mTitle;
TextView mTime;
TextView mDec;
TextView mPhone;
}
}
好的,一个万能adapter的雏形已经写好了,继承万能adpter之后,我们的Myadapter只需要重写一下构造函数和getview方法就可以了。
可是这并没有什么卵用,依然不够面向对象
进阶方案----继续重铸通用Adapter
我们接下来去优化尝试去优化ViewHolder。我们分析一下ViewHolder的作用:避免重复的findviewbyid,对于已经生成的converview,直接从与之对应的ViewHodler(getTag)中拿到convertView布局中的控件,省去了findViewByid的时间
也就是说,每个convertview都会去绑定一个viewholder对象,存储那些布局中的控件的引用。当converview复用的时候,我们只需要去调用converview的gettag方法就可以了。
多个listview的adapter,他们的settag和gettag其实都是有共同性的
这样,我们就可以尝试去定义一个通用的viewholder对象
思考一下,既然我们这个viewholder是通用的,那么我们就不可能再去含有各个控件的变量,因为每个listview他的item布局都是不同的,那么最好的方式是什么?
我们提供一个容器,去存储我们的每一个控件的引用,而且我们也可以从容器中根据特定的标识,取出我们的控件,这里我们可以使用java的键值对的概念,使用Map来存储多个键值对,键为控件的id,值为控件的引用,
Map<int,view>
定义一个getView(int id)方法 ;通过传入id来取我们的控件
编写完ViewHolder之后,这样我们的Myadapter的getView方法就可以写成这样
public View getView(int positon, View convertView ,ViewGroup parent)
{
ViewHolder holder = TextView tv = holder.getView(ViewId);
Textview title_TextView = holder.getView(R.id.id_title);
TextView desc_TextView = holder.getView(R.id.id_desc);
TextView time_TextView = holder.getView(R.id.id_time);
TextView phone_TextView = holder.getView(R.id.id_phone);
Bean bean = mDatas.get(position);
title_TextView.setText(bean.getTitle());
desc_TextView.setText(bean.getTitle());
time_TextView.setText(bean.getTime());
phone_TextView.setText(bean.getPhone());
retrue converView;
}
好,我们回到的我们的ViewHolder的代码编写,其实Map的效率并不高,我们使用android为我们专门提供的sparseArray这个容器来存储键值对
public class ViewHolder {
private final SparseArray<View> mViews;
private View mConvertView;
private int mPosition;
public int getmPosition() {
return mPosition;
}
public ViewHolder(Context context,ViewGroup parent, int layoutId, int position) {
this.mPosition = position;
this.mViews = new SparseArray<View>();
this.mConvertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
mConvertView.setTag(this);
}
public static ViewHolder get(Context context,View convertView,
ViewGroup parent,int layoutId,int position)
{
if (convertView == null) {
return new ViewHolder(context, parent, layoutId, position);
}else {
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.mPosition = position;
return holder;
}
}
/**
* 通过Viewid获取控件
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId)
{
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T)view;
}
public View getConvertView() {
return mConvertView;
}
}
好的,我们再次对MyAdapter进行修改
public class MyAdapter extends CommonAdapter<Bean> {
private Context mContext;
private LayoutInflater mInflater;
private List<Bean> mDatas;
public MyAdapter(Context mContext, List<Bean> mDatas) {
super(mContext, mDatas,R.layout.item_layout);
mInflater = LayoutInflater.from(mContext);
this.mDatas = mDatas;
this.mContext = mContext;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = new ViewHolder(mContext, parent, R.layout.item_layout, position);
//接下来就是拿到我们各个的Textview
TextviewTextView title_TextView = holder.getView(R.id.id_title);
TextView desc_TextView = holder.getView(R.id.id_desc);
TextView time_TextView = holder.getView(R.id.id_time);
TextView phone_TextView = holder.getView(R.id.id_phone);
//接下来就可以对每个Textview的详细操作,比如说点击事件什么的,这里我们就不再详细写了
Bean bean = mDatas.get(position);
title_TextView.setText(bean.getTitle());
desc_TextView.setText(bean.getTitle());
time_TextView.setText(bean.getTime());
phone_TextView.setText(bean.getPhone());
}
}
中间的一段其实可以更加简洁,我们使用java的链式编程,进一步去精简中间的代码
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = new ViewHolder(mContext, parent, R.layout.item_layout, position);
//接下来就是拿到我们各个的Textview
Bean bean = mDatas.get(position);
((TextView)holder.getView(R.id.id_title)).setText(bean.getTitle());
((TextView)holder.getView(R.id.id_desc)).setText(bean.getDesc());
((TextView)holder.getView(R.id.id_time)).setText(bean.getTime());
((TextView)holder.getView(R.id.id_phone)).setText(bean.getPhone());
//接下来就可以对每个Textview的详细操作,比如说点击事件什么的,这里我们就不再详细写了
return holder.getConvertView();
}
这里我们只用了4行代码就写完了
继续进阶-----通用ViewHolder整合到通用的Adapter中去
我们观察一下Myadpter的getView方法
<pre name="code" class="java">ViewHolder holder = new ViewHolder(mContext, parent, R.layout.item_layout, position);
......
return holder.getConvertView();
第一行的与最后一行其实在多个listadapter中都是一样的。都是new一个holder出来,并且返回一个convertView
不一样的是中间处理的过程而已,因为每个listview他的item布局可能都不同
所以我们把第一行的代码和最后一行的代码交给通用的adapter处理就可以了
中间处理的过程,我们把它写成一个抽象的方法,让Myadapter去自己去实现它就可以了
上通用adapter的代码
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = new ViewHolder(mContext, parent, layoutId, position);
convert(viewHolder, getItem(position));
return viewHolder.getConvertView();
}
public abstract void convert(ViewHolder holder, T bean);
写好了。接下来我们就去进一步精简我们的Myadapter
public class MyAdapter extends CommonAdapter<Bean> {
public MyAdapter(Context mContext, List<Bean> mDatas) {
super(mContext, mDatas,R.layout.item_layout);
}
@Override
public void convert(ViewHolder holder, Bean bean) {
// TODO Auto-generated method stub
((TextView)holder.getView(R.id.id_title)).setText(bean.getTitle());
((TextView)holder.getView(R.id.id_desc)).setText(bean.getDesc());
((TextView)holder.getView(R.id.id_time)).setText(bean.getTime());
((TextView)holder.getView(R.id.id_phone)).setText(bean.getPhone());
}
看到了吗,这里我们的Myadapter只需要写一个convert函数就够了,别的全部都不用写
还能再进一步精简吗?
最终铸造--------匿名内部类
这里我们的Myadapter只需要写一个convert函数就够了,别的全部都不用写
代码简化到这样,我已经不需要单独写一个Adapter了,直接MainActivity匿名内部类走起。我们使用匿名内部类,new一个commonadapter,然后复写里面的方法就可以了
上代码
public class MainActivity extends Activity {
private ListView mListView;
private MyAdapter mAdapter;
private List<Bean> mDates;;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDates();
initView();
}
private void initDates() {
// TODO Auto-generated method stub
mDates = new ArrayList<Bean>();
Bean bean = new Bean("南村群童欺我老无力", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("公然抱我入竹去", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("唇焦口燥呼不得", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("啪啪啪啪啪啪啪", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
bean = new Bean("归来倚窗自叹息", "茅屋为秋风所破歌", "呵呵", "杜甫");
mDates.add(bean);
mAdapter = new MyAdapter(this, mDates);
}
private void initView() {
// TODO Auto-generated method stub
mListView = (ListView) findViewById(R.id.id_listview);
mListView.setAdapter(new CommonAdapter<Bean>(this,mDates,R.layout.item_layout) {
@Override
public void convert(ViewHolder holder, Bean bean) {
// TODO Auto-generated method stub
((TextView)holder.getView(R.id.id_title)).setText(bean.getTitle());
((TextView)holder.getView(R.id.id_desc)).setText(bean.getDesc());
((TextView)holder.getView(R.id.id_time)).setText(bean.getTime());
((TextView)holder.getView(R.id.id_phone)).setText(bean.getPhone());
}
});
}
}
搞定,最终我们把代码精简到了只需要5行左右,就完成了adapter的创建了。
而我们只需要这2个文件就可以了
然后多个listview或者gridview去复用他就可以了。而且这2个类,可以用在往后开发的任何项目中,加快我们的开发进程。
代码简洁之道,不过如此。
谢谢大家。