这篇相对于之前的有点难度,需要大家熟练Java反射和Proxy代理流程才能看的容易些,因为这两块不是这篇文章的重点,不懂的可以网上查一下,了解个大概,就可以了。
首先,我先贴出MainActivity的点击事件代码,因为比较简单:
@IOnClick({R.id.btn_login, R.id.btn_login})
public void showToast(View view) {
switch (view.getId()) {
case R.id.btn_login:
Toast.makeText(this, "登录", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_logoff:
Toast.makeText(this, "注销", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
//btn_login.setOnClickListener(new View.OnClickListener() { //setListener listenerType
// @Override
// public void onClick(View v) { // listenerCallback
// }
//});
@Target(ElementType.METHOD) //注解应用于其他注解上
@Retention(RetentionPolicy.RUNTIME)
//这个注解需要注意,对应的参数在上面注解的 btn_login 点击事件里对应找即可
@BaseEvent(setListener = "setOnClickListener",//setOnClickListener为View.setOnClickListener
listenerType = View.OnClickListener.class,//监听的类型为点击事件
listenerCallback = "onClick")//这个onClick回调,即为setOnClickListener后回调的onClick
public @interface IOnClick {
int[] value();//因为一个方法可能与多个控件绑定
}
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface BaseEvent {
String setListener();//设置监听方法名
Class listenerType();//监听类型
String listenerCallback();//监听回调方法名
}
上面的图看明白后,接下来就很简单了没错,我们需要做的就是把onClick方法拦截下来,然后执行我们自己的方法。
贴InjectUtils里最后一个方法的代码 OnClick:
private static void OnClick(Activity activity) {
// 获取MainActivity
Class extends Activity> clazz = activity.getClass();
// 获取MainActivity中所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 获取方法上对应的@IOnclick的注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
// 通过annotationType获取注解@BaseEvent
Class extends Annotation> annotationType = annotation.annotationType();
//需要判断是否为null
if (annotationType != null) {
// 获取@IOnclick注解上的BaseEvent注解
BaseEvent baseEvent = annotationType.getAnnotation(BaseEvent.class);
//需要判断是否为null,因为有的注解没有@BaseEvent
if (baseEvent != null) {
// 获取@BaseEvent的三个value
String callback = baseEvent.listenerCallback();
Class type = baseEvent.listenerType();
String setListener = baseEvent.setListener();
try {
// 通过反射获取方法,@IOnclick里的int[] value()不需要传参,所以参数省略
Method declaredMethod = annotationType.getDeclaredMethod("value");
// 调用方法,获取到@IOnclick的value,即两个button的id,参数省略
int[] valuesIds = (int[]) declaredMethod.invoke(annotation);
// 这个类稍后会给出代码,目的是拦截方法
InjectInvocationHandler handler = new InjectInvocationHandler(activity);
// 添加到拦截列表
handler.add(callback, method);
// 得到监听的代理对象
Proxy proxy = (Proxy) Proxy.newProxyInstance(type.getClassLoader(),
new Class[]{type}, handler);
// 遍历所有button的id
for (int valuesId : valuesIds) {
View view = activity.findViewById(valuesId);
// 通过反射获取方法
Method listener = view.getClass().getMethod(setListener, type);
// 执行方法
listener.invoke(view, proxy);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
public class InjectInvocationHandler implements InvocationHandler {
// 拦截的方法名列表
private Map map = new HashMap<>();
// 在这里实际上是MainActivity
private Object target;
public InjectInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target != null) {
// 获取方法名
String name = method.getName();
Method m = map.get(name);
if (m != null) {//如果不存在与拦截列表,就执行
return m.invoke(target, args);
}
}
return null;
}
/**
* 向拦截列表里添加拦截的方法
*/
public void add(String name, Method method) {
map.put(name, method);
}
}
下面弹出了吐司,证明代码已经生效!喜欢的朋友们留个言吧~~~~~~~~~