使用注解来折腾BaseAdapter(1)
来源:互联网 发布:什么叫sql语句 编辑:程序博客网 时间:2024/06/06 22:39
使用注解来折腾BaseAdapter
在正式开始阅读博客前,我们先来大致了解一下JAVA中的注解:
- 定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。—来自某百科
作用分类:
①编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
② 代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
③编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
因此,我想说的是,注解是注解,反射是反射,二者不要混为一谈,只是说在对某一域进行了注解之后,若要取得注解的值,一般都是使用反射。
首先,我们来看看写Adapter的一般现在时
/** * Created by 阿木木 * com.example.administrator.myapplication.adapter * 2016/3/19 14:06 */public class YourAdapter extends BaseAdapter { private Context context; private List<GoodsInfo> goodsInfoList; public YourAdapter(Context context, List<GoodsInfo> goodsInfoList) { this.context = context; this.goodsInfoList = goodsInfoList; } public void setData(List<GoodsInfo> goodsInfoList) { this.goodsInfoList.addAll(goodsInfoList); notifyDataSetChanged(); } public List<GoodsInfo> getData() { return this.goodsInfoList; } @Override public int getCount() { return goodsInfoList.size(); } @Override public GoodsInfo getItem(int position) { return goodsInfoList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate(R.layout.list_view_item_goods, parent, false); holder.image = (ImageView) convertView.findViewById(R.id.goods_pic); holder.name = (TextView) convertView.findViewById(R.id.goods_name); holder.price = (TextView) convertView.findViewById(R.id.goods_price); holder.buy = (Button) convertView.findViewById(R.id.goods_buy); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } GoodsInfo current = getItem(position); holder.image.setImageResource(current.getGoodsPic()); holder.name.setText(current.getGoodsName()); holder.price.setText(context.getString(R.string.price_yuan, current.getGoodsPrice())); return convertView; } public static class ViewHolder { ImageView image; TextView name; TextView price; Button buy; }
显示效果就这样
在这可以看到,findViewById和holder.xxx.setText占用篇幅较长,而且每一个listView(gridView)的Adapter都这样写,手(逼)都(格)写(不)麻(高)了。
那么我们就来提升一下逼格
首先进行分析,在重写getView中,先加载此Item的布局(inflate),然后再对ViewHolder中的变量进行赋值(findViewById);将此ViewHolder存入布局中,在下次使用的时候可以再次取出。取出时然后通过Bean.get各种值,将此值设置到ViewHolder中定义的各种控件。
它们之间乱搞的关系大概是这样:
如果不考虑复用和多种布局的情况下,一个ViewHolder也就对应一个Adapter,而此Adapter也就加载一个布局,那么ViewHolder完全可以和布局进行绑定,最终要显示实体类值的时候,实体类的某个属性和ViewHolder(和显示的控件)也是一个对应的关系。
这样分析下来,如果要添加注解
- 那么给ViewHolder类上添加一个,告诉Adapter“我要加载这个布局”,
- 再在定义的变量上分别给它们添加一个注解,告诉Adapter“我的这个控件是刚才给你的布局里面的这个控件”。
那么就依据这两点,来新建两个注解类。
/** * Created by 阿木木 * com.example.administrator.myapplication.annoation * 2016/3/19 17:03 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ListAdapterLayoutId { int value();}
/** * Created by 阿木木 * com.example.administrator.myapplication.annoation * 2016/3/19 17:05 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ListAdapterViewId { int value();}
这俩货仅仅Target处不同,一个表示此注解只能放在类上,另一个则表示此注解只能放在成员属性(变量)上。
再到ViewHolder类中来使用,然后大概长这样:
@ListAdapterLayoutId(R.layout.list_view_item_goods) public static class ViewHolder { @ListAdapterViewId(R.id.goods_pic) ImageView image; @ListAdapterViewId(R.id.goods_name) TextView name; @ListAdapterViewId(R.id.goods_price) TextView price; @ListAdapterViewId(R.id.goods_buy) Button buy; }
注:使用反射获取注解中的值时,是一个比较繁琐的过程,也许取着取着,把对象搞错了,可能本应该此class的某个变量,结果取成另外一个东西的了。
那么这个Adapter类就不能只针对这一个地方使用,而应作为基类,在其它地方需要使用的时候,再对它进行定制。
整理好的Adapter基类
/** * Created by 阿木木 * com.example.administrator.myapplication.adapter * 2016/3/19 17:11 */public class MyBaseAdapter<T> extends BaseAdapter { private Context context; private List<T> list; public MyBaseAdapter(Context context) { this.context = context; this.list = new ArrayList<>(); } @Override public int getCount() { return list.size(); } @Override public T getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return convertView; } public static class BaseViewHolder { }}
重写getView,在这里面我们做一些有趣的事;
照搬普通的Adapter
ViewHolder holder; if (convertView == null) { holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate(R.layout.list_view_item_goods, parent, false); holder.image = (ImageView) convertView.findViewById(R.id.goods_pic); holder.name = (TextView) convertView.findViewById(R.id.goods_name); holder.price = (TextView) convertView.findViewById(R.id.goods_price); holder.buy = (Button) convertView.findViewById(R.id.goods_buy); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); }
那么问题来了,我作为父类,那么这个ViewHolder肯定不知道到底是什么类型的,自然就不能放在这里来进行实例化,那解决方式就是,由调用者告诉我,你用的ViewHolder是哪个类,我自己来造一个对象满足自己(的私欲)。
/** * Created by 阿木木 * com.example.administrator.myapplication.adapter * 2016/3/19 17:11 */public class MyBaseAdapter<T> extends BaseAdapter { private Context context; private List<T> list; private Class<? extends BaseViewHolder> holderClass; public MyBaseAdapter(Context context) { this.context = context; this.list = new ArrayList<>(); this.holderClass = holderClass; } @Override public int getCount() { return list.size(); } @Override public T getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return inject(holderClass, position, convertView, parent); } private void inject(Class<? extends BaseViewHolder> clazz, int position, View convertView, ViewGroup parent){ try { //和基本写法相同 if (convertView == null) { //获取到ViewHolder上的注解 ListAdapterLayoutId viewLayoutId = clazz.getAnnotation(ListAdapterLayoutId.class); //如果获取到的注解不为空,即为“客户端已按要求给ViewHolder指定了它要加载的ItemLayout” if (viewLayoutId != null) { //获取需要加载的布局的ID int layoutId = viewLayoutId.value(); convertView = LayoutInflater.from(context).inflate(layoutId, parent, false); //实例化出ViewHolder的一个对象 holder = clazz.newInstance(); //获取ViewHolder对象中的所有成员属性,包括私有(private)但排除父类 Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { //设置成员属性可修改 field.setAccessible(true); //找到这个成员属性给它的注解,对应为控件ID ListAdapterViewId viewIdAnnotation = field.getAnnotation(ListAdapterViewId.class); //如果给定的注解不为空,则说明在ViewHolder类中指定了它是哪个控件 if (viewIdAnnotation != null) { //获取得到注解中指定的控件ID int viewId = viewIdAnnotation.value(); //在布局中找到此控件 View view = convertView.findViewById(viewId); if (view != null) { //找到此控件后,对ViewHolder的对象设置对应成员属性为找到的这个控件 field.set(holder, view); //将此控件放入集合中 holderFields.put(viewId, view); } else { throw new RuntimeException("ViewHolder类中的"+field.getName()+"注解的View Id无法找到"); } } } //和普通写法相同 convertView.setTag(holder); } else { throw new RuntimeException("传入的ViewHolder类没有注解指定布局ID"); } } else { holder = (BaseViewHolder) convertView.getTag(); } }***省略了好多加代码 public static class BaseViewHolder { }}
现在就算把ViewHolder中定义的属性找到了,先测试一下。
OK,测试通过,说明前面的逼没白装。
继续
接下来得显示我们实体类中的信息了。
接上的else之后
//再次获取此ViewHolder的所有成员属性,这一步是用来设置具体的值 for (Field field : clazz.getDeclaredFields()) { //获取对应成员属性的指定注解 ListAdapterViewId holderViewAnnotation = field.getAnnotation(ListAdapterViewId.class); //判断ViewHolder有没有给这个成员属性添加注解,添加了注解才能继续下一步 if (holderViewAnnotation != null) { //获取ListView中对应下标的此处的实体,再获取此实体类的成员变量,对Bean类的属性进行遍历 for (Field beanField : getItem(position).getClass().getDeclaredFields()) { //获取实体类中的成员属性的注解,需要这个注解和View的ID进行对比 ListAdapterViewId annotation = beanField.getAnnotation(ListAdapterViewId.class); //设置可修改Bean类对应的成员属性可修改 beanField.setAccessible(true); //随即判断是否添加了注解,若不注解,则表明“这个变量我不设置到ListView中” if (annotation != null) { //对Bean类的变量与ViewHolder的变量进行对比,如果它们都指向同一个View的ID,则表明它们是一一对应 if (annotation.value() == holderViewAnnotation.value()) { field.setAccessible(true); //field.getType(),获取到这个ViewHolder的成员属性,是什么类型, Class<?> type = field.getType(); if (type.getName().equals(TextView.class.getName())) { //如果是TextView再通过类型来找setText(CharSequence)这个方法 Method setText = type.getMethod("setText", CharSequence.class); //如果能找到setText(CharSequence)方法,表明确实是TextView,并判断对应的View有没有添加到布局中 if (setText != null && holderFields.get(annotation.value()) != null) { //直接执行setText setText.invoke(holderFields.get(annotation.value()), beanField.get(getItem(position))+""); } } //注,动态设置显示的只支持TextView及其子类。对于ImageView,在此不能有ImageLoader来对ImageView进行显示的操作 //这里只管做它该做的事,怎么显示由客户端使用相应的操作 } } } } }
以上添加完毕后,一定不要忘记,还要给实体类注解,让它知道该显示哪里。
/** * Created by 阿木木 * com.example.administrator.myapplication.bean * 2016/3/19 13:58 */public class GoodsInfo { @ListAdapterViewId(R.id.goods_name) private String goodsName; @ListAdapterViewId(R.id.goods_price) private int goodsPrice; @ListAdapterViewId(R.id.goods_pic) private int goodsPic; public GoodsInfo() { } public GoodsInfo(String goodsName, int goodsPrice, int goodsPic) { this.goodsName = goodsName; this.goodsPrice = goodsPrice; this.goodsPic = goodsPic; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public int getGoodsPrice() { return goodsPrice; } public void setGoodsPrice(int goodsPrice) { this.goodsPrice = goodsPrice; } public int getGoodsPic() { return goodsPic; } public void setGoodsPic(int goodsPic) { this.goodsPic = goodsPic; }}
最终显示效果如下
测试通过
然后代码如下
Activity
public class MainActivity extends Activity { private ListView listView; private MyBaseAdapter<GoodsInfo> adapter; private List<GoodsInfo> goodsInfoList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); goGoGo(); listView.postDelayed(new Runnable() { @Override public void run() { getTheList(); } }, 500); } private void initView() { listView = (ListView) findViewById(R.id.test_list_view); goodsInfoList = new ArrayList<>(); adapter = new MyBaseAdapter(this, ViewHolder.class); } private void goGoGo() { listView.setAdapter(adapter); } private void getTheList() { for (int i = 0; i < 10; i++) { GoodsInfo goodsInfo = new GoodsInfo("商品9527:" + i, (int) (Math.random() * 10), R.mipmap.ic_launcher); goodsInfoList.add(goodsInfo); } adapter.setData(goodsInfoList); } @ListAdapterLayoutId(R.layout.list_view_item_goods) public static class ViewHolder extends MyBaseAdapter.BaseViewHolder{ @ListAdapterViewId(R.id.goods_pic) ImageView image; @ListAdapterViewId(R.id.goods_name) TextView name; @ListAdapterViewId(R.id.goods_price) TextView price; @ListAdapterViewId(R.id.goods_buy) Button buy; }}
MyBaseAdapter
/** * Created by 阿木木 * com.example.administrator.myapplication.adapter * 2016/3/19 17:11 */public class MyBaseAdapter<T> extends BaseAdapter { private Context context; private List<T> list; private Class<? extends BaseViewHolder> holderClass; public MyBaseAdapter(Context context, Class<? extends BaseViewHolder> holderClass) { this.context = context; this.list = new ArrayList<>(); this.holderClass = holderClass; } public void setData(List<T> list) { this.list.clear(); this.list.addAll(list); this.notifyDataSetChanged(); } public List<T> getData() { return this.list; } @Override public int getCount() { return list.size(); } @Override public T getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return inject(holderClass, position, convertView, parent); } private View inject(Class<? extends BaseViewHolder> clazz, int position, View convertView, ViewGroup parent) { BaseViewHolder holder; //用于存放Item布局中的View,key为控件ID,value为控件 Map<Integer, Object> holderFields = new HashMap<>(); try { //和基本写法相同 if (convertView == null) { //获取到ViewHolder上的注解 ListAdapterLayoutId viewLayoutId = clazz.getAnnotation(ListAdapterLayoutId.class); //如果获取到的注解不为空,即为“客户端已按要求给ViewHolder指定了它要加载的ItemLayout” if (viewLayoutId != null) { //获取需要加载的布局的ID int layoutId = viewLayoutId.value(); convertView = LayoutInflater.from(context).inflate(layoutId, parent, false); //实例化出ViewHolder的一个对象 holder = clazz.newInstance(); //获取ViewHolder对象中的所有成员属性 Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { //设置成员属性可修改 field.setAccessible(true); //找到这个成员属性给它的注解,对应为控件ID ListAdapterViewId viewIdAnnotation = field.getAnnotation(ListAdapterViewId.class); //如果给定的注解不为空 if (viewIdAnnotation != null) { //获取得到注解中指定的控件ID int viewId = viewIdAnnotation.value(); //在布局中找到此控件 View view = convertView.findViewById(viewId); if (view != null) { //找到此控件后,对ViewHolder的对象设置对应成员属性为找到的这个控件 field.set(holder, view); //将此控件放入集合中 holderFields.put(viewId, view); } else { throw new RuntimeException("ViewHolder类中的" + field.getName() + "注解的View Id无法找到"); } } } //和普通写法相同 convertView.setTag(holder); } else { throw new RuntimeException("传入的ViewHolder类没有注解指定布局ID"); } } else { holder = (BaseViewHolder) convertView.getTag(); }//再次获取此ViewHolder的所有成员属性,这一步是用来设置具体的值 for (Field field : clazz.getDeclaredFields()) { //获取对应成员属性的指定注解 ListAdapterViewId holderViewAnnotation = field.getAnnotation(ListAdapterViewId.class); //判断ViewHolder有没有给这个成员属性添加注解,添加了注解才能继续下一步 if (holderViewAnnotation != null) { //获取ListView中对应下标的此处的实体,再获取此实体类的成员变量,对Bean类的属性进行遍历 for (Field beanField : getItem(position).getClass().getDeclaredFields()) { //获取实体类中的成员属性的注解,需要这个注解和View的ID进行对比 ListAdapterViewId annotation = beanField.getAnnotation(ListAdapterViewId.class); //设置可修改Bean类对应的成员属性可修改 beanField.setAccessible(true); //随即判断是否添加了注解,若不注解,则表明“这个变量我不设置到ListView中” if (annotation != null) { //对Bean类的变量与ViewHolder的变量进行对比,如果它们都指向同一个View的ID,则表明它们是一一对应 if (annotation.value() == holderViewAnnotation.value()) { field.setAccessible(true); //field.getType(),获取到这个ViewHolder的成员属性,是什么类型, Class<?> type = field.getType(); if (type.getName().equals(TextView.class.getName())) { //如果是TextView再通过类型来找setText(CharSequence)这个方法 Method setText = type.getMethod("setText", CharSequence.class); //如果能找到setText(CharSequence)方法,表明确实是TextView,并判断对应的View有没有添加到布局中 if (setText != null && holderFields.get(annotation.value()) != null) { //直接执行setText setText.invoke(holderFields.get(annotation.value()), beanField.get(getItem(position))+""); } } //注,动态设置显示的只支持TextView及其子类。对于ImageView,在此不能有ImageLoader来对ImageView进行显示的操作 //这里只管做它该做的事,怎么显示由客户端使用相应的操作,要对其解藕 } } } } } } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); throw new RuntimeException("传入的ViewHolder类必须有无参数构造方法"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return convertView; } public static class BaseViewHolder { int index; }}
现在还有价格,我们并不能直接就显示1234,前面还有几个文字显示。
那么就不能直接使用这个BaseAdapter,还得定制一下。
public class MainActivity extends Activity { private ListView listView; private Adapter adapter; private List<GoodsInfo> goodsInfoList; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); goGoGo(); listView.postDelayed(new Runnable() { @Override public void run() { getTheList(); } }, 500); } private void initView() { listView = (ListView) findViewById(R.id.test_list_view); goodsInfoList = new ArrayList<>(); adapter = new Adapter(this, ViewHolder.class); } private void goGoGo() { listView.setAdapter(adapter); } private void getTheList() { for (int i = 0; i < 10; i++) { GoodsInfo goodsInfo = new GoodsInfo("商品9527:" + i, (int) (Math.random() * 10), R.mipmap.ic_launcher); goodsInfoList.add(goodsInfo); } adapter.setData(goodsInfoList); } private class Adapter extends MyBaseAdapter<GoodsInfo> { public Adapter(Context context, Class<? extends BaseViewHolder> holderClass) { super(context, holderClass); } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = super.getView(position, convertView, parent); ViewHolder holder = (ViewHolder) getHolderInstance(); holder.price.setText(getString(R.string.price_yuan, getItem(position).getGoodsPrice())); return view; } } @ListAdapterLayoutId(R.layout.list_view_item_goods) public static class ViewHolder extends MyBaseAdapter.BaseViewHolder { @ListAdapterViewId(R.id.goods_pic) ImageView image; @ListAdapterViewId(R.id.goods_name) TextView name; @ListAdapterViewId(R.id.goods_price) TextView price; @ListAdapterViewId(R.id.goods_buy) Button buy; }}
我们下期再来解决点击某个控件执行相应的方法。
- 使用注解来折腾BaseAdapter(1)
- 使用注解来配置MyBatis
- BaseAdapter使用
- BaseAdapter使用
- BaseAdapter使用
- BaseAdapter使用
- BaseAdapter使用
- BaseAdapter使用
- BaseAdapter 使用
- 开始折腾cocos2d-x,使用批处理来创建项目
- 又来折腾啦
- 使用注解来定义联合主键
- spring 使用注解来调度定时任务
- 使用Spring注解来注入属性
- 使用Spring注解来注入属性
- spring 使用注解来调度定时任务
- 使用注解来构造IoC容器
- 使用注解来构造IoC容器
- js---OOP浅谈
- 蓝桥杯_算法提高_最小方差生成树(Kruskal算法)
- Java通过JDBC访问数据库(两种方式)
- [Android进阶]Android消息机制
- 在Android Studio中使用Jni
- 使用注解来折腾BaseAdapter(1)
- 48.警告Pointer is missing a nullability type specifier
- 计算机视觉中的牛人贡献及其主页
- char数组转为 字符串
- 欢迎使用CSDN-markdown编辑器
- 推荐系统起步---0319
- 组合数取模
- c ++ 总结与备忘
- sqoop