浅谈android IOC控制反转二(事件篇)

上一篇博主已经带大家简单的了解了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;

/**
 * <p>描述:<p>
 *
 * @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;


/**
 * <p>描述:<p>
 *
 * @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;

/**
 * <p>描述:<p>
 *
 * @author guoweiquan
 * @version 1.0
 * @data 2018/5/15 上午11:28
 */


public class DynamicHandler implements InvocationHandler {
    private Object handler;
    private final HashMap<String, Method> methodMap = new HashMap<String, 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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值