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反射的知识,感觉自己对这一块还很陌生,正好借这个机会稍微了解了一下这方面的知识,写下记录希望对大家有用。
网友评论