美文网首页
Ioc注入框架实现控件查找,点击事件 2019-11-01

Ioc注入框架实现控件查找,点击事件 2019-11-01

作者: GoodWen | 来源:发表于2019-11-01 08:43 被阅读0次
1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png 11.png 12.png

新建model 结构图

111111.png

代码如下

/**
 * 时间:2019/10/30 15:29
 * 描述:Target  是目标的意思 也叫该注解作用在XXX上的意思  ElementType.TYPE  作用于接口类枚举之上
 * ANNOTATION_TYPE 作用在注解之上
 */
@Target(ElementType.TYPE)//
@Retention(RetentionPolicy.RUNTIME)//jvm加载时通过反射获取注解的值
public @interface ContentView {
    //int 类型的布局
    int value();
}
/**
 * 时间:2019/10/30 16:50
 * 描述:
 */
@Target(ElementType.ANNOTATION_TYPE)//改注解作用在其他注解之上
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
    //1,监听的方法名,setOnClickListener()
    String listenerSetter();

    //2,监听的对象,View.OnClickListener()
    Class<?> listenerType();

    //3,回调方法名,OnClick(View v)
    String callBackListener();
}
@Target(ElementType.FIELD)//在属性之上
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
    //int 类型的控件(R.id.xxx)
    int value();
}
@Target(ElementType.METHOD)//方法之上
@Retention(RetentionPolicy.RUNTIME)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callBackListener = "onClick")
public @interface OnClick {
    int[] value();//点击ID可能有很多个
}
/**
 * 描述:注入管理
 */
public class InjectManager {
    public static void inject(Activity activity) {
        //布局注入
        injectLayout(activity);
        //控件注入
        injectViews(activity);
        //事件注入
        injectEvents(activity);

    }

    //布局注入
    private static void injectLayout(Activity activity) {
        //获取类
        Class<? extends Activity> clazz = activity.getClass();
        //获取类的注解
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (null != contentView) {
            int layoutId = contentView.value();
            //方式一
            //activity.setContentView(layoutId);
            try {
                //方式二:反射的技术
                //1,获取指定的方法 setContentView()
                Method method = clazz.getMethod("setContentView", int.class);//获取本类和父类的所有公共方法
                //2,执行方法
                method.invoke(activity, layoutId);

            } catch (Exception e) {
                e.printStackTrace();
            }


        }


    }

    //控件注入
    private static void injectViews(Activity activity) {
        //获取类
        Class<? extends Activity> clazz = activity.getClass();
        //获取所有属性(包括私有属性)
        Field[] fields = clazz.getDeclaredFields();
        //遍历  拿到每个属性
        for (Field field : fields) {
            // 拿到每个属性额注解
            InjectView injectView = field.getAnnotation(InjectView.class);
            if (null != injectView) {
                // 拿到每个属性额注解的值 R.id.xxx
                int viewId = injectView.value();
                //方式一
                //View view = activity.findViewById(viewId);
                try {
                    //方式二:反射的技术
                    //1,获取指定的方法 findViewById()
                    Method method = clazz.getMethod("findViewById", int.class);//获取本类和父类的所有公共方法
                    //2,执行方法
                    Object view = method.invoke(activity, viewId);
                    //设置访问private私有权限(私有属性可以访问)
                    field.setAccessible(true);
                    //赋值过程 属性的值赋值给控件
                    field.set(activity, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    //事件注入
    private static void injectEvents(Activity activity) {
        //获取类
        Class<? extends Activity> clazz = activity.getClass();
        //获取所有方法 onResume onCreate ....  还有可能是私有的方法
        Method[] methods = clazz.getDeclaredMethods();//获取当前类所有的方法,包括私有
        //遍历方法
        for (Method method : methods) {
            //获取所有方法的注解(@Override @OnClick)
            Annotation[] annotations = method.getAnnotations();//每个方法上的注解 有可能是个数组 一个方法上可能有好多个注解
            //遍历该方法的所有注解 拿到我想要的注解
            for (Annotation annotation : annotations) {
                //获取 OnClick 注解上的注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (null != annotationType) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    if (null != eventBase) {
                        //这时候已经可以获得事件 重要的三成员  如下
                        String listenerSetter = eventBase.listenerSetter();
                        Class<?> listenerType = eventBase.listenerType();
                        String callBackListener = eventBase.callBackListener();

                        ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
                        handler.addMethod(callBackListener, method);
                        //通过代理的方式 操作这个对象  并且中间拦截onClick方法 执行自定义方法
                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),
                                new Class[]{listenerType}, handler);

                        try {
                            //通过annotationType获取onClick注解的值
                            Method valueMethod = annotationType.getDeclaredMethod("value");
                            //再去执行value的方法获取注解的值
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);
                            //把id循环出来再去赋值  避免没有赋值而报异常
                            for (int viewId : viewIds) {
                                //获取当前activity的view(赋值)
                                View view = activity.findViewById(viewId);
                                //获取指定的方法
                                Method setterMethod = view.getClass().getMethod(listenerSetter, listenerType);
                                //再去执行方法
                                setterMethod.invoke(view, listener);

                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }

                    }
                }
            }
        }
    }
}
/**
 * 时间:2019/10/30 17:52
 * 描述:拦截器   拦截本应该执行的回调方法 onClick()  而去执行我们自定义的方法
 */
public class ListenerInvocationHandler implements InvocationHandler {
    //拉结的目标 比如需要拦截MainActivity中的方法
    private Object target;
    //需要拦截额对象 键值对
    private HashMap<String,Method> methodHashMap = new HashMap();

    public ListenerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (null != target) {
            //获取需要拦截的方法名
            String methodName = method.getName();//如果是onClick 那就要做拦截
            method = methodHashMap.get(methodName);//需要执行自定义的方法 show()方法
            if (null != method) {
                return method.invoke(target, args);
            }
        }
        return null;
    }

    /**
     * 添加将要拦截的方法
     * @param methodName 拦截的方法 如 onClick
     * @param method 执行的方法  如  show(自己定义的方法)
     */
    public void addMethod(String methodName,Method method) {
        methodHashMap.put(methodName, method);
    }
}

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate( Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectManager.inject(this);
    }
}
@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {
    @InjectView(R.id.tv)
    private TextView textView;

    @Override
    protected void onResume() {
        super.onResume();
    }

    @OnClick({R.id.tv, R.id.btn, R.id.btn1})
    public void show(View view) {
        switch (view.getId()) {
            case R.id.btn:
                Toast.makeText(this, "点了btn1", Toast.LENGTH_SHORT).show();
                break;
            case R.id.btn1:
                Toast.makeText(this, "点了btn2", Toast.LENGTH_SHORT).show();
                break;
            case R.id.tv:
                textView.setText("哈哈哈");
                Toast.makeText(this, textView.getText().toString(), Toast.LENGTH_SHORT).show();
                break;
        }
    }
}

布局很简单 按照自己心情来

相关文章

网友评论

      本文标题:Ioc注入框架实现控件查找,点击事件 2019-11-01

      本文链接:https://www.haomeiwen.com/subject/ncaxbctx.html