Butterknife详解——自己实现编译时注解框架

ButterKnife注解框架相信大家都是用过,记得以前的老大老是说黄油刀,黄油刀的,那里面的实现原理是什么呢?其实内部原理比较简单, 简单的你看文本文也可以自己写

编译时注解实现

1,定义接口,成员变量的

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

这个injectView 接口的名字可以自己定义,但一般做到见名知意里面的@Target 等注解看统一专题里面的详解。都是注解的内容,里面的是生命周期等属性的限制
2.定义点击事件的接口

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int[] value();
}

3.写一个工具类也就是相当于 Butterknife.bind(this)效果:


/**
 * 文件描述:   代码注入工具类
 * Created by  xn069392
 * 创建时间    2018/8/2
 */

public class InjectViewUtils {

    /**
     * 代码注入方法,相当于Butterknife的 bind()
     *
     * @param activity
     */
    public static void inject(final Activity activity) {
        //反射拿到类对象
        Class clazz = activity.getClass();
        //这里我们需要的是所有的成员变量的参数
        Field[] Fileds = clazz.getDeclaredFields();
        //遍历集合
        for (int i = 0; i < Fileds.length; i++) {
            //获取每一个成员变量
            Field filed = Fileds[i];
            // 如果私有 暴力反射
            filed.setAccessible(true);
            //获取到成员变量的注解,传入参数就是之前我们定义的接口的类对象
            InjectView inject = filed.getAnnotation(InjectView.class);
            if (inject != null) {
                //我们的接口中定义了一个int  类型的value 值  ,获取到value 也就是我们成员变量的id
                int id = inject.value();
                //通过这个ID,也是需要在传入Activity中去找到对应view的
                View view = activity.findViewById(id);
                //成员变量找到了,这个要去设置
                try {
                    filed.set(activity, view);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "inject: 设置成员变量失败!");
                    e.printStackTrace();
                }

            }
        }

        //处理点击事件
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            method.setAccessible(true);
            OnClick onClick = method.getAnnotation(OnClick.class);
            //为什么是一个数组呢,因为我们的点击事件 有事后就写一个方法,多个id .
            int[] value = onClick.value();
            for (int j = 0; j < value.length; j++) {
                int idValue = value[j];
                final View viewById = activity.findViewById(idValue);
                viewById.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        try {
                            method.invoke(activity, viewById);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
}
上面的代码主要是用到暴力反射,

4.在主方法中调用

public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.button1)
    Button  mButton;
    @InjectView(R.id.button2)
    Button  mButton2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectViewUtils.inject(this);
        mButton.setText("我是第一个button");
    }

    @OnClick({R.id.button1,R.id.button2})
    private void  XXX(View view ){
        switch (view.getId()){
            case R.id.button1:
                Toast.makeText(this,"第一个按钮的手动编译时注解",Toast.LENGTH_LONG).show();
                break;
            case R.id.button2:
                Toast.makeText(this,"第222个按钮的手动编译时注解,主要用到反射",Toast.LENGTH_LONG).show();
                break;
        }
    }
}package com.xiaoniu.finance.butterknifesimple;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static android.content.ContentValues.TAG;

/**
 * 文件描述:   代码注入工具类
 * Created by  xn069392
 * 创建时间    2018/8/2
 */

public class InjectViewUtils {

    /**
     * 代码注入方法,相当于Butterknife的 bind()
     *
     * @param activity
     */
    public static void inject(final Activity activity) {
        //反射拿到类对象
        Class clazz = activity.getClass();
        //这里我们需要的是所有的成员变量的参数
        Field[] Fileds = clazz.getDeclaredFields();
        //遍历集合
        for (int i = 0; i < Fileds.length; i++) {
            //获取每一个成员变量
            Field filed = Fileds[i];
            // 如果私有 暴力反射
            filed.setAccessible(true);
            //获取到成员变量的注解,传入参数就是之前我们定义的接口的类对象
            InjectView inject = filed.getAnnotation(InjectView.class);
            if (inject != null) {
                //我们的接口中定义了一个int  类型的value 值  ,获取到value 也就是我们成员变量的id
                int id = inject.value();
                //通过这个ID,也是需要在传入Activity中去找到对应view的
                View view = activity.findViewById(id);
                //成员变量找到了,这个要去设置
                try {
                    filed.set(activity, view);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "inject: 设置成员变量失败!");
                    e.printStackTrace();
                }

            }
        }

        //处理点击事件
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            method.setAccessible(true);
            OnClick onClick = method.getAnnotation(OnClick.class);
            //为什么是一个数组呢,因为我们的点击事件 有事后就写一个方法,多个id .
            //这里有的方法没有注解,必须做空判断
            if(onClick !=null){
                int[] value = onClick.value();
                for (int j = 0; j < value.length; j++) {
                    int idValue = value[j];
                    final View viewById = activity.findViewById(idValue);
                    viewById.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(activity, viewById);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }

        }
    }
}

5.效果的实现

Butterknife详解——自己实现编译时注解框架_第1张图片
inject.gif

6.源码地址:
https://github.com/zh2016hz/ButterknifeSimple.git

你可能感兴趣的:(Butterknife详解——自己实现编译时注解框架)