title: 為Fragment也寫一個ViewInject
date: 2018-10-28 11:29:51
tags: fragment ViewInject
1.簡介
鴻洋博客:https://blog.csdn.net/lmj623565791/article/details/39269193
前段時間根據(jù)鴻洋的博客,寫了一個ViewInject,本以為可以和findViewById說再見了,但是今天在fragment中使用ViewInject,卻報了空指針的錯誤,原來是控件沒有實例化就調(diào)用了。找了一下,我把目光放到ViewInjectUtil.inject(getActivity)上。
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ViewInjectUtils.inject(getActivity());//問題可能出在這里
init();
setListener();
}
ViewInject只是一個標(biāo)志,實際上是通過inject方法調(diào)用其他方法,讀取ViewInect中的值,實現(xiàn)動態(tài)綁定。因為Activity中可以直接調(diào)用findViewById()方法,而Fragment中則需要先獲得所綁定的視圖View,然后通過view.findViewById()來獲取控件。
所以有必要給fragment重新寫一個inject()方法,網(wǎng)上沒有太多關(guān)于Fragment的inject(),干脆自己動手寫一個,以下代碼基于鴻洋博客中代碼加以修改。
2.動手為Fragment寫一個inject()方法
在ViewInjectUtils.java中添加方法fragmentInject()
public static void fragmentInject(Fragment fragment){
fragmentInjectViews(fragment);
}
因為是給Fragment用的,所以傳入的參數(shù)是一個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調(diào)用findViewById()獲取到控件對象,返回獲取到控件對象的fragmentlayout
Object resView = method.invoke(fragment.getView(), viewId);
field.setAccessible(true);
//將fragmentLayout設(shè)置給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()來取得對應(yīng)ID的view。
主要思路:控件的引用聲明在fragment中,但要通過view的findViewById方法獲取控件對象,所以我們既需要fragment,又需要其對應(yīng)的View。第2處不同中,獲取到了fragment中聲明的字段(其中包括我們聲明的控件引用),第3處不同中,我們通過view獲取到findViewById()方法,之后通過讓view執(zhí)行findViewById()方法獲取到對應(yīng)的控件對象,返回的resView是一個獲取到控件對象的fragmentLayout,最后把它set給我們的fragment。
3.在fragment中調(diào)用fragmentInject()方法
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
ViewInjectUtils.fragmentInject(this);
init();
setListener();
}
我們在onActivityCreated中調(diào)用fragmentInject()方法,因為fragmrent在onCreateView()中已經(jīng)綁定了所屬的view,之后通過getView()方法取得view。
4.總結(jié)
這里用到的是java反射的知識,感覺自己對這一塊還很陌生,正好借這個機(jī)會稍微了解了一下這方面的知識,寫下記錄希望對大家有用。