前言

在Android开发中ListView是最为经常使用的控件之中的一个,基本每一个应用都会涉及到它,要使用ListView列表展示,就不可避免地涉及到另外一个东西——Adapter,我们都知道,Adapter是连接数据和列表界面的一个桥梁,一般项目中一个listview就会有一个Adapter与之相应。然后就是一堆方法的重写,包含getCount,getItem,getView等等。遇到自己定义布局时还需重写getView方法,重写getView的时候逻辑不复杂还好。遇到代码逻辑复杂的时候adapter简直臃肿,而且还须要写非常多次反复的代码,比方推断convertView是否为空,findViewById无数次停不下来

写了这么多。你是否想过,可否有一个公用的自己定义Adapter基类,将这些经常反复的代码和逻辑封装起来。方便我们调用,降低getView中的代码逻辑,下面就来一步步将其“包装”起来成为我们想要的效果。

先走一遍我们之前写ListView和Adapter的方式:

activity_main.xml:

<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/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"> </ListView> </RelativeLayout>

MainActivity:

public class MainActivity extends Activity {

	private ListView listview;

	private MyAdapter adapter;

	private List<String> data;

	@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview); getData(); adapter = new MyAdapter(this.getApplicationContext(), data); listview.setAdapter(adapter);
} public void getData(){
data = new ArrayList<String>();
for(int i=0; i<20; i++){
data.add("数据"+i);
}
}
}

自己定义适配器 MyAdapter:

public class MyAdapter extends BaseAdapter{

	private Context mContext;

	private List<String> list;

	public MyAdapter(Context context, List<String> list){
this.mContext = context;
this.list = list;
} @Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.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 viewholder = null;
if(convertView == null){
convertView = View.inflate(mContext, R.layout.item_listview, null);
viewholder = new ViewHolder();
viewholder.titleTv = (TextView)convertView.findViewById(R.id.titleTv);
convertView.setTag(viewholder);
}
else{
viewholder = (ViewHolder)convertView.getTag();
}
viewholder.titleTv.setText(list.get(position)); return convertView;
} public static class ViewHolder{
TextView titleTv;
} }

每一个列表项的布局文件 item_listview.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <TextView
android:id="@+id/titleTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:paddingLeft="15dp"
android:textSize="20sp"
android:textColor="#000"
/> </LinearLayout>

对于以上这部分代码不太明确能够參考我之前的文章ListView基础篇ListView优化篇,通过以上代码就能够完成一个简单的列表界面和数据展示:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

接下来我们就在这个基础上进行一步步封装:

能够看到。我们每次调用getView时候,都会新建一个ViewHolder,然后推断convertView是否为空。以及用

viewholder存储我们每一个列表项的子控件,再通过setTag和getTag来复用Viewholder,这一部分逻辑是每次getView

都会调用的,所以首先能够想到将ViewHolder对象的逻辑给封装起来,封装ViewHolder首先要考虑下面几点:

封装ViewHolder

1.首先这个封装来肯定要有一个ViewHolder的构造方法。另外,从上面能够看出每次getView都须要初始化convertView,那么我们能够将convertView的初始化搬到ViewHold的构造方法中来进行。既然convertView要在ViewHolder的构造方法中初始化。那么必然还须要inflate所须要的參数,以及每一个Item的下标。即context、layoutId、ViewGroup、position:

public class CommonViewHolder {

	public View mConvertView;

	public CommonViewHolder(Context context, int position, int layoutId, ViewGroup parent){
mConvertView = View.inflate(context, layoutId, null);
mConvertView.setTag(this);
}
}

2.注意到曾经的方式每次都须要推断convertView是否为null,是则new一个新的convertView和ViewHolder实例而且setTag,否则採用getTag重用之前的ViewHolder:

public static CommonViewHolder get(Context context, View convertView, int position, int layoutId, ViewGroup parent){
if(convertView == null){
return new CommonViewHolder(context, position, layoutId, parent);
}
else{
return (CommonViewHolder)convertView.getTag();
}
}

3.不同场景下列表项的元素是不确定的。数量和类型都不一致,既然是打造通用Adapter。那肯定要兼容多种情况,数量上我们能够想到使用Map来存储我们的子控件,类型上能够使用Java的泛型来构造。例如以下:

