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