为Fragment也写一个ViewInject


title: 为Fragment也写一个ViewInject
date: 2018-10-28 11:29:51
tags: fragment ViewInject


1.简介

鸿洋博客:https://blog.csdn.net/lmj623565791/article/details/39269193
前段时间根据鸿洋的博客,写了一个ViewInject,本以为可以和findViewById说再见了,但是今天在fragment中使用ViewInject,却报了空指针的错误,原来是控件没有实例化就调用了。找了一下,我把目光放到ViewInjectUtil.inject(getActivity)上。

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ViewInjectUtils.inject(getActivity());//问题可能出在这里
        init();
        setListener();
    }

ViewInject只是一个标志,实际上是通过inject方法调用其他方法,读取ViewInect中的值,实现动态绑定。因为Activity中可以直接调用findViewById()方法,而Fragment中则需要先获得所绑定的视图View,然后通过view.findViewById()来获取控件。
所以有必要给fragment重新写一个inject()方法,网上没有太多关于Fragment的inject(),干脆自己动手写一个,以下代码基于鸿洋博客中代码加以修改。

2.动手为Fragment写一个inject()方法

在ViewInjectUtils.java中添加方法fragmentInject()

public static void fragmentInject(Fragment fragment){
    fragmentInjectViews(fragment);
}

因为是给Fragment用的,所以传入的参数是一个Fragment,不再是activity。fragmentInjectView()是具体的注入方法,接下来我们来写fragmentInjectView()

private static void fragmentInjectViews(Fragment fragment){
        Class viewClass = fragment.getView().getClass();
        Class fragmentClass = fragment.getClass();
        Field[] fields = fragmentClass.getDeclaredFields();
        //遍历所有成员对象
        for(Field field : fields){
            ViewInject viewInjectAnnotation = field.getAnnotation(ViewInject.class);
            if (viewInjectAnnotation != null) {
                int viewId = viewInjectAnnotation.value();
                if(viewId != -1){
                    Log.e("TAG", "injectViews: "+viewId);
                    //初始化View
                    try {
                        //通过view获取fingViewById()方法
                        Method method = viewClass.getMethod(METHOD_FIND_VIEW_BY_ID, int.class);
                        //让view调用findViewById()获取到控件对象,返回获取到控件对象的fragmentlayout
                        Object resView = method.invoke(fragment.getView(), viewId);
                        field.setAccessible(true);
                        //将fragmentLayout设置给fragment.
                        field.set(fragment, resView);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

不同的地方有3处:

  1. 声明了viewClass和fragmentClass两个变量。viewClass是fragment所绑定view的反射类,fragmentClass就是我们写的Fragment类的反射类。
  2. fields是通过fragmentClass获取的Fragment类中的字段
  3. 在"初始化View"下面,Method是通过viewClass获得的,resView也是通过fragment.getView()来取得对应ID的view。

主要思路:控件的引用声明在fragment中,但要通过view的findViewById方法获取控件对象,所以我们既需要fragment,又需要其对应的View。第2处不同中,获取到了fragment中声明的字段(其中包括我们声明的控件引用),第3处不同中,我们通过view获取到findViewById()方法,之后通过让view执行findViewById()方法获取到对应的控件对象,返回的resView是一个获取到控件对象的fragmentLayout,最后把它set给我们的fragment。

3.在fragment中调用fragmentInject()方法

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ViewInjectUtils.fragmentInject(this);
        init();
        setListener();
    }

我们在onActivityCreated中调用fragmentInject()方法,因为fragmrent在onCreateView()中已经绑定了所属的view,之后通过getView()方法取得view。

4.总结

这里用到的是java反射的知识,感觉自己对这一块还很陌生,正好借这个机会稍微了解了一下这方面的知识,写下记录希望对大家有用。

你可能感兴趣的:(为Fragment也写一个ViewInject)