private HashMap<Integer, View> map = new HashMap<Integer, View>();

public <T extends View> T getView(int viewId){
View view = map.get(viewId);
//假设view为空,则findId找到。并放进map中
if(view == null){
view = mConvertView.findViewById(viewId);
map.put(viewId, view);
}
//假设view不会空,则直接返回
return (T)view;
}

4.当然,曾经我们getView方法最后返回的是一个convertView,所以还能够提供一个getConvertView的方法返回每一行相应的convertView:

public View getConvertView(){
return mConvertView;
}

至此,完整的通用ViewHolder已打造完成,完整代码例如以下:

public class CommonViewHolder {

	public HashMap<Integer, View> map;

	public View mConvertView;

	public CommonViewHolder(Context context, int position, int layoutId, ViewGroup parent){
map = new HashMap<Integer, View>();
mConvertView = View.inflate(context, layoutId, null);
mConvertView.setTag(this);
} public static CommonViewHolder get(Context context, View convertView, int position, int layoutId, ViewGroup parent){
if(convertView == null){
return new CommonViewHolder(context, position, layoutId, parent);
}
else{
return (CommonViewHolder)convertView.getTag();
}
} public <T extends View> T getView(int viewId){
View view = map.get(viewId);
if(view == null){
view = mConvertView.findViewById(viewId);
map.put(viewId, view);
}
return (T)view;
} public View getConvertView(){
return mConvertView;
}
}

现在,我们Adapter中的getView也就变成了这个样子:

public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent);
TextView titleTv = holder.getView(R.id.titleTv);
titleTv.setText(list.get(position));
return holder.getConvertView();
}

它的代码执行流程例如以下:
1.首先进入get方法获取一个ViewHolder实例,假设convertView为空,则进入到构造方法,new一个用来存放这一行的map集合,inflate一个新的View,而且给它setTag,假设convertView不为空。则直接通过getTag获得ViewHolder实例

2.接着调用holder.getView。传入控件ID。假设在该map中还未有过,则通过findViewById找到控件,并存放进该行的View集合中。假设已经存在,则能够进行View的复用,即直接map.get(viewId);

3.最后调用getConvertView,获得我们已经处理好的convertView实例

封装Adapter

上面我们对ViewHolder进行了封装。让adapter的getView方法大大简化。接下来開始封装我们的Adapter
封装Adapter成为公共类。我们须要注意下面问题:
平时我们写Adapter的时候数据类型总是不一样的,比方一会儿是一个User列表,一会儿是一个Car列表。传进来的数据源的类型通常是不一样的,那怎样做到无论传进来什么类型都能使用呢?是的没错,又是通过泛型来解决:

public abstract class CommonAdapter<T> extends BaseAdapter{

	public Context mContext;

	public List<T> list;

	public CommonAdapter(Context context, List<T> list){
this.mContext = context;
this.list = list;
} @Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.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
CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent);
TextView titleTv = holder.getView(R.id.titleTv);
titleTv.setText(list.get(position));
return holder.getConvertView();
} }

能够看到。我们将Adapter的数据类型取代为泛型的形式,而且我们定义的CommonAdapter为一个抽象类。这样做的原因是在基类先将getCount、getItem等方法给实现了,然后以后的具体Adapter类就仅仅须要继承该CommonAdapter,可是事实上开发中我们的getView是每次的操作都是各有所异的,不可能定死,而且细致看你会发现getView中的生成holder实例的代码和返回convertView实例的代码是千篇一律,差别仅仅在于传进来的item的布局id以及控件的生成不一样罢了,所以能够将这部分不一样的提取出来放在一个抽象方法中,留给子类去实现。将

MainActivity中:CommonAdapter改动例如以下:

public abstract class CommonAdapter<T> extends BaseAdapter{

	public Context mContext;

	public List<T> list;

	public int layoutId;

	public CommonAdapter(Context context, List<T> list, int layoutId){
this.mContext = context;
this.list = list;
this.layoutId = layoutId;
} @Override
public int getCount() {
// TODO Auto-generated method stub
return list.size();
} @Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return list.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
CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, layoutId, parent);
convert(holder, list.get(position), position);
return holder.getConvertView();
}
//这个就是留给具体Adapter实现的方法
public abstract void convert(CommonViewHolder viewHolder, T data, int position); }

