download:2021助理高薪-拉钩产品经理高薪训练营
使用反射和动静代理实现一个View注解绑定库
反对的功能
@ContentView 绑定layout 代替setContentView()
@BindView 绑定View 代替findViewById()
@OnClick 绑定点击事件 代替setOnClickListener()
@OnLongClick 绑定长按事件 代替setOnLongClickListener()
代码
注解类
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView {
int value();
}
复制代码
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
int value();
}
复制代码
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnEvent {
//订阅形式String setCommonListener();//事件源对象Class<?> commonListener();
}
复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnEvent(setCommonListener = "setOnClickListener",
commonListener = View.OnClickListener.class)
public @interface OnClick {
int value();
}
复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnEvent(setCommonListener = "setOnLongClickListener",
commonListener = View.OnLongClickListener.class)
public @interface OnLongClick {
int value();
}
复制代码
实现类
public class MsInjector {
public static void inject(Object object) { injectContentView(object); injectView(object); injectEvent(object);}private static void injectContentView(Object object) { Class<?> clazz = object.getClass(); //获取到ContentView注解 ContentView contentView = clazz.getAnnotation(ContentView.class); if (contentView == null) { return; } //获取到注解的值,也就是layoutResID int layoutResID = contentView.value(); try { //反射出setContentView方法并调用 Method method = clazz.getMethod("setContentView", int.class); method.invoke(object, layoutResID); } catch (Exception e) { e.printStackTrace(); }}private static void injectView(Object object) { Class<?> clazz = object.getClass(); //获取到所有字段并遍历 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); //获取字段上的BindView注解 BindView bindView = field.getAnnotation(BindView.class); if (bindView == null) { continue; } //获取到viewId int viewId = bindView.value(); try { //通过反射调用findViewById失去view实例对象 Method method = clazz.getMethod("findViewById", int.class); Object view = method.invoke(object, viewId); //赋值给注解标注的对应字段 field.set(object, view); } catch (Exception e) { e.printStackTrace(); } }}private static void injectEvent(Object object) { Class<?> clazz = object.getClass(); //获取到当前页年一切办法并遍历 Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { declaredMethod.setAccessible(true); //获取方法上的所有注解并遍历 Annotation[] annotations = declaredMethod.getDeclaredAnnotations(); for (Annotation annotation : annotations) { //获取注解本身 Class<? extends Annotation> annotationType = annotation.annotationType(); //获取注解上的OnEvent注解 OnEvent onEvent = annotationType.getAnnotation(OnEvent.class); if (onEvent == null) { continue; } //拿到注解中的元素 String setCommonListener = onEvent.setCommonListener(); Class<?> commonListener = onEvent.commonListener(); try { //因为上边没有明确获取是哪个注解,所以这里需要使用反射获取viewId Method valueMethod = annotationType.getDeclaredMethod("value"); valueMethod.setAccessible(true); int viewId = (int) valueMethod.invoke(annotation); //通过反射findViewById获取到对应的view Method findViewByIdMethod = clazz.getMethod("findViewById", int.class); Object view = findViewByIdMethod.invoke(object, viewId); //通过反射获取到view中对应的setCommonListener方法 Method viewMethod = view.getClass().getMethod(setCommonListener, commonListener); //使用动静代理监听回调 Object proxy = Proxy.newProxyInstance( clazz.getClassLoader(), new Class[]{commonListener}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //最终执行被标注的方法 return declaredMethod.invoke(object, null); } } ); //调用view的setCommonListener方法 viewMethod.invoke(view, proxy); } catch (Exception e) { e.printStackTrace(); } } }}
}
复制代码
使用
@ContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@BindView(R.id.button1)private Button button1;@BindView(R.id.button2)Button button2;@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MsInjector.inject(this);}@OnClick(R.id.button1)public void clickButton1() { Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();}@OnClick(R.id.button2)public void clickButton2() { Toast.makeText(this, "click button2", Toast.LENGTH_SHORT).show();}@OnLongClick(R.id.button1)public boolean longClickButton1() { Toast.makeText(this, "long click button1", Toast.LENGTH_SHORT).show(); return false;}@OnLongClick(R.id.button2)public boolean longClickButton2() { Toast.makeText(this, "long click button2", Toast.LENGTH_SHORT).show(); return false;}
}