[置顶] View注解框架简单学习(绑定对象,绑定事件)
2016-11-21 13:38
417 查看
View注解框架简单学习(绑定对象,绑定事件)
转载请说明出处:http://blog.csdn.net/a15286856575/article/details/53258696前言
现在有许多针对View的注解框架,例如Xutils的ViewUtis,解决了令我们厌烦的findViewById对象的初始化,以及绑定OnClick事件的问题,提高了开发效率,那么怎么实现的呢?我们来简单学习一下,自己写个框架,此种方法是运行时的注解,而Buttknife是编译时期的注解.绑定View对象
自定义针对View的注解
对注解不熟悉的同学可以参考注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface InjectView { int value(); }
该注解修饰字段,里面有value属性,返回值为int用法如下:
@InjectView(R.id.text) TextView textView;
针对view的注解解释器
上面我们定义了InjectView注解,但是并没有对注解进行解释,从而进行初始化的操作,所以我们要写注解解释器完成初始化工作。/** * 对于注解字段的,是要实现字段的赋值,怎么复制呢? * 1.实例化对象 * 1.1 通过拿字段的注解我们能够拿到id * 1.2 findviewById() * 2.对象复制 * 2.1 declaredField.set(activity,viewById); * * @param activity */ public static void injects(Activity activity) { Class<? extends Activity> aClass = activity.getClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); if (declaredField.isAnnotationPresent(InjectView.class)) { declaredField.setAccessible(true); InjectView annotation = declaredField.getAnnotation(InjectView.class); int value = annotation.value(); View viewById = activity.findViewById(value);//当然也可以是反射 try { declaredField.set(activity, viewById); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
分析:我们要解决findViewById(int id),我们首先需要什么
1. 首先是该方法的对象是Activity,那么我们就需要传入一个对象activity,然后调用该方法。
2. 我们还需要资源id,怎么拿id呢,还记得我们的注解吗?我们就是通过I注解拿到Id,怎么拿注解的,我们发现该注解修饰的字段,我们就拿字段,通过遍历字段,拿到相应的id,然后调用
activity的findViewById()方法,拿到View对象。
3. 赋值 我们可以通过Field 的set方法进行赋值
调用
injects(this);
在onCreate方法中调用该方法。也就是说我们初始化是在这个方法中实现的。
绑定事件
针对事件的注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface OnClick { int[] value(); }
事件一般是方法,那么我们就需要注解方法的注解,又因为点击事件可能有好多view,所以我们需要返回值是个数组,那么我们就可以根据这个注解,拿到相应View的id;
用法如下:
@OnClick({R.id.bt_1,R.id.bt_2}) public void showOnclick(View view) { switch (view.getId()) { case R.id.bt_1: Log.e("自定义标签", "onClick: bt1"); break; case R.id.bt_2: Log.e("自定义标签", "onClick: bt2 "); break; } }
对事件注解的解释器
不用动态代理/** * 注解事件 * 原理:我们知道点击事件的监听.是通过 view.setOnclickListener(new onClickListener())的方法实现的,问题是我们怎么调用 * 注解事件的方法 * 1.拿到对象View * 2.实现点击事件 * * @param activity */ private static void injectOnlick(final Activity activity) { Class<? extends Activity> aClass = activity.getClass(); Method[] declaredMethods = aClass.getDeclaredMethods(); for (final Method declaredMethod : declaredMethods) { { OnClick annotation = declaredMethod.getAnnotation(OnClick.class); if (annotation != null) { // 我们拿到带注解的方法,从而拿到value,然后遍历所有的id,拿到对应的View int[] value = annotation.value(); for (int j : value) { final View viewById = activity.findViewById(j); viewById.setOnClickListener(new View.OnClickListener() { private Method method; @Override public void onClick(View v) { try { declaredMethod.invoke(activity, v); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }); } } } } }
思路:我们绑定点击事件,必定要调用view.setOnClickListener(new onClickListener())方法,那我们需要什么呢?
* view
view怎么拿?我们的注解是注解方法的,注解里面有我们需要的值,那么,我们首先拿注解,通过遍历方法,我们拿到带注解的方法,从而拿到value,然后遍历所有的id,拿到对应的View
* 重写OnClick事件,调用我们注解的方法showOnclick();
我们在OnClick事件中通过重写OnClick事件,调用调用注解的method方法,实现了showOnclick()的调用,也就是反射declaredMethod.invoke(activity, v);
动态代理View.OnClickListener对象,实现对OnClick事件的重写。
对于不懂动态代理的同学,请参考:
动态代理
//动态代理实现方式 /** * 1.拿到View对象 * 2.监听对象我们可以用动态代理实现.通过代理View.OnClickListener对象,当触发onClick事件的时候调用InjectInvocationHandler的Invoke方法,从而我们可以再Invoke方法中调用 * 我们注解的方法 * * @param activity */ private static void InjectProxyOnClick(Activity activity) { Class<? extends Activity> aClass = activity.getClass(); Method[] declaredMethods = aClass.getDeclaredMethods(); for (Method declaredMethodd : declaredMethods) { //1.拿到带注解的onclick事件,并取得先关值 OnClick aOnClick = declaredMethodd.getAnnotation(OnClick.class); if (aOnClick != null) { int[] ids = (int[]) aOnClick.value(); //2.生成一个Onclick的代理类 InjectInvocationHandler handler = new InjectInvocationHandler(activity); handler.add("onClick", declaredMethodd); ** 通过代理方式实现了一个View.OnclickListener对象,** Object o = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(), new Class[]{View.OnClickListener.class}, handler); for (int id : ids) { //3.拿到对应的控件 View view = activity.findViewById(id); try { //4.通过控件拿到方法 Method listener = view.getClass().getMethod("setOnClickListener", View.OnClickListener.class); listener.setAccessible(true); //5方法调用 listener.invoke(view, o); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
public class InjectInvocationHandler implements InvocationHandler { // 拦截的方法名列表,里面存的就是我们注解的方法名字,关键字是OnClick private Map<String, Method> map = new HashMap<>(); // 在这里实际上是MainActivity private Object target; public InjectInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { if (target != null) { // 获取方法名,因为我们是要截获OnClick方法,所以我们要拿到OnClick方法 String name = method.getName(); Log.e("自定义标签", "invoke: " + method.getName()); Method m = map.get(name); //如果method的方法名字是onClick,我们就能取得我们注解的方法,从而直接调用 Log.e("自定义标签", "invoke: activity" + m.getName()); //其实调用的是Activity的加了Onclick的方法 if (m != null) { return m.invoke(target, args); } } return null; } /** * 向拦截列表里添加拦截的方法 */ public void add(String name, Method method) { map.put(name, method); } }
View
参考不用代理的方式,实现一样
重写OnClick事件,调用我们注解的方法showOnclick();
上面的方法是直接是new onClickListener();而这种是通过代理方式实现了一个View.OnclickListener对象,也就是
Object o = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(),new Class[]{View.OnClickListener.class},handler),
这个O就是View.OnclickListener代理对象,也就是说View.OnclickListener对象的所有方法都会被我们的代理对象代理,从而回调用
public Object invoke(Object proxy, Method method, Object[] args)
那么我们只需要在这里拦截Onclick方法就行了,InjectInvocationHandler里面有个map集合,我们在初始化该对象的时候
InjectInvocationHandler handler = new InjectInvocationHandler(activity); handler.add("onClick", declaredMethodd);
添加了一个关键字是OnClick ,值是对应的注解方法也就是showOnclick()方法,在invoke方法中里面有个参数method,上面我们说了,我们的o会代理View.OnclickListener的所有方法,那么method就要其中的一个方法OnClick方法,我们通过
String name = method.getName(); Method m = map.get(name);
是不是拿到了map里面我们存放的我们要调用的方法也就是showOnclick(View view);那我们通过反射方法调用就可以了
m.invoke(target, args)
也就实现了我们调用我们注解方法showOnclick(View view)的功能。
绑定事件调用
InjectProxyOnClick(this);
也就是说我们在这个方法中相当于实现了view.SetonClickListener()方法,重写了OnClick()方法,调用了我们注解的方法。这也就讲完了。
源码传送
相关文章推荐
- Android:开源框架xutils介绍之 ViewUtils(注解实现UI绑定和事件绑定)
- 自己动手写一个简单的IOC框架,使用注解绑定资源和事件
- [置顶] 三方网络框架学习Xutils3.3.4补充view绑定和数据库操作
- 简单的二级城市联动 对同一个对象绑定多个事件
- 用注解实现简单的框架 避免频繁调用FindViewById
- Spring.NET依赖注入框架学习--简单对象注入
- Android中的IOC框架,完全注解方式就可以进行UI绑定和事件绑定
- Android网络框架xUtils中的View的视图绑定注解操作
- springmvc学习笔记(11)-springmvc注解开发之简单参数绑定
- Backbone.js框架中简单的View视图编写学习笔记
- Android IOC 之 注解绑定控件 以及事件 事例 代码简单注释讲解
- [原创]java WEB学习笔记48:其他的Servlet 监听器:域对象中属性的变更的事件监听器 (3 个),感知 Session 绑定的事件监听器(2个)
- Backbone.js框架中简单的View视图编写学习笔记
- 对于对象的简单验证及返回处理——@JsonView ,@Valid注解的使用
- javascript学习(十二):js 中为某个对象(控件)绑定事件通常可以采取两种手段
- QGraphicsView 框架学习(三),图形对象的剪贴板操作。
- springmvc学习笔记(11)-springmvc注解开发之简单参数绑定
- 【转】Android中的IOC框架,完全注解方式就可以进行UI绑定和事件绑定
- Android开发框架xUtils3.x新手教学(三)使用注解绑定UI、资源和事件
- [置顶] Android RecyclerView简单请求数据框架