上一篇博主已经带大家简单的了解了IoC,实现了Activity中View的布局以及控件的注入,如果你不了解,请参考:浅谈android ioc控制反转一(控件篇)

本篇文章主要讲解事件注入

目标效果(继上篇文章代码下)

package shopping.com.androidioc;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.TextView;import android.widget.Toast;@SetContentView(R.layout.activity_main)public class MainActivity extends AppCompatActivity {    @ViewBind(R.id.tv)    private TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        ViewInjectUtils.inject(this);    }    @OnClick({R.id.tv,R.id.btn})    public void click(View view)    {        switch (view.getId())        {            case R.id.tv:                Toast.makeText(MainActivity.this,"我是textview被点击了",Toast.LENGTH_LONG).show();                break;            case R.id.btn:                Toast.makeText(MainActivity.this,"我btn被点击了",Toast.LENGTH_LONG).show();                break;        }    }}

从上面代码可以看出事件绑定的关键代码是在 @OnClick({R.id.tv,R.id.btn}) 这个注解中

好,下面先来看下OnClick注解类

package shopping.com.androidioc;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 

描述:

* * @author guoweiquan * @version 1.0 * @data 2018/5/15 上午10:41 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface OnClick{ int[] value();}

这个类和之前的注解不同的是 @Target(ElementType.METHOD) ,ElementType.METHOD意思是一个方法注解。

然后还是来看核心实现类ViewInjectUtils:

package shopping.com.androidioc;import android.app.Activity;import android.view.View;import java.lang.annotation.Annotation;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 

描述:

* * @author guoweiquan * @version 1.0 * @data 2018/5/14 下午2:19 */public class ViewInjectUtils { private static final String SETCONTENTVIEW = "setContentView"; private static final String FINDVIEWBYID = "findViewById"; private static final String EVENTMETHOD = "setOnClickListener"; private static final Class EVENTTYPE = View.OnClickListener.class; private static final String METHODNAME = "onClick"; public static void inject(Activity activity) { bindContentView(activity); bindViews(activity); bindEvents(activity); } /** * 绑定主布局文件 * * @param activity */ private static void bindContentView(Activity activity) { Class<? extends Activity> clazz = activity.getClass(); SetContentView contentView = clazz.getAnnotation(SetContentView.class);// 查询类上SetContentView注解 if (contentView != null) { int contentViewLayoutId = contentView.value(); try { Method method = clazz.getMethod(SETCONTENTVIEW, int.class); method.setAccessible(true);//设置可以访问private域 method.invoke(activity, contentViewLayoutId); } catch (Exception e) { e.printStackTrace(); } } } /** * 绑定所有的控件 * * @param activity */ private static void bindViews(Activity activity) { Class<? extends Activity> clazz = activity.getClass(); Field[] fields = clazz.getDeclaredFields();//获取自己声明的各种字段,包括public,protected,private for (Field field : fields) { ViewBind viewInjectAnnotation = field .getAnnotation(ViewBind.class); if (viewInjectAnnotation != null) { int viewId = viewInjectAnnotation.value(); if (viewId != -1) { try { Method method = clazz.getMethod(FINDVIEWBYID, int.class); Object view = method.invoke(activity, viewId);//找到相应控件对象 field.setAccessible(true); field.set(activity, view);//给本字段赋值 } catch (Exception e) { e.printStackTrace(); } } } } } /** * 绑定所有的事件 * * @param activity */ private static void bindEvents(Activity activity) { Class<? extends Activity> clazz = activity.getClass(); Method[] methods = clazz.getMethods(); //遍历所有的方法 for (Method method : methods) { //拿到方法上的所有的注解 Annotation[] annotations = method.getAnnotations(); for (Annotation annotation : annotations) { //获取注解类型 OnClick Class<? extends Annotation> annotationType = annotation .annotationType(); //判断是否为OnClick注解 if(annotationType.getName().equals(OnClick.class.getName())) { try { //拿到Onclick注解中的value方法 Method method1 = annotationType .getDeclaredMethod("value"); //取出所有的viewId int[] viewIds = (int[]) method1.invoke(annotation); //设置动态代理 DynamicHandler handler = new DynamicHandler(activity); handler.addMethod(METHODNAME, method);//添加onClick方法 Object listener = Proxy.newProxyInstance( EVENTTYPE.getClassLoader(), new Class<?>[] { EVENTTYPE }, handler);// BindListener listener = new BindListener(activity);// listener.setmMethod(method); //遍历所有的View,设置事件 for (int viewId : viewIds) { View view = activity.findViewById(viewId); Method setEventListenerMethod = view.getClass().getMethod(EVENTMETHOD, EVENTTYPE); setEventListenerMethod.invoke(view, listener); } } catch (Exception e) { e.printStackTrace(); } } } } }}

事件绑定的方法是bindEvents。这个方法主要就是遍历activity所有的方法,过滤拿到注释为OnClick的方法,得到事件监听的需要调用的方法名,通过Proxy和InvocationHandler得到监听器的代理对象,显示设置了方法,最后通过反射设置监听器。

这里有个难点,就是关于DynamicHandler和Proxy的出现,如果不理解没事,注释掉动态代理这一段,打开BindListener这段好了实现效果一样。

DynamicHandler代码如下:

package shopping.com.androidioc;import java.lang.ref.WeakReference;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.HashMap;/** * 

描述:

* * @author guoweiquan * @version 1.0 * @data 2018/5/15 上午11:28 */public class DynamicHandler implements InvocationHandler { private Object handler; private final HashMap, Method> methodMap = new HashMap, Method>(); public DynamicHandler(Object handler) { this.handler = handler; } public void addMethod(String name, Method method) { methodMap.put(name, method); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (handler != null) { String methodName = method.getName(); method = methodMap.get(methodName); if (method != null) { return method.invoke(handler, args); } } return null; }}


好了到这里我们已经实现了事件绑定效果:


项目地址:https://github.com/seaeel/AndroidIoc.git

博主技术qq交流群:239025382


更多相关文章

  1. Android(安卓)ART Hook & 注入实现细节
  2. android四种点击事件触发写法
  3. 修改Android模拟器的system分区,以及加入SuperSU
  4. Android(安卓)NDK用法
  5. Android视频播放时停止后台运行的方法
  6. 帅气的属性动画赏析(Property Animation)
  7. Android(安卓)6.0 Marshmallow root 方法
  8. Android使用ListView批量删除item的方法
  9. [置顶] Android的AlertDialog详解

随机推荐

  1. Android让所有应用都竖屏显示或者横屏显
  2. unity 与 android 协调工作 注意事项
  3. android基础(开发环境搭建)
  4. 在 Android(安卓)Studio 上调试数据库 (
  5. Android Studio 启用Lambda表达式
  6. android SDK编译版本修改
  7. Android开发小技巧1
  8. Android: TextView with border 带边框的
  9. android studio常见错误及异常处理记录
  10. android连接网络