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

2021-11-24  本文已影响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: 被点击了");
}
上一篇下一篇

猜你喜欢

热点阅读