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自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
随机推荐
- POJ-3295 Tautology---栈+表达式求值
题目链接: https://vjudge.net/problem/POJ-3295 题目大意: 输入由p.q.r.s.t.K.A.N.C.E共10个字母组成的逻辑表达式WFF 其中 ...
- LabelFrame
LabelFrame组件是Frame组件的变体. 默认情况下,LabelFrame会在其子组件的周围绘制一个边框以及一个标题. 何时使用LabelFrame组件?当你想要奖一些相关的组件分为一组的时候 ...
- 简述angular自定义过滤器在页面和控制器中的使用
首先设置自定义过滤器. 定义模块名:angular ? 1 2 3 4 5 6 .module('myApp') .filter('filterName',function(){ return fun ...
- 如何用Math.max.apply()获取数组最大/小值
最近似乎对JavaScript有点兴趣了~~~打算好好钻研这个东西.可是,一开始就遇到问题了!!! Math.min.apply(obj,args);//这个obj对象将代替Function类里thi ...
- background属性的学习整理转述
前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! background我们一般用到的的属性有: background-attachment:背景(图片)是否 ...
- 用PHP如何实现这种乘法口诀表?
用PHP如何实现这种乘法口诀表? 1x1=1 ,1x2=2 ,1x3=3 ,.....,1x9=9 2x2=4 ,2x3=6 ,......,2x9=18 ........ ...... 8x8=64 ...
- [LeetCode] Design Excel Sum Formula 设计Excel表格求和公式
Your task is to design the basic function of Excel and implement the function of sum formula. Specif ...
- SpringMVC 自定义类型转换器
先准备一个JavaBean(Employee) 一个Handler(SpringMVCTest) 一个converters(EmployeeConverter) 要实现的输入一个字符串转换成一个emp ...
- shell编程-项目部署(二)
上节我们讲了项目部署的准备工作,现在具体讲下代码部署 首先梳理下思路,大致是这样: 获取代码 打包代码 传输代码 关闭应用 解压文件 放置文件(备份老文件,放置新的文件) 开启应用 最后检查下 OK, ...
- 机器学习技法:14 Radial Basis Function Network
Roadmap RBF Network Hypothesis RBF Network Learning k-Means Algorithm k-Means and RBF Network in Act ...