ButterKnife的实现需要先熟悉注解、反射、动态代理,不熟悉的小伙伴可以去看这三篇文章:
下面我们就来看一下ButterKnife的实现:
ButterKnife相信大家都会用,它能通过注解帮我们绑定布局文件中的控件,能通过注解帮我们设置view的click事件
今天我们除了实现这两个功能还要实现通过注解代替setContentView(R.layout.xxx)功能
1.通过注解实现setContentView功能
思路:定义一个MyContentView注解携带布局文件的Id,通过反射获取Activity中的setContentView的方法,然后找到MyContentVIew注解获取Id,再用反射调用setContentView把布局文件的Id设置进去
2.通过注解实现控件绑定功能即:通过注解实现findVIewById
思路:定义一个注解MyViewBinding携带控件的Id,通过反射获取所有带有MyViewBinding注解的成员变量(View控件类型),通过反射获取Activity中的findViewById的方法,然后通过反射调用findViewById把控件的Id设置进去,最后再把findViewById得到的view赋值给之前获取的成员变量
3.通过注解实现设置点击事件即: View.setOnclickListener(new View.OnclickListener)这个功能
定义一个注解ClickView携带控件的Id,通过反射获取到这个控件的实例,再获取View的setOnclickListener的方法,通过动态代理获取一个OnclickListener 接口的代理,然后反射实现view.setOnclickListener(代理)
分别定义三个注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface MyContentView {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface MyViewBinding {
int value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ClickView {
int value();
}
反射注入核心类:
public class MyViewInject {
public static void inject(Object activity) {
injectContentView(activity);
injectView(activity);
injectClick(activity);
}
private static void injectContentView(Object activity) {
try {
Class activityClass = activity.getClass();
MyContentView myContentView = (MyContentView) activityClass.getAnnotation(MyContentView.class);
Method setContentViewMethod = activityClass.getMethod("setContentView",int.class);
setContentViewMethod.invoke(activity,myContentView.value());
} catch (Exception e) {
e.printStackTrace();
}
}
private static void injectView(Object activity) {
try {
Class activityClass = activity.getClass();
Class appCompatActivityClass = activityClass.getSuperclass();
Field[] fields = activityClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(MyViewBinding.class)) {
MyViewBinding myViewBinding = field.getAnnotation(MyViewBinding.class);
Method findViewById = appCompatActivityClass.getMethod("findViewById", int.class);
Object view = findViewById.invoke(activity, myViewBinding.value());
field.set(activity, view);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void injectClick(Object activity) {
try {
Class activityClass = activity.getClass();
Class appCompatActivityClass = activityClass.getSuperclass();
Method[] methods = activityClass.getMethods();
for (Method methodActivity : methods) {
if (methodActivity.isAnnotationPresent(ClickView.class)) {
ClickView myViewBinding = methodActivity.getAnnotation(ClickView.class);
Method findViewById = appCompatActivityClass.getMethod("findViewById", int.class);
Object view = findViewById.invoke(activity, myViewBinding.value());
Class viewClass = view.getClass().getSuperclass().getSuperclass();
Method setOnClickListenerMethod = viewClass.getMethod("setOnClickListener", View.OnClickListener.class);
Object proxy = Proxy.newProxyInstance(activityClass.getClassLoader(), new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
methodActivity.invoke(activity, view);
return null;
}
});
setOnClickListenerMethod.invoke(view, proxy);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Activity使用:
@MyContentView(R.layout.activity_annotation)
public class AnnotationTestActivity extends AppCompatActivity {
@MyViewBinding(R.id.tv_name)
public TextView textView;
@MyViewBinding(R.id.bt_jump)
public Button bt_jump;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyViewInject.inject(AnnotationTestActivity.this);
textView.setText("绑定");
bt_jump.setText("跳转");
}
@ClickView(R.id.tv_name)
public void click(View v){
Toast.makeText(getApplicationContext(),"调用click",Toast.LENGTH_SHORT).show();
}
@ClickView(R.id.bt_jump)
public void jump(View v){
}
}