码农小鸥, 在看某TT开源项目的Android实现, 发现到处分布的ViewHolder, 作为有代码洁癖的小码农, 小鸥觉得这个地方应该有很大的优化点.
基本的ViewHolder的样式
public class MyViewHorlder
{
public TextView textView;
public Button button;
}
ViewHolder的作用
1.保存各种View, 方便下次使用.
2.设计上抽出视图层次, 更便于MVC的实现.
但是繁琐的是
1.ViewHolder的各种定义, 基本上一个Layout下的布局文件, 对应一个ViewHolder.
2.调用大量的findViewById函数对这个ViewHolder初始化.
这种繁琐没乐趣的事是码农最最痛恨的. 以作为一个纯粹的码农自居的小鸥表示这种工作模式不能忍.
那么怎么办呢?
既然一个layout文件可以对应一个ViewHolder, 何不写个eclipse插件来自动完成从layout.xml=>ViewHolder.java的转化呢?
说干就干.
首先, 设计我们大致需要的ViewHolder的模型
/**
* @author zju_wjf
*
*/
public interface IViewHolder {
/**
* 对应的R.layout中 id
* @return
*/
public int getId() ;
/**
* 自定义实现, 根据数据设置View的展示等
* @param data
*/
public void initByData(Bundle data);
/**
* 获得该ViewHolder的根View
* @return
*/
public View getRootView();
/**为该ViewHolder设置根View
*
* @param rootView
*/
public void setRootView(View rootView);
}
一个ViewHolder 会有
1.一个根View
2.一个R.layout.id和它唯一对应
3.一个根据数据模型设置View的属性的同步方法.
实现了IViewHolder接口的抽象类 AbstractViewHolder
/**
* @author zju_wjf
*/
public abstract class AbstractViewHolder implements IViewHolder {
private View rootView;
@Override
public View getRootView(){
return rootView;
}
@Override
public void setRootView(View rootView){
this.rootView = rootView;
}
@Override
public void initByData(Bundle data) {
}
}
下面是我们的主角出场啦, 最终生成的ViewHolder 应该是这个样子滴 - -
public class Tt_activity_preview_text extends AbstractViewHolder{
@ViewHolderInject(id = R.id.textView)
public TextView textView;
@ViewHolderInject(id = R.id.button)
public Button button;
public final int getId() {
return R.layout.tt_activity_preview_text;
}
}
等等, 这里怎么多出了@ViewHolderInject这家伙?
这个注解类是为了辅助自动为ViewHolder注入各种View而生成的.
于是我们应该有一个工具类来生成这个ViewHolder实例 以及自动注入这个ViewHolder内部的所有View.
/**
* @author zju_wjf
*
*/
public class ViewHolderUtils {
public static <T extends IViewHolder> T create(final Class<T> _class, final Context context, final ViewGroup viewGroup) throws InstantiationException, IllegalAccessException {
final T t = _class.newInstance();
final View rootView = LayoutInflater.from(context).inflate(t.getId(), viewGroup);
t.setRootView(rootView);
injectViews(t);
return t;
}
private static void injectViews(IViewHolder viewSet) throws IllegalArgumentException, IllegalAccessException {
final Class<? extends IViewHolder> _class = viewSet.getClass();
final View rootView = viewSet.getRootView();
final Field [] fields = _class.getFields();
for(Field field:fields) {
final ViewHolderInject viewInject = field.getAnnotation(ViewHolderInject.class);
if(viewInject != null) {
//去除检查, 提高反射效率
field.setAccessible(true);
final View sub = rootView.findViewById(viewInject.id());
field.set(viewSet, sub);
}
}
}
}
有了它, 我们再也不用写讨厌的findViewById了.
大功告成, 我们基本上可以试一试了.
try {
Tt_activity_preview_text myTt_activity_preview_text = ViewHolderUtils.create(Tt_activity_preview_text.class, this, null);
myTt_activity_preview_text.textView.setText("Hello ViewHolder.");
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
小鸥写的有些老太太的裹脚布, 又臭又长了. 那么如何编写这个自动生成的eclipse插件的工程就放在下一章吧.
项目git地址:https://github.com/zjuwjf/AutoViewHolder