美文网首页
Butterknife详解——自己实现编译时注解框架

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

作者: gogoingmonkey | 来源:发表于2018-08-02 11:49 被阅读11次

    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<? extends Activity> 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<? extends Activity> 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.效果的实现

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

    相关文章

      网友评论

          本文标题:Butterknife详解——自己实现编译时注解框架

          本文链接:https://www.haomeiwen.com/subject/dgjjvftx.html