用注解、反射、动态代理实现ButterKnife功能

ButterKnife的实现需要先熟悉注解、反射、动态代理,不熟悉的小伙伴可以去看这三篇文章:

Java注解

Java反射

动态代理

下面我们就来看一下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){

    }


}

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>