美文网首页
AndroidStudio自定义插件技术(仿xutils)

AndroidStudio自定义插件技术(仿xutils)

作者: 壹元伍角叁分 | 来源:发表于2021-11-24 14:53 被阅读0次

    一、基础注解

    1、完成setContentView()的工作

    1.1 创建注解@setContentView

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

    1.2 反射完成setContentView()调用

    private static void injectContentViewLayout(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        // 获取activity上的setContentView注解
        setContentView annotation = aClass.getAnnotation(setContentView.class);
    
        if (annotation != null) {
            try {
                // 反射获取activity的setContentView方法。第一个参数是方法名,第二个参数是方法的参数
                Method classMethod2 = aClass.getMethod("setContentView", int.class);
                // 执行setContentView方法。第一个参数是对象bean,第二个参数是方法的参数
                classMethod2.invoke(activity, annotation.value());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    1.3 添加注解@setContentView

    @setContentView(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity
    

    2、完成findViewById()的工作

    2.1 创建注解@InjectView

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

    2.2 反射完成findViewById()调用

    private static void injectViewLayout(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        try {
            Method classMethod = aClass.getMethod("findViewById", int.class);
    
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                InjectView annotation = declaredField.getAnnotation(InjectView.class);
                if (annotation != null) {
                    Object invoke = classMethod.invoke(activity, annotation.value());
                    declaredField.set(activity, invoke);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    2.3 添加注解@InjectView

    @InjectView(R.id.tv1)
    private TextView tv1;
    

    3、完成setOnClickListener()的工作

    3.1 创建注解@ViewClick

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface ViewClick {
        int value();
    }
    

    3.2 反射完成setOnClickListener()调用

    private static void injectViewClick(final Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        Method classMethod = null;
        try {
            // 先找到findViewById()方法
            classMethod = aClass.getMethod("findViewById", int.class);
    
            // 获取到activity的所有方法。getDeclaredMethods包括私有。
            Method[] declaredMethods = aClass.getDeclaredMethods();
            for (final Method declaredMethod : declaredMethods) {
                // 遍历所有方法,找到添加@ViewClick注解的方法。
                ViewClick annotation = declaredMethod.getAnnotation(ViewClick.class);
                if (annotation != null) {
                    // 获取到@ViewClick注解中的viewId
                    int value = annotation.value();
                    // 反射执行findViewById()
                    View view = (View) classMethod.invoke(activity, value);
                    // 找到view后,设置监听。执行添加@ViewClick注解的方法。
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                declaredMethod.invoke(activity);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    3.3 添加注解@ViewClick

    @ViewClick(R.id.bt1)
    public void bt1Click() {
        Log.e(TAG, "bt1Click: 被点击了");
    }
    

    二、通用注解

    如果后期又添加了setOnLongClickListener()方法,那我们得重写一遍。那有什么办法可以一劳永逸呢?

    还是以setOnClickListener为例,我们可以将之前的代码分成三个要素组成:setOnClickListener()、OnClickListener.class、onClick()

    bt2.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            
        }
    });
    

    1、定义base注解

    @Target(ElementType.ANNOTATION_TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BaseListener {
       
        String listenerMethodName();
        // listerClass = ""
        Class listerClass();
    
        String listenerCallback();
    }
    

    2、定义子注解

    2.1 定义@ViewClickListener注解,完成setOnClickListener()工作
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @BaseListener(listenerMethodName = "setOnClickListener" // 监听
                  ,listerClass = View.OnClickListener.class // 接口
                  ,listenerCallback = "onClick") // 接口回调方法
    @interface ViewClickListener {
        int value();
    }
    

    2.2 定义@ViewLongClickListener注解,完成setOnLongClickListener()工作

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @BaseListener(listenerMethodName = "setOnLongClickListener"
                 ,listerClass = View.OnLongClickListener.class
                 ,listenerCallback = "onLongClick")
    @interface ViewLongClickListener {
        int value();
    }
    

    3、反射完成监听工作

    private static void injectViewListener(final Activity activity) {
        Class<? extends Activity> activityClass = activity.getClass();
        Method[] declaredMethods = activityClass.getDeclaredMethods();
        for (final Method declaredMethod : declaredMethods) {
            declaredMethod.setAccessible(true);
    
            // 找到该方法上所有的注解
            Annotation[] annotations = declaredMethod.getAnnotations();
            // 遍历注解
            for (Annotation annotation : annotations) {
                // 找注解上的注解,也就是我们前面的base注解
                Class<? extends Annotation> aClass1 = annotation.annotationType();
                BaseListener baseListenerAnnotation = aClass1.getAnnotation(BaseListener.class);
                
                // 找到了base注解,获取值。
                if (baseListenerAnnotation != null) {
                    String methodName = baseListenerAnnotation.listenerMethodName();
                    Class listerClass = baseListenerAnnotation.listerClass();
                    final String listenerCallback = baseListenerAnnotation.listenerCallback();
                    // 先获取到@ViewClickListener上的值。通过反射吧
                    try {
                        // 获取到activity中注解的值,也就是viewId。但无法像前面那样直接获取。需要调用value方法获取。
                        Method valueMethod = annotation.getClass().getDeclaredMethod("value");
                        valueMethod.setAccessible(true);
                        // 找到viewId。
                        int viewId = (int) valueMethod.invoke(annotation);
    
                        // 找到了viewId后,执行findViewById
                        Method classMethod = activityClass.getMethod("findViewById", int.class);
                        View invokeView = (View) classMethod.invoke(activity, viewId);
    
                        // 获取到view的方法
                        Method doClassMethod = invokeView.getClass().getMethod(methodName, listerClass);
    
                        // 这里需要用到动态代理了
                        Object proxyInstance = Proxy.newProxyInstance(listerClass.getClassLoader()
                                , new Class[]{listerClass}
                                , new InvocationHandler() {
                                    @Override
                                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                
                                        // 当监听回调返回,会执行到这里
                                        if (TextUtils.equals(listenerCallback, method.getName())) {
                                            // 执行activity中添加注解的方法。
                                            return declaredMethod.invoke(activity, null);
                                        }
                                        return null;
                                    }
                                });
                        // 执行view的setOnClickListener()方法
                        doClassMethod.invoke(invokeView, proxyInstance);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    4、activity中引用

    @ViewClickListener(R.id.bt1)
    public void bt1Click() {
        Log.e(TAG, "bt1Click: 被点击了");
    }
    
    @ViewLongClickListener(R.id.bt1)
    public boolean bt1Click2() {
        Log.e(TAG, "bt1Click: 被长按了.........");
        return true;
    }
    
    @ViewClickListener(R.id.bt2)
    public void bt1Click3() {
        Log.e(TAG, "bt2Click: 被点击了");
    }
    

    相关文章

      网友评论

          本文标题:AndroidStudio自定义插件技术(仿xutils)

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