美文网首页框架【库】
手写ButterKnife框架

手写ButterKnife框架

作者: 农田蚂蚁 | 来源:发表于2019-08-06 17:34 被阅读18次

    引入

    ButterKnife是一个专注于Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤。是大神JakeWharton的力作,目前使用很广。项目集成起来也是特别方便,使用起来也是特别简单。
    ButterKnife可以做很多事:绑定View(@BindView);绑定资源(@BindString);绑定监听(@OnClick()等等,不过感觉项目里用得比较频繁的应该也就这几个了,今天我们就来花五分钟的时间,自己动手写一个,不用ButterKnife,轻松搞定注解。
    注解和反射密不可分,注解的实现依赖于反射。

    首先使用注解的方式,实例化View为例,实现一个类似ButterKnife的@BindView的注解:

    1. 新建一个注解BindView

    微信图片_20190806162449.png
    @Retention(RetentionPolicy.RUNTIME)//CLASS 编译时注解  RUNTIME运行时注解 SOURCE 源码注解
    @Target(ElementType.FIELD)//注解作用范围:FIELD 属性  METHOD方法  TYPE 放在类上
    public @interface BindView {  //@interface则是表明这个类是一个注解
        int value(); //表示@BindView() 注解时,括号里面的编写的为int类型的值
    }
    

    2. 新建一个ButterKnife的类,通过反射来给注解的变量创建实例

    public class ButterKnife {
    
        public static void inJect(Activity activity) {
            findViewById(activity);
        }
    
        private static void findViewById(Activity activity) {
            //获取Activity的class
            Class<? extends Activity> clazz = activity.getClass();
            //获取该类中的所有声明的属性
            Field[] declaredFields = clazz.getDeclaredFields();
            //遍历所有属性,找到用@ViewById注解了的属性
            for (int i = 0; i < declaredFields.length; i++) {
                Field field = declaredFields[i];
                //获取属性上的注解对象
    //            @ViewById(R.id.textView) R.id.textView--value
    //            TextView textView;//属性
                BindView annotation = field.getAnnotation(BindView.class);
                if (annotation != null) {
                    int viewId = annotation.value();
                    View view = activity.findViewById(viewId);
                    try {
                        //私有属性也可以动态注入(不写该句代码,private声明的属性会报异常)
                        field.setAccessible(true);
                        field.set(activity, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    3. 在Activity中使用

    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.textView)
        TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.inJect(this);
            textView.setText("我是手写ButterKnife框架");
        }
    }
    
    

    实现事件绑定的注解

    1. 新建一个OnClick注解

    @Target(ElementType.METHOD)//注解作用范围:FIELD 属性  METHOD方法  TYPE 放在类上
    @Retention(RetentionPolicy.RUNTIME)//CLASS 编译时注解  RUNTIME运行时注解 SOURCE 源码注解
    public @interface OnClick {
        int[] value();
    }
    

    2. 在前面的ButterKnife类中添加方法,并在inJect()方法中调用setOnClickListener()

    private static void setOnClickListener(final Activity activity) {
            // findViewById  setOnClick
            // 1.获取该Activity的所有方法
            Class<?> clazz = activity.getClass();
            try {
                final Method method = clazz.getMethod("onClick", View.class);
                OnClick onClick = method.getAnnotation(OnClick.class);
                // 2.2 该方法上是否有OnClick注解
                if (onClick != null) {
                    // 2.3 获取OnClick里面所有的值
                    int[] viewIds = onClick.value();// @OnClick({R.id.text_view,R.id.button})
    
                    // 2.4 先findViewById , setOnclick
                    for (int viewId : viewIds) {
                        // 先findViewById
                        final View view = activity.findViewById(viewId);
                        // 后设置setOnclick
                        view.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                // 首先需要判断 方法是否需要检测网络
                                // 3.反射调用原来配置了OnClick的方法
                                method.setAccessible(true);// 私有的方法
    
                                try {
                                    method.invoke(activity);// 调用无参的方法
                                } catch (Exception e) {
                                    e.printStackTrace();
                                    try {
                                        method.invoke(activity, view);// 调用有参的方法 view 代表当前点击的View
                                    } catch (Exception e1) {
                                        e1.printStackTrace();
                                    }
                                }
                            }
                        });
                    }
    //            }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
    

    3. 在Activity中使用

    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.textView)
        TextView textView;
    
        @OnClick({R.id.button})  //可注入多个view
        public void onClick(View view){
            switch (view.getId()){
                case R.id.button:
                    textView.setText("我是手写ButterKnife框架");
                    break;
                default:
                    Log.e("MainActivity","view not found");
                    break;
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.inJect(this);
        }
    }
    

    主布局注入的实现

    1. 新建一个ContentView 注解

    @Target(ElementType.TYPE)//注解作用范围:FIELD 属性  METHOD方法  TYPE 放在类上
    @Retention(RetentionPolicy.RUNTIME)//CLASS 编译时注解  RUNTIME运行时注解 SOURCE 源码注解
    public @interface ContentView {
        int value();
    }
    

    2. 布局注入的具体实现

        private static void injectContentView(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            ContentView contentView = clazz.getAnnotation(ContentView.class);
            if (contentView != null){// 存在
                int contentViewLayoutId = contentView.value();
                try {
                    Method method = clazz.getMethod("setContentView", int.class);
                    method.setAccessible(true);
                    method.invoke(activity, contentViewLayoutId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    

    3. 在Activity中使用

    @ContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    //        setContentView(R.layout.activity_main);   不再需要setContentView了  因为注解已经帮我们完成了
            ButterKnife.inJect(this);
        }
    }
    

    项目地址:https://github.com/belong571/Simple-ButterKnife

    集成使用:

    添加依赖

    implementation 'com.github.belong571:Simple-ButterKnife:v1.0.0'
    

    简单使用

    @ContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.textView)
        TextView textView;
    
        @OnClick({R.id.button})  //可注入多个view  {R.id.button,R.id.textView}
        public void onClick(View view){
            switch (view.getId()){
                case R.id.button:
                    textView.setText("我是手写ButterKnife框架");
                    break;
                default:
                    Log.e("MainActivity","view not found");
                    break;
            }
        }
    
        @OnLongClick({R.id.button})  //可注入多个view  {R.id.button,R.id.textView}
        public void onLongClick(View view){
            switch (view.getId()){
                case R.id.button:
                    textView.setText("button  长按了");
                    break;
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    //        setContentView(R.layout.activity_main);   不再需要setContentView了  因为注解已经帮我们完成了
            ButterKnife.inJect(this);
        }
    }
    

    相关文章

      网友评论

        本文标题:手写ButterKnife框架

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