1.目的:
通过代理的方式去注册控件的点击事件,长按事件等其他事件。
优点:隔离代码层,解耦 缺点:运行时反射的效率低2.第一版本(先不用动态代理):
2.1 创建一个注解类 (用来区分方法是否需要注册点击事件)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
//接收控件的ID
int[] value() default -1;
}
2.2 创建一个注解工具类
public class InjectUtil {
public static void init(Object content) {
//1.获取类中所有的方法
Method[] methods = content.getClass().getMethods();
for (Method method : methods) {
//2.找到有标注注解的方法
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
int[] value = onClick.value();
try {
//3.通过反射获取findViewById方法
Method findViewById = content.getClass().getMethod("findViewById", int.class);
for (int i : value) {
//4.获取控件
View view = (View) findViewById.invoke(content, i);
if (view != null) {
//5.设置监听
view.setOnClickListener(v -> {
try {
//6.方法回调 method-> 就是我们外面标注注解的方法
method.invoke(content, v);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
});
}
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
}
2.3 注册工具类
BaseActivity
类
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtil.init(this);
}
}
MainActivity
类
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
super.onCreate(savedInstanceState);
}
@OnClick(R.id.btn)
public void start(View view) {
Toast.makeText(this,"我被点击了",Toast.LENGTH_SHORT).show();
}
}
注意:MainActivity类中,setContentView必须在super.onCreate()方法前,因为只有在setContentView后控件才会被创建。
很简单的代码,但这样会产生一个问题;
1.那就是如果你还要注册长按事件,viewpager的滑动事件,那是不是还得继续往文件写代码,违背了类的单一原则,也不好扩展,
2.你如何区分哪个控件需要长按,哪个不需要?按照下面的写法,所有的控件都会被自动注册长按事件,
如下长按事件;
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
//6.方法回调 method-> 就是我们外面标注注解的方法
try {
//6.方法回调 method-> 就是我们外面标注注解的方法
method.invoke(content, v);
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return false;
}
});
接下来我们用动态代理看下,能不能解决上面的问题
3.使用动态代理:
我们简单的修改下InjectUtil
类
public static void init(Object content) {
//1.获取类中所有的方法
Method[] methods = content.getClass().getMethods();
for (Method method : methods) {
//2.找到有标注注解的方法
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
int[] value = onClick.value();
try {
//3.通过反射获取findViewById方法
Method findViewById = content.getClass().getMethod("findViewById", int.class);
for (int i : value) {
//4.获取控件
View view = (View) findViewById.invoke(content, i);
//5.通过代理获取View.OnClickListener
View.OnClickListener listener = (View.OnClickListener) Proxy.newProxyInstance(content.getClass().getClassLoader(), new Class[]{View.OnClickListener.class}, new OnClickInvocationHandler(content,method));
//6.设置监听
view.setOnClickListener(listener);
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
OnClickInvocationHandler
类
public class OnClickInvocationHandler implements InvocationHandler {
private final Object content;//可以是Activity,fragment,dialog
private final Method method;
public OnClickInvocationHandler(Object content, Method method) {
this.content = content;
this.method = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (content != null) {
//这里的回调是 setOnClickListener的回调
//method 也就是start方法
this.method.invoke(content,args);
}
return null;
}
}
动态代理用上了,但好像也没解决问题,只不过是换了个高级点的用法,屁用没有。
问题依旧存在,如下:
1.那就是如果你还要注册长按事件,viewpager的滑动事件,那是不是还得继续往文件写代码,违背了类的单一原则,也不好扩展,
2.你如何区分哪个控件需要长按,哪个不需要?按照下面的写法,所有的控件都会被自动注册长按事件。
留到下一章解决,haha
动态代理-事件注入(二)https://blog.csdn.net/wumeixinjiazu/article/details/122499788
代码地址:IOCDemo: 动态代理ioc注解https://gitee.com/small_insects/IOCDemo