為Fragment也寫一個ViewInject
title: 為Fragment也寫一個ViewInject
date: 2018-10-28 11:29:51
tags: fragment ViewInject
1.簡介
鴻洋部落格:ofollow,noindex">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處:
- 聲明瞭viewClass和fragmentClass兩個變數。viewClass是fragment所繫結view的反射類,fragmentClass就是我們寫的Fragment類的反射類。
- fields是通過fragmentClass獲取的Fragment類中的欄位
- 在"初始化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反射的知識,感覺自己對這一塊還很陌生,正好借這個機會稍微瞭解了一下這方面的知識,寫下記錄希望對大家有用。