Android开发技巧——BaseAdapter的另一种优雅封装
RecyclerView虽然因其灵活性、高效性等特点而备受好评,但也不是一定得用它把ListView给替代掉。在某些场景中,ListView还是相对更适合的。比如数据量不大,不频繁更新,并且需要简单地设置一下divider或header、footer的时候,相对于RecyclerView的繁琐,ListView在实现上则表现得更方便和简洁。
过去的封装
在使用ListView的过程中,为了复用ListView中的convertView以及优化getView()时的findViewById操作,我们通常会引入一个ViewHolder类,来持有itemView的子view。但是不便的是,我们需要为每一种显示不同数据的ListView都重写其BaseAdapter的getView(),于是就有了对其的一种封装:
创建一个通常的ViewHolder,里面用SparseArray<View>来缓存,如下:
public class ViewHolder {
private final View itemView;
private SparseArray<View> mHolderViews;
public ViewHolder(View view) {
itemView = view;
view.setTag(this);
mHolderViews = new SparseArray<>();
}
public void hold(int... resIds) {
for (int id : resIds) {
mHolderViews.put(id, itemView.findViewById(id));
}
}
public <V> V get(int id) {
return (V) mHolderViews.get(id);
}
}
再通过重写BaseAdapter的getView方法,作如下封装:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = createHolder(position, parent);
convertView = holder.itemView;
} else {
holder = (ViewHolder) convertView.getTag();
}
bindData(position, holder, getItem(position));
return convertView;
}
public abstract ViewHolder createHolder(int position, ViewGroup parent);
public abstract void bindData(int position, ViewHolder holder, T data);
这样使用的时候只需要继承并实现两个抽象方法即可。
这样写法,在后来我觉得还是有些别扭的地方。一是使用的时候还是需要去继承我们封装过的Adapter;二是在bindData()方法中,需要对每一个我们要设置的控件调用holder.get(id)方法,才能开始赋值或进行其他的设置,当我们的item的控件较多时,这些调用会使bindData()显得臃肿而不够纯粹,于是我又对其进行了另一种封装。
另一种优雅封装
这里我主要是通过接口解决重写Adapter的问题,然后再借鉴RecyclerView.ViewHolder,封装结果如下:
假设我们重写BaseAdapter的类为BaseListAdapter,那么首先定义它的一个静态内部类:
public static abstract class ViewHolder {
public final View itemView;
public ViewHolder(View itemView) {
this.itemView = itemView;
itemView.setTag(this);
}
}
然后我们定义一个接口,用于创建ViewHolder以及绑定数据,如下:
package com.githang.android.snippet.demo.adapter.adapter;
import android.view.ViewGroup;
public interface ViewCreator<T, H extends BaseListAdapter.ViewHolder> {
H createHolder(int position, ViewGroup parent);
/**
* 设置列表里的视图内容
*
* @param position 在列表中的位置
* @param holder 该位置对应的视图
*/
void bindData(int position, H holder, T data);
}
这里定义了两个泛型 ,一个是T,表示我们Adapter里的数据对象,另一个是H,为我们的ViewHolder的子类,表示我们最终创建出来的ViewHolder。
接下来,修改我们的BaseListAdapter,如下:
public class BaseListAdapter<T, H extends BaseListAdapter.ViewHolder> extends BaseAdapter {
private final List<T> mData;
private final ViewCreator<T, H> mViewCreator;
public BaseListAdapter(ViewCreator<T, H > creator) {
this(new ArrayList<T>(), creator);
}
public BaseListAdapter(List<T> data, ViewCreator<T, H> creator) {
mData = data == null ? new ArrayList<T>() : data;
mViewCreator = creator;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public T getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final H holder;
if (convertView == null) {
holder = mViewCreator.createHolder(position, parent);
convertView = holder.itemView;
} else {
holder = (H) convertView.getTag();
}
mViewCreator.bindData(position, holder, getItem(position));
return convertView;
}
public void update(List<T> data) {
mData.clear();
addData(data);
}
public void addData(List<T> data) {
if (data != null) {
mData.addAll(data);
}
notifyDataSetChanged();
}
public static abstract class ViewHolder {
public final View itemView;
public ViewHolder(View itemView) {
this.itemView = itemView;
itemView.setTag(this);
}
}
}
在这个BaseListAdapter里,同样定义了两个泛型,与我们的ViewCreator相同。我们创建ViewHolder及绑定数据的操作,通过调用该接口来执行(见getView方法),而该接口实例则通过构造方法传入。
这样,我们在使用的时候就可以先像使用RecyclerView一样,定义一个实现我们ViewHolder的子类,然后使我们的Fragment或Activity实现这个ViewCreator接口就可以了。
最后,我们还可以继承ViewHolder,封装一个通用的ViewHolder,代码如下:
public static class DefaultViewHolder extends ViewHolder {
private SparseArray<View> mHolderViews;
public DefaultViewHolder(View view) {
super(view);
mHolderViews = new SparseArray<>();
}
public void hold(int... resIds) {
for (int id : resIds) {
mHolderViews.put(id, itemView.findViewById(id));
}
}
public <V> V get(int id) {
return (V) mHolderViews.get(id);
}
}
接下来是使用示例:
public class SampleFragment extends Fragment implements ViewCreator<User,SampleFragment.UserViewHolder> {
private BaseListAdapter<User, UserViewHolder> mAdapter = new BaseListAdapter<User, UserViewHolder>(this);
@Override
public UserViewHolder createHolder(int position, ViewGroup parent) {
return new UserViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_user, parent, false));
}
@Override
public void bindData(int position, UserViewHolder holder, User user) {
holder.name.setText(user.name);
holder.email.setText(user.email);
}
static class UserViewHolder extends BaseListAdapter.ViewHolder {
public final TextView name;
public final TextView email;
public UserViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
email = (TextView) itemView.findViewById(R.id.email);
}
}
}
全部的封装代码加上注释等也不过一百来行,但是可以看到,在这样的封装下,我们的代码已经变得很清晰。
本文的代码见:http://download.csdn.net/detail/maosidiaoxian/9700700
更详细的封装及相关代码见我的开源项目:https://github.com/msdx/AndroidSnippet/tree/master/library/src/main/java/com/githang/android/snippet/adapter
Android开发技巧——BaseAdapter的另一种优雅封装的更多相关文章
- Android开发技巧——大图裁剪
本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...
- Android开发技巧——实现可复用的ActionSheet菜单
在上一篇<Android开发技巧--使用Dialog实现仿QQ的ActionSheet菜单>中,讲了这种菜单的实现过程,接下来将把它改成一个可复用的控件库. 本文原创,转载请注明出处: h ...
- Android开发技巧——高亮的用户操作指南
Android开发技巧--高亮的用户操作指南 2015-12-15补记: 发现使用PopupWindow进行遮罩层的显示,在华为P7上会有问题.具体表现为:画出来的高亮部分会偏下.原因为:通过view ...
- Android开发技巧——自定义控件之增加状态
Android开发技巧--自定义控件之增加状态 题外话 这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天.不想把这篇拖到下个月,所以还是先硬着头皮写了. ...
- Android开发技巧——自定义控件之使用style
Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...
- Android开发技巧——自定义控件之自定义属性
Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...
- 50个android开发技巧
50个android开发技巧 http://blog.csdn.net/column/details/androidhacks.html
- Android开发技巧——使用PopupWindow实现弹出菜单
在本文当中,我将会与大家分享一个封装了PopupWindow实现弹出菜单的类,并说明它的实现与使用. 因对界面的需求,android原生的弹出菜单已不能满足我们的需求,自定义菜单成了我们的唯一选择,在 ...
- Android开发技巧——自定义控件之组合控件
Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
随机推荐
- Struts(二十六):文件上传
表单的准备 想要使用html表单上传一个或多个文件 1.须把html表单的enctype属性设置为multipart/form-data 2.须把html表单的method属性设置为post 3.须添 ...
- Java:List集合内的对象进行排序
List集合中的对象进行排序,除了for外,还有java的Collections对象来对摸个集合进行排序的用法. 比如说我有一个List集合,集合元素为: public class TaskAutoE ...
- 使用 RHEL(RedHat)6.1 iso 安装包 安装Samba过程
今天因为工作的需要安装了(RHEL)redhat 6.1 自己为了方便就安装Samba 以记之. 注:Linux系统是刚刚安装好的所以没有samba安装的任何记录. 安装准备: ISO:RHEL_6. ...
- 卷积神经网络(CNN)中卷积的实现
卷积运算本质上就是在滤波器和输入数据的局部区域间做点积,最直观明了的方法就是用滑窗的方式,c++简单实现如下: 输入:imput[IC][IH][IW] IC = input.channels IH ...
- 浅析开源数据库MySQL架构
数据库是所有应用系统的核心,故保证数据库稳定.高效.安全地运行是所有企业日常工作的重中之重.数据库系统一旦出现问题无法提供服务,有可能导致整个系统都无法继续工作.所以,一个成功的数据库架构在高可用设计 ...
- [转]linux查看日志文件内容命令
linux查看日志文件内容命令tail.cat.tac.head.echo tail -f test.log你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C, ---------- ...
- join()的用法
Python中有join()和os.path.join()两个函数,具体作用如下: join(): 连接字符串数组.将字符串.元组.列表中的元素以指定的字符(分隔符)连接生成一个新的字符串 ...
- Flask博客开发——登录验证码
这部分为Flask博客的登录页面加个验证码.使用了PIL模块生成验证码图片,并通过Flask的session机制,进行验证码验证. 1.生成验证码 使用string模块:string.ascii_le ...
- NLog日志管理工具(转)
一.通过VS建立一个控制台应用程序. 二.打开程序包管理器控制台.具体操作如下:[工具]>[库程序包管理器]>[程序包管理器控制台]. 三.在程序包管理器控制台下输入命令:Install- ...
- 获取 Let's Encrypt 免费通配符证书实现Https
说明 3月14日,Let's Encrypt的执行董事Josh Aas对外宣布,他们的通配符证书正式上线,用户可以基于此特性轻松部署/开启所有子域名的HTTPS功能. Let's Encrypt 是国 ...