网上已经有一些关于ViewHolder的另类写法,但如果item里控件稍多起来的话,整个adpter类还是会稍显冗余。于是接下来为大家介绍另一种另类的写法。
项目就是对一个ListView填充数据。
ListView优化里,ViewHolder类的重要性不言而喻,寻常ViewHolder类写法的弊端就不说了,大家都懂。
这里先自定义一个BaseHolder类,代码如下:
/**
* @des 提供视图,绑定数据
*/
public abstract class BaseHolder<HOLDERBEANTYPE> {
public View mHolderView; //提供的视图
private HOLDERBEANTYPE mData; //数据,注意:这里的数据是List集合里的子数据
public Context mContext; //上下文
public BaseHolder(Context context){
mContext = context;
//初始化视图
mHolderView = initHolderView();
//将视图与holder类进行绑定
mHolderView.setTag(this);
}
/**
* @des 接收数据,与视图进行绑定
* @param data List集合里的子数据,即每一个视图里需要填充的数据
*/
public void setDataAndRefreshHolderView(HOLDERBEANTYPE data){
mData = data;
//与视图进行绑定
refreshHolderView(data);
}
/**
* @des 与视图进行绑定,交给子类去实现
*/
protected abstract void refreshHolderView(HOLDERBEANTYPE data);
/**
* @des 初始化数据,交给子类去实现
* @return
*/
public abstract View initHolderView();
}
仅仅是一个BaseHolder类并不能明确需要提供的视图和数据绑定等操作,所以,接下来,就根据具体的item.xml文件来提供特定的holder类:代码如下:
/**
* @des 提供视图;接收数据;视图和数据的绑定
*/
public class ChildHolder extends BaseHolder<String> {
//子类holder,到这里就已经知道自己到传递的数据是什么类型了,这里就用String类作为演示
private TextView mTextView;
public ChildHolder(Context context) {
super(context);
}
/**
* @return
* @des 初始化视图
*/
@Override
public View initHolderView() {
//这里的item.xml文件里只添加了一个TextView,布局比较简单就不给出代码了。
View holderView = View.inflate(mContext, R.layout.item, null);
mTextView = (TextView) holderView.findViewById(R.id.item_text);
return holderView;
}
/**
* @des 进行数据与视图的绑定
*/
@Override
protected void refreshHolderView(String data) {
mTextView.setText(data);
}
}
到此,整个ViewHolder类的准备工作就已经完成了,是否可以直接在Adapter适配器里面使用了呢?当然可以,但如果直接使用BaseAdapter类,我们还得重写getView、getItem、getItemId和getCount等方法。
这里,我们再进一步,对BaseAdapter类进行继承抽取,代码如下:
/**
* @des 这个类主要对BaseAdapter的除getView的其三个方法 进行复写
*/
public abstract class MyAdapter<ITEMBEANTYPE> extends BaseAdapter {
public List<ITEMBEANTYPE> mDatas; //要填充的数据,这里的数据是全部数据的集合
public MyAdapter(List<ITEMBEANTYPE> datas) {
mDatas = datas;
}
@Override
public int getCount() {
if (mDatas != null) {
return mDatas.size();
}
return 0;
}
@Override
public Object getItem(int position) {
if (mDatas != null) {
return mDatas.get(position);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
}
上面的MyAdapter只是对三个方法进行了重写,接下来我们去重写getView方法,这里是重点,大家细细看,代码如下:
/**
* @des 针对getView方法进行复写
*/
public abstract class SuperAdapter<ITEMBEANTYPE> extends MyAdapter {
public SuperAdapter(List<ITEMBEANTYPE> datas) {
super(datas);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
BaseHolder baseHolder;
if (convertView == null) {
//创建holder类
baseHolder = getSpecialHolder();
} else {
//如果不为空,就直接获取,至于这里为什么能直接获取,是因为BaseHolder类的构造方法里已经对视图进行了绑定
baseHolder = (BaseHolder) convertView.getTag();
}
//获取视图
View holderView = baseHolder.mHolderView;
//进行视图和数据的绑定
baseHolder.setDataAndRefreshHolderView(mDatas.get(position));
return holderView;
}
/**
* @return
* @des 获取具体的holder类,交给子类去实现
*/
public abstract BaseHolder getSpecialHolder();
}
至些,一切的准备工作已经完成,我们可以直接去使用了:
public class MainActivity extends AppCompatActivity {
private ListView mListview;
private List<String> mDatas; //要填充的数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这里为了方便演示,直接模拟了数据
mDatas = new ArrayList<String>();
for (int i = 0; i < 100; i++) {
mDatas.add(i + "");
}
mListview = (ListView) findViewById(R.id.main_listview);
mListview.setAdapter(new MainListViewAdapter(mDatas));
}
public class MainListViewAdapter extends SuperAdapter {
//通过构造方法传递数据
public MainListViewAdapter(List<String> datas) {
super(datas);
}
//返回相应的holder子类
@Override
public BaseHolder getSpecialHolder() {
return new ChildHolder(MainActivity.this);
}
}
}
直接运行就能出效果了。
这里抽取的比较多,看似比直接使用BaseAdapter还要复杂得多,但在之后的使用中你只需要去写一个相应的holder子类就可以了,而且继承SuperAdapter后写法变得超级简便。
更重要的一点,这种写法对于之后代码的维护非常的有利。
(初写博文,写的不尽详细请见谅,如有改进之处,望大家不吝建议。)