美文网首页Android开发经验谈
为Fragment也写一个ViewInject

为Fragment也写一个ViewInject

作者: 月塘路 | 来源:发表于2018-10-28 15:45 被阅读8次

    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<? extends View> viewClass = fragment.getView().getClass();
            Class<? extends Fragment> 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

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