代码重构-------ListView与GridView的适配器重用篇
2015-06-03 22:24
525 查看
回顾传统的Listview编写
1.Activity_main 的编写
2.每个Item布局文件的编写
3.listview的适配器编写
4. MainActivity的编写
效果图
初级优化方案----打造
如果多个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类,所以要使用泛型
好的,我们的通用的Adapter已经编写完成。我们现在让Myadpter不要再继承BaseAdapter,而是去继承CommonAdapter
好的,一个万能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方法就可以写成这样
好,我们回到的我们的ViewHolder的代码编写,其实Map的效率并不高,我们使用android为我们专门提供的sparseArray这个容器来存储键值对
好的,我们再次对MyAdapter进行修改
中间的一段其实可以更加简洁,我们使用java的链式编程,进一步去精简中间的代码
这里我们只用了4行代码就写完了
继续进阶-----通用ViewHolder整合到通用的Adapter中去
我们观察一下Myadpter的getView方法
第一行的与最后一行其实在多个listadapter中都是一样的。都是new一个holder出来,并且返回一个convertView
不一样的是中间处理的过程而已,因为每个listview他的item布局可能都不同
所以我们把第一行的代码和最后一行的代码交给通用的adapter处理就可以了
中间处理的过程,我们把它写成一个抽象的方法,让Myadapter去自己去实现它就可以了
上通用adapter的代码
写好了。接下来我们就去进一步精简我们的Myadapter
看到了吗,这里我们的Myadapter只需要写一个convert函数就够了,别的全部都不用写
还能再进一步精简吗?
这里我们的Myadapter只需要写一个convert函数就够了,别的全部都不用写
代码简化到这样,我已经不需要单独写一个Adapter了,直接MainActivity匿名内部类走起。我们使用匿名内部类,new一个commonadapter,然后复写里面的方法就可以了
上代码
搞定,最终我们把代码精简到了只需要5行左右,就完成了adapter的创建了。
而我们只需要这2个文件就可以了
然后多个listview或者gridview去复用他就可以了。而且这2个类,可以用在往后开发的任何项目中,加快我们的开发进程。
代码简洁之道,不过如此。
谢谢大家。
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个类,可以用在往后开发的任何项目中,加快我们的开发进程。
代码简洁之道,不过如此。
谢谢大家。
相关文章推荐
- 程序不稳定是因为C++基础不扎实
- asp中Scripting.Dictionary字典对象使用示例
- C/C++文件操作1——FILE结构体
- Windows下python环境安装scrapy
- OC基础学习——Block语法的学习和Category与Extension的初步认识
- struts2实现自定义数据类型转换器
- Eclipse配置PHP及自动提示功能
- (C++)关于i++和i++的左值、右值问题
- 用Python开始机器学习(2:决策树分类算法)
- 关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
- javaI/O操作之文件的切割合并
- JAVA基础知识之编译、运行、打包
- Java DateUtils 的实用
- [Servlet]什么是Servlet
- Matlab之统计函数
- Java 基础入门随笔(3) JavaSE版——逻辑运算符、位运算符
- PHP设计模式——观察者模式
- java第三次实验报告
- 关于在freemarker模板中遍历数据模型List<JavaBean>的经验
- 判断字符串中是否包含中文