您的位置:首页 > 编程语言

代码重构-------ListView与GridView的适配器重用篇

2015-06-03 22:24 525 查看
回顾传统的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个类,可以用在往后开发的任何项目中,加快我们的开发进程。

代码简洁之道,不过如此。

谢谢大家。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: