android架构师之路——IOC原理,简单实现BufferKnife

简介

 

Inversion of Control,英文缩写为IOC,字面翻译:控制反转。什么意思呢?就是一个类里面需要用到很多个成员变量,传统的写法,你要用这些成员变量,那么你就new 出来用呗!IOC的原则是:NO,我们不要new,这样耦合度太高,你配置个xml文件,里面标明哪个类,里面用了哪些成员变量,等待加载这个类的时候,我帮你注入(new)进去。具体可以参考https://www.jianshu.com/p/3968ffabdf9d

我们这里要简单实现一个BufferKnife

  • 实现setContentView功能
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.init(this);
    }

}

ContentView代码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView {
    int value();
}

InjectUtils中的initLayout方法

    /**
     * 设置布局文件
     *
     * @param context
     */
    private static void initLayout(Object context) {
        int layoutId = 0;
        Class<?> aClass = context.getClass();
        ContentView contentView = aClass.getAnnotation(ContentView.class);
        if (null != contentView) {
            layoutId = contentView.value();
            try {
                Method method = context.getClass().getMethod("setContentView", int.class);
                method.invoke(context, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

这样就实现了一个布局

 

  • 实现findViewById布局文件 

 


    @ViewInject(R.id.text)
    TextView textView;

 ViewInject 文件

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
    int value();
}

InjectUtils中的initView方法

    /**
     * 控件
     *
     * @param context
     */
    private static void initView(Object context) {
        Class<?> aClass = context.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            ViewInject annotation = declaredField.getAnnotation(ViewInject.class);
            if (null != annotation) {
                int value = annotation.value();
                try {
                    Method method = aClass.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(context, value);
                    declaredField.setAccessible(true);
                    declaredField.set(context, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

这样就实现了TextView textView = findViewById(R.id.text)功能

  • 给textview设置点击事件

我们要实现这个方法

//        textView.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                Toast.makeText(MainActivity.this, "被点击", Toast.LENGTH_SHORT).show();
//            }
//        });

mainActivity中的方法

 @OnClick({R.id.text})
    public void Myclick(View view) {
        Toast.makeText(MainActivity.this, "被点击", Toast.LENGTH_SHORT).show();
    }

 OnClick 注解实现

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick")
public @interface OnClick {
    int[] value() default  -1;
}

 EventBase 注解实现,这里是给注解添加注解,用来区别点击事件

@Retention(RetentionPolicy.RUNTIME)
//该注解在另外一个注解上使用
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
    //  setOnClickListener  订阅
    String  listenerSetter();

//    事件以及他的类型
    /**
     * 事件监听的类型
     * @return
     */
    Class<?> listenerType();

    /**
     * 事件处理
     * @return
     */
    String callbackMethod();
}

 inittClick实现方式

/**
     * 点击事件
     *
     * @param context
     */
    private static void inittClick(Object context) {
        Class<?> aClass = context.getClass();
        //得到所有的方法
        Method[] methods = aClass.getMethods();
        //Myclick 方法
        for (Method method : methods) {
            Log.d(TAG, "--method--" + method.getName());
            //得到方法上的所有注解
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //interface com.zkq.myioc.animation.OnClick
                Class<?> annotionClass = annotation.annotationType();
                Log.e(TAG, "--annotionClass--" + annotionClass);

                EventBase eventBase = annotionClass.getAnnotation(EventBase.class);
                if (null == eventBase) {
                    continue;
                }

                Log.e(TAG, "--annotation--" + annotation.toString() + "---" + annotionClass);
                //开始获取事件处理的相关信息,
                // 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理
                //订阅
                String listenerSetter = eventBase.listenerSetter();
                //事件(事件监听的类型)
                Class<?> listenerType = eventBase.listenerType();
                //事件处理   事件被触发之后,执行的回调方法的名称
                String callBackMethod = eventBase.callbackMethod();


                Method valueMethod = null;

                try {
                    valueMethod = annotionClass.getDeclaredMethod("value");
                    int[] viewId= (int[]) valueMethod.invoke(annotation);
                    for (int id : viewId) {
                        Log.d("zkq","id--"+id);
                        Method findViewById=aClass.getMethod("findViewById",int.class);
                        View view= (View) findViewById.invoke(context,id);
                        if(view==null)
                        {
                            continue;
                        }
                        //得到ID对应的VIEW以后
                        //开始在这个VIEW上执行监听  (使用动态代理)
                        //需要执行activity上的onClick方法
                        //activity==context       click==method
                        ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(context, method);
                        //proxy======View.OnClickListener()对象
                        Object proxy= Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
                        //执行方法                                   setOnClickListener,new View.OnClickListener()
                        Method onClickMethod = view.getClass().getMethod(listenerSetter, listenerType);
                        onClickMethod.invoke(view, proxy);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        }
    }

 ListenerInvocationHandler实现方式,实现动态代理

public class ListenerInvocationHandler implements InvocationHandler {
    private Object activity;
    private  Method activityMethod;

    public ListenerInvocationHandler(Object activity, Method activityMethod) {
        this.activity = activity;
        this.activityMethod = activityMethod;
    }
    /**
     *按钮点下去就执行这个方法
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return activityMethod.invoke(activity,args);
    }
}

这样就实现了点击事件

总结

这里讲解一下EventBase这个注解的注解

如果我们想要实现

//        textView.setOnLongClickListener(new View.OnLongClickListener() {
//            @Override
//            public boolean onLongClick(View v) {
//                return false;
//            }
//        });

 我们就只需要将

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick")
public @interface OnClick {
    int[] value() default  -1;
}

 中的EventBase修改成如下,和setOnLongClickListener中的方法相对应,就行了

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnLongClickListener",listenerType = View.OnLongClickListener.class,callbackMethod = "onLongClick")
public @interface OnLongClick {
    int[] value() default  -1;
}

在mainActivity中调用

    @OnLongClick({R.id.text_long})
    public boolean MyLongClick(View view){
        Toast.makeText(MainActivity.this, "长按被点击", Toast.LENGTH_SHORT).show();
        return  true;
    }

demo下载地址demo下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值