至此,我们的通用Adapter打造完成,接下来我们来看看实践效果:

样例1
先定义一个用于纯文本显示的ListView的Adapter类:

public class TextListViewAdapter extends CommonAdapter<String>{

	public TextListViewAdapter(Context context, List<String> list) {
super(context, list);
// TODO Auto-generated constructor stub
} public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
CommonViewHolder holder = CommonViewHolder.get(mContext, convertView, position, R.layout.item_listview, parent);
TextView titleTv = holder.getView(R.id.titleTv);
titleTv.setText(list.get(position));
return holder.getConvertView();
} }

MainActivity中:

public class MainActivity extends Activity {

	private ListView listview;
private CommonAdapter adapter;
private List<String> data; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview);
initData();
adapter = new TextListViewAdapter(this.getApplicationContext(), data);
listview.setAdapter(adapter);
} public void initData(){
data = new ArrayList<String>();
for(int i=0; i<20; i++){
data.add("数据"+i);
}
}
}

能够看到,Adapter的代码比曾经省去了好多,执行后效果:

样例2

我们再试试多控件的情况,将item_listview布局文件更改例如以下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp"
android:orientation="horizontal"
> <ImageView
android:id="@+id/item_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_launcher"/> <LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical"> <TextView
android:id="@+id/titleTv"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="bottom"
android:paddingTop="10dp"
android:paddingBottom="0dp"
android:paddingLeft="15dp"
android:textSize="15sp"
android:textColor="#000"
android:text="标题"
/>
<TextView
android:id="@+id/detailTv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="top"
android:paddingTop="0dp"
android:paddingBottom="10dp"
android:paddingLeft="15dp"
android:textSize="12sp"
android:textColor="#676767"
android:text="具体内容"
/>
</LinearLayout> </LinearLayout>

MainActivity中:

public class MainActivity extends Activity {

	private ListView listview;

	private CommonAdapter adapter;

	private List<ItemBean> data;

	@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); listview = (ListView)this.findViewById(R.id.listview);
initData();
adapter = new TextImgAdapter(this.getApplicationContext(), data, R.layout.item_listview);
listview.setAdapter(adapter);
} public void initData(){
data = new ArrayList<ItemBean>();
for(int i=0; i<20; i++){
ItemBean bean = new ItemBean(R.drawable.ic_launcher, "标题"+i, "具体内容"+i);
data.add(bean);
}
}
}

能够看到,做了些更改,数据类型更改为了我们自己定义的bean,bean中有三个属性,分别每一个ListViewItem中的头像、标题、内容

ItemBean类:

public class ItemBean {

	private int imgid;
private String title;
private String detail; public ItemBean() {
super();
// TODO Auto-generated constructor stub
}
public ItemBean(int imgid, String title, String detail) {
super();
this.imgid = imgid;
this.title = title;
this.detail = detail;
}
public int getImgid() {
return imgid;
}
public void setImgid(int imgid) {
this.imgid = imgid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
} }

最后再看看我们的Adapter。因为现在的item布局多了一个ImageView以及一个TextView,所以我们的Adapter相应得变成例如以下:

public class TextImgAdapter extends CommonAdapter<ItemBean>{

	public TextImgAdapter(Context context, List<ItemBean> list, int layoutId) {
super(context, list, layoutId);
// TODO Auto-generated constructor stub
} @Override
public void convert(CommonViewHolder viewHolder, ItemBean data, int position) {
// TODO Auto-generated method stub
ImageView item_iv = viewHolder.getView(R.id.item_iv);
TextView titleTv = viewHolder.getView(R.id.titleTv);
TextView detailTv = viewHolder.getView(R.id.detailTv);
item_iv.setBackgroundResource(R.drawable.ic_launcher);
titleTv.setText(data.getTitle());
detailTv.setText(data.getDetail());
} }

仅仅是多了几行控件的生成以及设置值,清晰了非常多有木有~~以后有再多的元素,依旧仅仅需先生成相应的实例,然后set值。一目了然。

执行结果:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

成功实现我们的效果。妈妈再也不用操心我写Adapter写到废寝忘食.........

