美文网首页安卓开发博客
使用反射和动态代理实现一个View注解绑定库

使用反射和动态代理实现一个View注解绑定库

作者: 我爱田Hebe | 来源:发表于2022-05-12 17:20 被阅读0次

    使用反射结合动态代理实现一个View注解绑定库,支持View和事件绑定,代码简洁,使用简单,扩展性强。

    支持的功能

    • @ContentView 绑定layout 替代setContentView()
    • @BindView 绑定View 替代findViewById()
    • @OnClick 绑定点击事件 替代setOnClickListener()
    • @OnLongClick 绑定长按事件 替代setOnLongClickListener()

    代码

    注解类

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ContentView {
        int value();
    }
    
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BindView {
        int value();
    }
    
    
    @Target(ElementType.ANNOTATION_TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OnEvent {
    
        //订阅方式
        String setCommonListener();
    
        //事件源对象
        Class<?> commonListener();
    
    }
    
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @OnEvent(setCommonListener = "setOnClickListener",
            commonListener = View.OnClickListener.class)
    public @interface OnClick {
        int value();
    }
    
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @OnEvent(setCommonListener = "setOnLongClickListener",
            commonListener = View.OnLongClickListener.class)
    public @interface OnLongClick {
        int value();
    }
    
    

    实现类

    public class MsInjector {
    
        public static void inject(Object object) {
            injectContentView(object);
            injectView(object);
            injectEvent(object);
        }
    
        private static void injectContentView(Object object) {
            Class<?> clazz = object.getClass();
            //获取到ContentView注解
            ContentView contentView = clazz.getAnnotation(ContentView.class);
    
            if (contentView == null) {
                return;
            }
    
            //获取到注解的值,也就是layoutResID
            int layoutResID = contentView.value();
    
            try {
                //反射出setContentView方法并调用
                Method method = clazz.getMethod("setContentView", int.class);
                method.invoke(object, layoutResID);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private static void injectView(Object object) {
            Class<?> clazz = object.getClass();
    
            //获取到所有字段并遍历
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                //获取字段上的BindView注解
                BindView bindView = field.getAnnotation(BindView.class);
    
                if (bindView == null) {
                    continue;
                }
    
                //获取到viewId
                int viewId = bindView.value();
    
                try {
                    //通过反射调用findViewById得到view实例对象
                    Method method = clazz.getMethod("findViewById", int.class);
                    Object view = method.invoke(object, viewId);
                    //赋值给注解标注的对应字段
                    field.set(object, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static void injectEvent(Object object) {
            Class<?> clazz = object.getClass();
    
            //获取到当前页年所有方法并遍历
            Method[] declaredMethods = clazz.getDeclaredMethods();
            for (Method declaredMethod : declaredMethods) {
                declaredMethod.setAccessible(true);
    
                //获取方法上的所有注解并遍历
                Annotation[] annotations = declaredMethod.getDeclaredAnnotations();
                for (Annotation annotation : annotations) {
    
                    //获取注解本身
                    Class<? extends Annotation> annotationType = annotation.annotationType();
                    //获取注解上的OnEvent注解
                    OnEvent onEvent = annotationType.getAnnotation(OnEvent.class);
    
                    if (onEvent == null) {
                        continue;
                    }
    
                    //拿到注解中的元素
                    String setCommonListener = onEvent.setCommonListener();
                    Class<?> commonListener = onEvent.commonListener();
    
                    try {
                        //由于上边没有明确获取是哪个注解,所以这里需要使用反射获取viewId
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        valueMethod.setAccessible(true);
                        int viewId = (int) valueMethod.invoke(annotation);
    
                        //通过反射findViewById获取到对应的view
                        Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
                        Object view = findViewByIdMethod.invoke(object, viewId);
    
                        //通过反射获取到view中对应的setCommonListener方法
                        Method viewMethod = view.getClass().getMethod(setCommonListener, commonListener);
    
                        //使用动态代理监听回调
                        Object proxy = Proxy.newProxyInstance(
                                clazz.getClassLoader(),
                                new Class[]{commonListener},
                                new InvocationHandler() {
                                    @Override
                                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                        //最终执行被标注的方法
                                        return declaredMethod.invoke(object, null);
                                    }
                                }
    
                        );
    
                        //调用view的setCommonListener方法
                        viewMethod.invoke(view, proxy);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
    

    使用

    @ContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @BindView(R.id.button1)
        private Button button1;
    
        @BindView(R.id.button2)
        Button button2;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            MsInjector.inject(this);
        }
    
        @OnClick(R.id.button1)
        public void clickButton1() {
            Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();
        }
    
        @OnClick(R.id.button2)
        public void clickButton2() {
            Toast.makeText(this, "click button2", Toast.LENGTH_SHORT).show();
        }
    
        @OnLongClick(R.id.button1)
        public boolean longClickButton1() {
            Toast.makeText(this, "long click button1", Toast.LENGTH_SHORT).show();
            return false;
        }
    
        @OnLongClick(R.id.button2)
        public boolean longClickButton2() {
            Toast.makeText(this, "long click button2", Toast.LENGTH_SHORT).show();
            return false;
        }
    }
    
    

    相关文章

      网友评论

        本文标题:使用反射和动态代理实现一个View注解绑定库

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