使用Fragment一些建议
封装BaseFragment基类
例如为了实例化View,抽象一个getLayoutId方法,子类无需关心具体的创建操作,父类来做View的创建处理。同时可以提供一个afterCreate抽象函数,在初始化完成之后调用,子类可以做一些初始化的操作,你也可以添加一些常用的方法在基类,例如ShowToast().
public abstract class BaseFragment extends Fragment {
protected View mRootView;@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(null == mRootView){
mRootView = inflater.inflate(getLayoutId(), container, false);
}
return mRootView;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
afterCreate(savedInstanceState);
}
protected abstract int getLayoutId();
protected abstract void afterCreate(Bundle savedInstanceState);
}
使用newInstance(参数) 创建Fragment对象
调用者只需要关心传递的哪些数据,而无需关心传递数据的Key是什么。
public static WeatherFragment newInstance(String cityName) {
Bundle args = new Bundle();
args.putString(cityName,"cityName");
WeatherFragment fragment = new WeatherFragment();
fragment.setArguments(args);
return fragment;
}
Fragment推荐使用setArguments来传递参数,避免在横竖屏切换的时候Fragment自动调用自己的无参构造函数,导致数据丢失。
使用Activity全局变量
如果在Fragment要使用宿主Activity,建议在Fragment中定义一个全局变量,在onAttach中初始化,这不是最好的解决办法,但这可以有效避免一些意外Crash,通常由于getActivity()返回null。
protected Activity mActivity;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.mActivity = activity;
}
不要在Fragment里面保存ViewState
为了让你的代码更加清晰和稳定,最好区分清楚fragment状态保存和view状态保存,如果某个属性属于View,则不要在Fragment中做它的状态保存,除非属性属于Fragment。每一个自定义View都有义务实现状态的保存,可以像EditText一样,设置一个开关来选择是否保存
比如说:android:freezeText=”true/false”。
add()、show()、hide()、replace()使用建议
区别:
show()、hide()是让Fragment中的view setVisibility true or false(即显示还是隐藏),不会调用到Fragment的生命周期函数;
replace()会销毁视图,即调用onDestoryView、onCreateView等一系列生命周期
add()和 replace()不要在同一个阶级的FragmentManager里混搭使用。
使用场景:
如果你频繁使用当前Fragment,建议使用show()、hide();
如果你的app有大量图片,建议使用replace(),因为销毁时能够回收其图片所占用的内存;
onHiddenChanged的回调时机:
当使用add()+show(),hide()跳转新的Fragment时,旧的Fragment回调onHiddenChanged(),不会回调onStop()等生命周期方法,而新的Fragment在创建时是不会回调onHiddenChanged(),这点要切记。
Fragment重叠问题:
使用show(),hide()带来的一个问题就是,如果你不做任何额外处理,在“内存重启”后,Fragment会重叠;(该BUG在support-v4 24.0.0+以上 官方已修复)
Fragment里监听虚拟按键和实体按键的返回事件
给rootView设置一个OnKeyListener来监听key事件
mRootView.setFocusable(true);
mRootView.setFocusableInTouchMode(true);
mRootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
//不一定是要触发返回栈,可以做一些其他的事情,我只是举个栗子。
getActivity().onBackPressed();
return true;
}
return false;
}
});
使用FragmentManager注意点
FragmentManager是用来管理Fragment
FragmentManager栈视图:每个Fragment以及宿主Activity(继承自FragmentActivity)都会在创建时,初始化一个FragmentManager对象,处理好Fragment嵌套问题的关键,就是理清这些不同阶级的栈视图。
下面给出一个简要的关系图:
对于宿主Activity,getSupportFragmentManager()获取的FragmentActivity的FragmentManager对象;
对于Fragment,getFragmentManager()是获取的是父Fragment(如果没有,则是FragmentActivity)的FragmentManager对象,而getChildFragmentManager()是获取自己的FragmentManager对象。
使用FragmentPagerAdapter+ViewPager的注意事项
使用FragmentPagerAdapter+ViewPager时,切换回上一个Fragment页面时(已经初始化完毕),不会回调任何生命周期方法以及onHiddenChanged(),只有setUserVisibleHint(boolean isVisibleToUser)会被回调,所以如果你想进行一些懒加载,需要在这里处理。
在给ViewPager绑定FragmentPagerAdapter时,
new FragmentPagerAdapter(fragmentManager)的FragmentManager,一定要保证正确,如果ViewPager是Activity内的控件,则传递getSupportFragmentManager(),如果是Fragment的控件中,则应该传递getChildFragmentManager()。只要记住ViewPager内的Fragments是当前组件的子Fragment这个原则即可。
你不需要考虑在“内存重启”的情况下,去恢复的Fragments的问题,因为FragmentPagerAdapter已经帮我们处理啦。
是使用单Activity+多Fragment的架构,还是多模块Activity+多Fragment的架构?
单Activity+多Fragment:
一个app仅有一个Activity,界面皆是Frament,Activity作为app容器使用。
优点:性能高,速度最快。参考:新版知乎 、google系app
缺点:逻辑比较复杂,尤其当Fragment之间联动较多或者嵌套较深时,比较复杂。
多模块Activity+多Fragment:
一个模块用一个Activity,比如
1、登录注册流程:
LoginActivity + 登录Fragment + 注册Fragment + 填写信息Fragment + 忘记密码Fragment
2、或者常见的数据展示流程:
DataActivity + 数据列表Fragment + 数据详情Fragment + …
优点:速度快,相比较单Activity+多Fragment,更易维护。
建议:多模块Activity+多Fragment是最合适的架构
最后推荐一个作为参考的demo框架:Fragmentation 源码地址:Github
(第三篇到此结束)