天天看點

為Fragment也寫一個ViewInject1.簡介2.動手為Fragment寫一個inject()方法3.在fragment中調用fragmentInject()方法4.總結

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反射的知識,感覺自己對這一塊還很陌生,正好借這個機會稍微了解了一下這方面的知識,寫下記錄希望對大家有用。