Android教你怎样一步步打造通用适配器的更多相关文章

  1. Android 教你打造炫酷的ViewPagerIndicator 不仅仅是高仿MIUI

    1.概述 哈,今天给大家带来一个ViewPagerIndicator的制作,相信大家在做tabIndicator的时候,大多数人都用过 TabPageIndicator,并且很多知名APP都使用过这个 ...

  2. Android | 教你如何在安卓上实现通用卡证识别,一键各种卡绑定

    目录 前言 通用卡证识别的应用场景 如何使用通用卡证识别服务 集成通用卡证识别服务的关键流程 开发实战 1 开发准备 1.1 在项目级gradle里添加华为maven仓 1.2 在应用级的build. ...

  3. 为RecyclerView打造通用Adapter

    ##RecycleView简单介绍 RecyclerView控件和ListView的原理有非常多相似的地方,都是维护少量的View来进行显示大量的数据.只是RecyclerView控件比ListVie ...

  4. 为RecyclerView打造通用Adapter 让RecyclerView更加好用

    原文出处: 张鸿洋 (Granker,@鸿洋_ ) 一.概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器,如今RecyclerView异军突起, ...

  5. Android | 教你如何用代码开发一个拍照翻译小程序

    引子   想必有很多小伙伴喜欢外出旅游,能去海外玩一圈那是更好不过了,旅游前大家一定会对吃.穿.住.行.游玩路线做各种攻略,然后满怀期待的出发- 想象中的旅游   出发前,想象中的旅游目的地可能有漂亮 ...

  6. Android | 教你如何快速集成机器学习能力

    背景   继上篇博文说了如何快速集成扫码以后 我又上官网去了解了一下其他的功能,其中机器学习服务是当下比较火的,而且还是免费的.就赶紧点进去学习一下.看看能够快速实现哪些功能. 链接在这里:https ...

  7. [.NET] 一步步打造一个简单的 MVC 网站 - BooksStore(一)

    一步步打造一个简单的 MVC 网站 - BooksStore(一) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 简介 主 ...

  8. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(二)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(二) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore 前: ...

  9. [.NET] 一步步打造一个简单的 MVC 电商网站 - BooksStore(三)

    一步步打造一个简单的 MVC 电商网站 - BooksStore(三) 本系列的 GitHub地址:https://github.com/liqingwen2015/Wen.BooksStore &l ...

随机推荐

  1. String和StringBuilder、StringBuffer的区别?

    估计很多Java初学者在学习Java的过程中都会遇到这个问题,那就是String,StringBuilder,StringBuffer这三个类之间有什么区别?今天在这里整理一下,希望对大家有帮助哈.如 ...

  2. Homebrew安装卸载

    安装homebrew ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/inst ...

  3. jQuery自己定义绑定的魔法升级版

    jQuery自己定义绑定 首先让我们来看看jQuery的自己定义绑定的用法,你能够使用bind或者live来订阅一个事件(当然1.7以后也能够使用on了),代码例如以下: $("#myEle ...

  4. chrome ui源码剖析-ViewProp

      先熟悉set的find原理 http://www.cnblogs.com/Clingingboy/p/3252136.html 这个类改造下,还是非常实用的,可以对于不同的类型做数据存储 一.Vi ...

  5. Java 微服务实践 - Spring Boot 系列

    https://segmentfault.com/l/1500000009515571

  6. 安装oracle10g“程序异常终止。发生内部错误。请将以下文件提供给oracle技术支持部门

    发生情景:测试环境搭建的是windows 2008 r2 sp1系统 在安装Oracle 10g数据库时发生了错误,现在把解决问题的方法和原因分享给大家. *  安装出现的现象: 输入完密码后下一步时 ...

  7. 理解 HTTPS 协议

    英文原文:Understanding HTTPS Protocol 最近我们看到很多站点使用 HTTPS 协议提供网页服务.通常情况下我们都是在一些包含机密信息的站点像银行看到 HTTPS 协议. 如 ...

  8. JSTL(c标签)与Struts2(s标签)标签的常用功能对比

    1. 条件标签 JSTL <c:if test="${user.password == 'hello'}"> <c:choose> <c:when t ...

  9. OCP-1Z0-051-题目解析-第22题

    22. You need to create a table for a banking application. One of the columns in the table has the fo ...

  10. sk_buff的数据预留和对齐

    转自:http://blog.csdn.net/keepmovingnow/article/details/45850387 数据预留和对齐主要由skb_reserve().skb_put().skb ...