手写IOC注解,解决findViewById和点击事件

来源:互联网 发布:淘宝网资产处置平台 编辑:程序博客网 时间:2024/06/16 20:08

在项目开发中findViewById以及控件的点击事件的场景是最多的,大量的findViewById并强转会浪费不少的开发时间,那么是否能解决掉这个问题呢?答案肯定是能解决;像xutils、butterKnidnife等主流第三方框架就很好的解决了这些问题,那么这些第三方的已经帮助开发者解决掉这些问题了,是否还要自己学着去写一套手写的IOC注解呢,肯定是需要的,在学习写的过程中能更好的理解这些第三方的实现原理,同时也能也符合项目的需要。下面这个是在学习过程中写的一个IOC注解:

涉及知识点:
1、注解
2、反射
3、动态代理(点击事件注入的时候会涉及到)

实现:
1、activity布局注入,不需要调用setContentView
2、activity、fragment等 findViewById、setOnClickListener、setOnLongClickListener等事件注入,点击和长按事件可轻松切换

代码实现:
ContentView 是用来activity布局注入:

/** * Created by Administrator on 2017/12/13. * activity 布局注入 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ContentView {    int value();}

activity布局注入逻辑:

/** * Activity加载布局 * * @param context */private static void injectLayout(Object context) {    int layoutId = 0;    Class<?> clazz = context.getClass();    //拿到activity类上面的注解    ContentView contentView = clazz.getAnnotation(ContentView.class);    if (contentView != null) {        layoutId = contentView.value();        try {            Method method = clazz.getMethod("setContentView", int.class);            method.setAccessible(true);            try {                //反射执行                method.invoke(context, layoutId);            } catch (IllegalAccessException e) {                e.printStackTrace();            } catch (InvocationTargetException e) {                e.printStackTrace();            }        } catch (NoSuchMethodException e) {            e.printStackTrace();        }    }}

ViewInject是findViewById注入:

/** * Created by Administrator on 2017/12/13. * findViewById注入 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ViewInject {    int value();}

findViewById注入逻辑:

/**  * Activity加载view  *  * @param context  */private static void injectView(ViewFinder finder, Object context) {    Class<?> clazz = context.getClass();    //获取activity类中所有的成员变量    Field[] fields = clazz.getDeclaredFields();    //遍历    for (Field field : fields) {        field.setAccessible(true);        //得到成员变量的注解        ViewInject viewInject = field.getAnnotation(ViewInject.class);        if (viewInject != null) {            //获取对应的id            int valueId = viewInject.value();            //反射获取控件            View viewById = finder.findViewById(valueId);            if (viewById == null) {                continue;            }            //反射调用方法            try {                field.set(context, viewById);            } catch (IllegalAccessException e) {                e.printStackTrace();            }        }    }}

下面这些是点击事件、长按事件、条目点击事件等事件注入:

/** * Created by Administrator on 2017/12/15. * 对所有的点击事件扩展 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface EventBase {    /**     * 设置监听的方法     * @return     */    String listenerSetter();    /**     * 事件类型     * @return     */    Class<?> listenerType();    /**     * 回调方法     * 事件被触发后,执行回调方法名称     * @return     */    String callBackMethod();}
/** * Created by Administrator on 2017/12/15. * 点击事件 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBase(listenerSetter = "setOnClickListener",        listenerType = View.OnClickListener.class,        callBackMethod = "onClick")public @interface OnClick {    /**     * 点击事件控件数组     * @return     */    int [] value()default -1;;}
/** * Created by Administrator on 2017/12/15. * 长按事件 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBase(listenerSetter = "setOnLongClickListener",        listenerType = View.OnLongClickListener.class,        callBackMethod = "onLongClick")public @interface OnLongClick {    int [] value() default -1;}
/** * Created by Administrator on 2017/12/15. * 条目点击事件 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBase(listenerSetter = "setOnItemClickListener",        listenerType = AdapterView.OnItemClickListener.class,        callBackMethod = "onItemClick")public @interface OnItemClick {    int [] value();}

在注入事件时涉及到动态代理的使用;

/** * Created by Administrator on 2017/12/15. * 动态代理 */public class ListenerInvocationHandler implements InvocationHandler{    //代理的真实对象    private Object context;    private Map<String,Method> methodMap;    public ListenerInvocationHandler(Object context, Map<String, Method> methodMap) {        this.context = context;        this.methodMap = methodMap;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String name = method.getName();        //决定是否需要进行代理        Method metf = methodMap.get(name);        if(metf!=null){            return metf.invoke(context,args);        }else{            return metf.invoke(proxy,args);        }    }}

事件注入逻辑:

/** * 事件注入 * * @param context */private static void injectEvents(ViewFinder finder, Object context) {    Class<?> clazz = context.getClass();    //获取activity里面所有的方法    Method[] methods = clazz.getDeclaredMethods();    //循环遍历    for (Method method : methods) {        //获取方法上面所有的注解        Annotation[] annotations = method.getAnnotations();        //循环遍历所有的注解        for (Annotation annotation : annotations) {            //获取注解类型            Class<?> anntionType = annotation.annotationType();            //获取注解上面的注解            EventBase eventBase = anntionType.getAnnotation(EventBase.class);            if (eventBase == null) {                continue;            }            //开始获取事件三要素  通过反射注入进去            String listenerSetter = eventBase.listenerSetter();            Class<?> listenerType = eventBase.listenerType();            String callMethod = eventBase.callBackMethod();            //方法名与方法method进行对应关系            Map<String, Method> methodMap = new HashMap<>();            methodMap.put(callMethod, method);            try {                Method valueMethod = anntionType.getDeclaredMethod("value");                int[] viewIds = (int[]) valueMethod.invoke(annotation);                //遍历获取到的id                for (int viewId : viewIds) {                    //通过反射获取相应的view控件                    View view = finder.findViewById(viewId);                    if (view == null) {                        continue;                    }                    Method setListener = view.getClass().getMethod(listenerSetter, listenerType);                    ListenerInvocationHandler handle = new ListenerInvocationHandler(context, methodMap);                    Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(),                            new Class[]{listenerType},                            handle);                    setListener.invoke(view, proxy);                }            } catch (Exception e) {                e.printStackTrace();            }        }    }}

activity中使用:

@ContentView(R.layout.activity_main)public class MainActivity extends AppCompatActivity {    @ViewInject(R.id.tv_ioc)    private TextView tvIOC;    @ViewInject(R.id.tv_ioc1)    private TextView tvIOC1;    @ViewInject(R.id.tv_ioc2)    private TextView tvIoc2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        InjectUtils.inject(this);        tvIOC.setText("IOC注解  单击事件");        tvIOC1.setText("IOC注解  长按事件");        tvIoc2.setText("fragment IOC注解");    }    @OnClick(R.id.tv_ioc)    public void click(View view){        Toast.makeText(this,tvIOC.getText().toString(),Toast.LENGTH_LONG).show();    }    @OnLongClick(R.id.tv_ioc1)    public boolean longClick(View view){        Toast.makeText(this,tvIOC1.getText().toString(),Toast.LENGTH_LONG).show();        return true;    }    @OnClick(R.id.tv_ioc2)    public void toFragment(View view){        startActivity(new Intent(this,SecondActivity.class));    }}

fragment中使用:

public class IOCFragment extends Fragment{    @ViewInject(R.id.tv_ioc)    private TextView tvIOC;    @ViewInject(R.id.tv_ioc1)    private TextView tvIOC1;    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        View view = inflater.inflate(R.layout.fragment_ioc, container, false);        InjectUtils.inject(view,this);        tvIOC.setText("IOC注解  fragment单击事件");        tvIOC1.setText("IOC注解  fragment长按事件");        return view;    }    @OnClick(R.id.tv_ioc)    public void click(View view){        Toast.makeText(getActivity(),tvIOC.getText().toString(),Toast.LENGTH_LONG).show();    }    @OnLongClick(R.id.tv_ioc1)    public boolean longClick(View view){        Toast.makeText(getActivity(),tvIOC1.getText().toString(),Toast.LENGTH_LONG).show();        return true;    }}

效果如下:
这里写图片描述

源码地址:
https://pan.baidu.com/s/1pL5gK4b

阅读全文
0 0