概述
本文主要分享基于动态代理实现控件点击事件注入。
实现思路:
- 自定义EventType注解标记需要点击事件类型
- 自定义OnClick、OnLongClick注解标记点击、长按事件
- 扫描页面中的注解,判断该注解是否被EventType注解标记
- 获取EventType注解的事件类型通过动态代理创建对应的事件代理对象
- 查找被OnClick、OnLongClick标示的控件并通过反射注入事件代理对象完成事件注入
自定义EventType、OnClick、OnLongClick注解
//这是一个元注解:即可以用在注解之上的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventType {
Class listenerType();
String listenerSetter();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventType(listenerType = View.OnClickListener.class,listenerSetter = "setOnClickListener")
public @interface OnClick {
int []value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventType(listenerType = View.OnLongClickListener.class,listenerSetter = "setOnLongClickListener")
public @interface OnLongClick {
int []value();
}
动态代理实现点击事件注入
关键代码如下:
public static void injectEvent(final MainActivity activity) {
//注意:反射是基于Class
Class<? extends MainActivity> activityClass = activity.getClass();
//获取所有的方法
Method[] declaredMethods = activityClass.getDeclaredMethods();
for (final Method declaredMethod : declaredMethods) {
//获取方法声明的注解数组,Annotation是所有注解的父接口
Annotation[] annotations = declaredMethod.getAnnotations();
for (Annotation annotation : annotations) {//OnClick、OnLongClick等
//获取注解类型
Class<? extends Annotation> annotationType = annotation.annotationType();
//A.isAnnotationPresent(B.class)即注释B是否在此A上
if (annotationType.isAnnotationPresent(EventType.class)) {
//获取EventType注解
EventType eventType = annotationType.getAnnotation(EventType.class);
//获取EventType注解声明的参数
Class listenerType = eventType.listenerType();
String listenerSetter = eventType.listenerSetter();
try {
Method valueMethod = annotationType.getDeclaredMethod("value");
//获取控件Id数组
int[] ids = (int[]) valueMethod.invoke(annotation);
declaredMethod.setAccessible(true);
//创建事件回调动态代理类,省去判断onClick、onLongClick
Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, new InvocationHandler() {
//动态代理回调方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return declaredMethod.invoke(activity, args);
}
});
for (int id : ids) {
//查处所有Id对应的控件
View view = activity.findViewById(id);
//获取指定的点击方法如:setOnClickListener、setOnLongClickListener
Method setMethod = view.getClass().getMethod(listenerSetter,listenerType);
//反射调用控件的setOnClickListener、setOnLongClickListener方法,进而调用到代理类的方法
setMethod.invoke(view, proxyInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
页面注入
public class MainActivity extends AppCompatActivity {
private static final String TAG = "fmt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InjectUtils.injectEvent(this);
}
@OnClick({R.id.btn1, R.id.btn2})
public void click(View view) {
switch (view.getId()) {
case R.id.btn1:
Log.e(TAG, "click: 按钮1");
break;
case R.id.btn2:
Log.e(TAG, "click: 按钮2");
break;
}
}
@OnLongClick({R.id.btn1, R.id.btn2})
public boolean longClick(View view) {
switch (view.getId()) {
case R.id.btn1:
Log.e(TAG, "longClick: 按钮1");
break;
case R.id.btn2:
Log.e(TAG, "longClick: 按钮2");
break;
}
return false;
}
}
完整代码实现
密码:9ln0
网友评论