安卓开发笔记——打造万能适配器(Adapter)
为什么要打造万能适配器?
在安卓开发中,用到ListView和GridView的地方实在是太多了,系统默认给我们提供的适配器(ArrayAdapter,SimpleAdapter)经常不能满足我们的需要,因此我们时常要去继承BaseAdapter类去实现一个自定义的适配器来满足我们的场景需要。
如果你是开发一个简单点的APP还好,可能ListView和GridView的数量不会太多,我们只要去写几个BaseAdapter实现类就可以了。
但如果有一天,你需要开发一个APP里面具有几十个ListView或者GridView的子页面,此时的你该怎么办?每个ListView或者GridView都去写一个适配的Adatper类吗?
当然你如果想做蛮牛不嫌累的话也不是不可以,但如果有办法可以让自己减少很多工作量,避免做重复无意义劳动,何乐而不为呢?
万能适配器思想?
软件设计模式:模板方法模式(有兴趣了解的朋友,可以参考看下我之前写过的博文《软件设计模式之模板方法模式(JAVA)》)
其实解决问题的核心思想很简单,一句话:抽取重复代码!
我们在继承BaseAdapter类时,都需要去实现它里面的抽象方法(getCount, getItem, getItemId, getView),其中除了getView这个方法里需要实现的代码不同,其他的都一样。
而这个getView方法里,我们考虑到性能的问题,我们经常会引入一个ViewHolder类(关于不清楚ViewHolder的朋友可以看看我之前写过的博文《安卓开发笔记——ListView加载性能优化ViewHolder》),尽可能的去节省资源。
那么解决问题的思路就出来了,我们可以把这个适配器抽取成2部分:
第一部分是解决(getCount, getItem, getItemId)方法里重复代码的问题。
第二部分是分离getView方法里使用到的ViewHolder,把它单独抽取出来成一个独立的类,利用键值对Key=>Value的方法,以控件ID去寻找对应的View对象。
如果你看完以上这些感觉已经云里来雾里去,没关系,接下去我们用代码说话。
传统适配器的实现方式:
package com.example.listviewtest; import java.util.List; import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView; /**
* 传统适配器Adapter写法
*
* @author Balla_兔子
*
*/
public class MyAdapter extends BaseAdapter {
private LayoutInflater layoutInflater;
private List<User> data; public MyAdapter(Context context, List<User> data, int layoutId) {
//this.context = context;
this.layoutInflater = LayoutInflater.from(context);
this.data = data;
//this.layoutId = layoutId;
} @Override
public int getCount() {
return data.size();
} @Override
public Object getItem(int position) {
return data.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.listview_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.iv_image = (ImageView) convertView.findViewById(R.id.iv_image);
viewHolder.tv_name = (TextView) convertView.findViewById(R.id.tv_name);
viewHolder.tv_phone = (TextView) convertView.findViewById(R.id.tv_phone);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
} User user = data.get(position);
viewHolder.iv_image.setImageResource(R.drawable.ic_launcher);
viewHolder.tv_name.setText(user.getName());
viewHolder.tv_phone.setText(user.getPhone()); return convertView;
} private class ViewHolder {
ImageView iv_image;
TextView tv_name;
TextView tv_phone;
} }
从上面的代码就可以感受到,如果我们去编写多个适配器Adapter的时候,那么我们就势必要去写多个ViewHolder和重复的去写(getCount, getItem, getItemId)方法,而ViewHolder里面常用的控件View也就无非那几种,而那三个方法里(getCount, getItem, getItemId)的代码也是固定不变的,所以重复代码量非常的多,我们应该把它们抽取出来。
万能适配器实现
1、首先我们先来分离这个ViewHolder,其实核心代码并没有改变,只是把传统ViewHolder给做的事情给分离出来罢了。
package com.example.listviewtest; import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup; /**
* ViewHolder集合类
*
* @author Balla_兔子
*
*/
public class CommonViewHolder {
private SparseArray<View> sparseArray;
private View convertView;
private int position; // 构造方法,完成传统Adapter里的创建convertView对象
public CommonViewHolder(Context context, View convertView, int layoutId, ViewGroup parent, int position) {
this.position = position;
this.sparseArray = new SparseArray<View>();
this.convertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
this.convertView.setTag(this); } // 入口方法,完成传统Adapter里面实例化ViewHolder对象工作
public static CommonViewHolder getCommonViewHolder(Context context, View convertView, int layoutId, ViewGroup parent, int position) {
if (convertView == null) {
return new CommonViewHolder(context, convertView, layoutId, parent, position);
} else {
CommonViewHolder commonViewHolder = (CommonViewHolder) convertView.getTag();
//特别需要注意的一点,由于ListView的复用,比如屏幕只显示5个Item,那么当下拉到第6个时会复用第1个的Item,所以这边需要更新position
commonViewHolder.position = position;
return commonViewHolder;
}
} //根据控件Id获取对应View对象
public <T extends View> T getView(int viewId) {
View view = sparseArray.get(viewId);
if (view == null) {
view = convertView.findViewById(viewId);
sparseArray.put(viewId, view);
}
return (T) view;
} //用于返回设置好的ConvertView对象
public View getConvertView(){
return convertView;
} }
这里我们提供了一个入口方法getCommonViewHolder来得到一个ViewHolder的实例对象,若实例不存在,我们去创建并设置Tag保存,这点和先前的ViewHolder所做的事情是一样的。
由于所有的控件都是View的子类,这里提供了一个getView来获取各控件的对象,在我们需要使用的时候强转成我们所需要的控件类型就可以了,这里提供了一个类似Map的集合SparseArray,这个类和Map一样是利用Key=>Value来存取对象的,不同的是这里的Key是整型变量。
下面是SpareseArray源码中对其的介绍:

SparseArray是Android为<Integer,Object>类型的HashMap专门写的类,目的是为了提供效率,其核心算法是折半查找,其用法和Map无两异。
2、再来分离下BaseAdapter,除getView这个方法会有一些不同,其他的代码其实每次书写都是一样的,我们可以自己写一个抽象类把它们都给实现了,只留getView最关键核心的代码部分给用户实现。由于方法操作,我们这里利用泛型<T>
package com.example.listviewtest; import java.util.List; import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView; /**
* 通用适配器Adapter写法
*
* @author Balla_兔子
* @param <T>
*
*/
public abstract class CommonAdapter<T> extends BaseAdapter {
//为了使得子类可以访问,这里修改包访问级别
protected Context context;
protected LayoutInflater layoutInflater;
protected List<T> data;
protected int layoutId; public CommonAdapter(Context context, List<T> data, int layoutId) {
this.context = context;
this.layoutInflater = LayoutInflater.from(context);
this.data = data;
this.layoutId = layoutId;
} @Override
public int getCount() {
return data.size();
} @Override
public Object getItem(int position) {
return data.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
//获取ViewHolder对象
CommonViewHolder myViewHolder = new CommonViewHolder(context, convertView, layoutId, parent, position);
//需要用户复写的方法,设置所对于的View所对应的数据
setConverView(myViewHolder,data.get(position));
return myViewHolder.getConvertView();
} //用户需要实现的方法
public abstract void setConverView(CommonViewHolder myViewHolder, T t); }
完成上面两部分的分离后,我们看看现在的适配器代码编程什么样子
package com.example.listviewtest; import java.util.List; import android.content.Context;
import android.widget.ImageView;
import android.widget.TextView; /**
* 万能适配器Adapter写法
*
* @author Balla_兔子
*
*/
public class MyAdapter extends CommonAdapter<User> { public MyAdapter(Context context, List<User> data, int layoutId) {
super(context, data, layoutId);
} @Override
public void setConverView(CommonViewHolder myViewHolder, User user) {
((ImageView) myViewHolder.getView(R.id.iv_image)).setImageResource(R.drawable.ic_launcher);
((TextView) myViewHolder.getView(R.id.tv_name)).setText(user.getName());
((TextView) myViewHolder.getView(R.id.tv_phone)).setText(user.getPhone());
}
}
很明显,代码量减少了近2/3,而且是一劳永逸,CommonAdapter和CommonViewHolder再也不需要变动了,需要什么我们往里面直接加就可以了,这样让我们可以更为专注的去实现核心代码。当然还可以更简化一点,把这些ViewHolder.getView和setText,setImage等方法再一次封装,变成只传递控件Id和对应数据就够了,这样一来我们连类都不需要写了,直接用new对象去写个内部类实现就可以了。
附上主MainActivity代码:
package com.example.listviewtest; import java.util.ArrayList;
import java.util.List; import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView; public class MainActivity extends Activity { private ListView listView;
private MyAdapter adapter;
private List<User> list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
listView=(ListView) findViewById(R.id.listview);
list=new ArrayList<User>(); //模拟数据源
for(int i=0;i<10;i++){
User user=new User();
user.setName("用户"+i);
user.setPhone("10000"+i);
list.add(user);
} adapter=new MyAdapter(MainActivity.this, list,R.layout.listview_item); listView.setAdapter(adapter); } }
就像这样,以后如果需要使用适配器Adapter就不需要再去继承BaseAdapter了,直接继承CommonAdapter配合CommonViewHolder就可以了。
安卓开发笔记——打造万能适配器(Adapter)的更多相关文章
- 安卓开发笔记——深入Activity
在上一篇文章<安卓开发笔记——重识Activity >中,我们了解了Activity生命周期的执行顺序和一些基本的数据保存操作,但如果只知道这些是对于我们的开发需求来说是远远不够的,今天我 ...
- Android 快速开发系列 打造万能的ListView GridView 适配器
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自[张鸿洋的博客] 1.概述 相信做Android开发的写 ...
- 安卓开发笔记——自定义广告轮播Banner(实现无限循环)
关于广告轮播,大家肯定不会陌生,它在现手机市场各大APP出现的频率极高,它的优点在于"不占屏",可以仅用小小的固定空位来展示几个甚至几十个广告条,而且动态效果很好,具有很好的用户& ...
- 安卓开发笔记——丰富多彩的TextView
随手笔记,记录一些东西~ 记得之前写过一篇文章<安卓开发笔记——个性化TextView(新浪微博)>:http://www.cnblogs.com/lichenwei/p/4411607. ...
- 安卓开发笔记——关于开源项目SlidingMenu的使用介绍(仿QQ5.0侧滑菜单)
记得去年年末的时候写过这个侧滑效果,当时是利用自定义HorizontalScrollView来实现的,效果如下: 有兴趣的朋友可以看看这篇文件<安卓开发笔记——自定义HorizontalScro ...
- 安卓开发笔记——关于Handler的一些总结(上)
接上篇文章<安卓开发笔记——关于AsyncTask的使用>,今天来讲下在安卓开发里"重中之重"的另一个异步操作类Handler. 今天打算先讲下关于Handler的一些 ...
- 安卓开发笔记——Fragment+ViewPager组件(高仿微信界面)
什么是ViewPager? 关于ViewPager的介绍和使用,在之前我写过一篇相关的文章<安卓开发复习笔记——ViewPager组件(仿微信引导界面)>,不清楚的朋友可以看看,这里就不再 ...
- 安卓开发笔记——关于照片墙的实现(完美缓存策略LruCache+DiskLruCache)
这几天一直研究在安卓开发中图片应该如何处理,在网上翻了好多资料,这里做点小总结,如果朋友们有更好的解决方案,可以留言一起交流下. 内存缓存技术 在我们开发程序中要在界面上加载一张图片是件非常容易的事情 ...
- 安卓开发笔记(十):升级ListView为RecylerView的使用
概述 RecyclerView是什么 从Android 5.0开始,谷歌公司推出了一个用于大量数据展示的新控件RecylerView,可以用来代替传统的ListView,更加强大和灵活.Recycle ...
随机推荐
- HTTP请求与响应报文详解
如图所示,这是客户端往服务器发送请求时的报文: 一般来说,将报文分成三个部分,请求行.请求头.请求体 如图,请求行包括三部分内容 1.请求方法,在HTTP里的请求方法种类较多,但就移动端开发来说,常用 ...
- SQL学习(持续更新)
1.having筛选分组 正如where子名限制了select显示的行数,having限制了group by显示的分组数.where查询条件在分组产生前就被计算,而having搜索条件在分组产生之后才 ...
- C#学习笔记(28)——委托排序(2)自定义排序
说明(2017-11-21 15:24:50): 1. 定义一个排序方法,参数是字符串数组,和委托.MySort(nums, string.Compare),调用时只需要更换里面的委托方法就行,或者直 ...
- android App抓包工具的应用(转)
安装好 fiddler ,手头有一部Android 手机,同时 还要有无线网,手机和 电脑在同一个无线网络.这些条件具备,我们就可以 开始下面的步骤了. 正题 :Fiddler 主菜单 Tools - ...
- 【Unity】角色受伤后的闪烁(blink/flash)效果
玩家受伤后,一段时间内快速闪烁.这里想要的闪烁效果是玩家快速的显隐切换效果,而不是玩家变白的情况. 快速切换玩家的显隐效果不能用SetActive修改角色物体本身的激活状态,因为玩家角色身上的其他脚本 ...
- springmvc访问静态资源出现Request method 'GET' not supported
答案最后.:D 默认的访问的URL都会被DispatcherServlet所拦截. 这里说一下如何配置springmvc访问静态文件. <mvc:default-servlet-handler/ ...
- TiKV 源码解析系列 - Raft 的优化
本系列文章主要面向 TiKV 社区开发者,重点介绍 TiKV 的系统架构,源码结构,流程解析.目的是使得开发者阅读之后,能对 TiKV 项目有一个初步了解,更好的参与进入 TiKV 的开发中.本文是本 ...
- 使用VMware将Linux装在物理硬盘上,开机即可进入Linux (转)
目录(?)[-] 本文目的 具体操作 1 软件准备 2 安装 21 对硬盘操作 22 创建虚拟机并安装 23 使用Grub引导Linux 1. 本文目的 适合于没有光驱的计算机来安装Linux,还有 ...
- Build 2016上一些令人兴奋的东西
本来应该要更新Windows IOT开发入门的,但是由于Build 2016刚刚过去,不得不将一些令人兴奋的东西给大家进行分享. 首先对于错过这次直播的,可以在以下链接再次看到观看: https:// ...
- 【转】android中的数据存取-方式一:preference(配置)
这种方式应该是用起来最简单的Android读写外部数据的方法了.他的用法基本上和J2SE(java.util.prefs.Preferences)中的用法一样,以一种简单. 透明的方式来保存一些用户个 ...