文章目录
理解 & “第五组件”
Fragment,碎片。作为Activity界面的一部分,可理解为模块化Activity。是为了解决屏幕适配问题及UI界面的灵活控制而设计的。
Fragment不能独立存在,必须嵌入到Activity中。
Fragment比Activity更节省内存,拥有自己的生命周期,并且可以接收并处理事件。
Fragment使用频率很高,完全不低于其余四大组件。可以被称为第五大组件。
生命周期
-
Fragment 完整生命周期流程
Fragment依赖Activity的存在而存在,Activity的状态决定了Fragment可能接收到的回调函数,故在Activity生命周期中的方法一般与Fragment生命周期中的方法同步执行(且Activity通常先于Fragment执行)。
Fragment比Activity多了几个生命周期的回调方法
onAttach(Activity) 当Fragment与Activity发生关联的时候调用
onCreateView(LayoutInflater, ViewGroup, Bundle) 创建该Fragment的视图
onActivityCreated(Bundle) 当Activity的onCreated方法返回时调用
onDestroyView() 与onCreateView方法相对应,当该Fragment的视图被移除时调用
onDetach() 与onAttach方法相对应,当Fragment与Activity取消关联时调用 -
Fragment切换生命周期变化
- 通过add、hide、show切换Fragment
切换时不执行Fragment生命周期,调用onHiddenChanged方法 - 通过replace切换Fragment
切换时,Fragment都进行了销毁,重建的过程。相当于执行了一次生命周期 - 通过ViewPager切换Fragment
切换时不执行生命周期,调用setUserVisVleHint方法
使用方式 / Fragment加载到Activity的两种方式
Fragment加载到Activity分为动态加载与静态加载两种方式:
- 静态加载
指在Activity布局文件中加载Fragment,使用指定属性name即可。
(1)创建一个类继承Fragment,重写onCreateView方法,来确定Fragment要显示的布局
public class MyFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/*
* 参数1:布局文件的id
* 参数2:容器
* 参数3:是否将这个生成的View添加到这个容器中去
* 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
* */
View v = inflater.inflate(R.layout.item_fragment, container, false);
return v;
}
}
(2)在Activity中声明该类,与普通的View对象一样
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.usher.fragment.MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="Good Boy" />
<fragment
android:id="@+id/myfragment"
android:name="com.usher.fragment.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- 动态加载
指在Activity的java文件中加载Fragment,需要使用FragmentManager,通过FragmentManager获取FragmentTransaction动态添加Fragment。
Activity 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.usher.fragment.MainActivity">
<Button
android:id="@+id/bt_red"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Red" />
<Button
android:id="@+id/bt_blue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Blue" />
<FrameLayout
android:id="@+id/myframelayout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button bt_red;
private Button bt_blue;
private FragmentManager manager;
private MyFragment fragment1;
private MyFragment2 fragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
fragment1 = new MyFragment();
fragment2 = new MyFragment2();
// 初始化FragmentManager对象(Fragment 管理器)
manager = getSupportFragmentManager();
//使用FragmentManager对象用来开启一个Fragment事务
FragmentTransaction transaction = manager.beginTransaction();
// 默认显示fragment1
// Activity 对应布局上FrameLayout 控件,作为Fragment的容器,Fragment通过FrameLayout显示在Acitivity里
transaction.add(R.id.myframelayout, fragment1).commit();
//对bt_red设置监听
bt_red.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.myframelayout, fragment1).commit();
}
});
//对bt_blue设置监听
bt_blue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
FragmentTransaction transaction = manager.beginTransaction();
transaction.replace(R.id.myframelayout, fragment2).commit();
}
});
}
private void initView() {
bt_red = (Button) findViewById(R.id.bt_red);
bt_blue = (Button) findViewById(R.id.bt_blue);
}
}
切换方式
FragmentTransaction对象,transaction的方法主要有以下几种:
方法 | 解释 |
---|---|
add | 向Activity中添加一个Fragment |
remove | 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁 |
replace | 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体 |
hide | 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁 |
show | 显示之前隐藏的Fragment |
commit | 提交事务,在add/replace/hide/show以后都要commit其效果才会在屏幕上显示出来 |
Fragment主要有3种界面切换方式:
MainActivity 布局文件(通过FrameLayout 显示fragment)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#ffffff"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/main_frame_layout"
android:background="#dddddd"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp">
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp">
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ffffff"
android:text="消息"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ffffff"
android:text="联系人"/>
<Button
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#ffffff"
android:text="动态"/>
</LinearLayout>
</LinearLayout>
Fragment 实现逻辑代码
public class MyFragment extends Fragment {
private TextView tv;
private String name;
public MyFragment(String fName){
this.name = fName;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_my,container,false);
tv = (TextView) view.findViewById(R.id.fragment_tv);
tv.setText(name);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通过点击fragment 中的文字,改变文字内容
tv.setText("我变了-" + name);
}
});
return view;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
//三个fragment
private MyFragment f1;
private MyFragment f2;
private MyFragment f3;
//底部三个按钮
private Button foot1;
private Button foot2;
private Button foot3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
foot1 = (Button) findViewById(R.id.btn1);
foot2 = (Button) findViewById(R.id.btn2);
foot3 = (Button) findViewById(R.id.btn3);
foot1.setOnClickListener(this);
foot2.setOnClickListener(this);
foot3.setOnClickListener(this);
//第一次初始化首页默认显示第一个fragment
initFragment1();
}
//显示第一个fragment
private void initFragment1(){
//开启事务,fragment的控制是由事务来实现的
//每处理一个事务都要开启一个新的事务,一个FragmentTransaction只能提交一次,否则会报错:java.lang.IllegalStateException: commit already called
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
//第一种方式(add),初始化fragment并添加到事务中,如果为null就new一个
if(f1 == null){
f1 = new MyFragment("消息");
transaction.add(R.id.main_frame_layout, f1);
}
//隐藏所有fragment
hideFragment(transaction);
//显示需要显示的fragment
transaction.show(f1);
//第二种方式(replace),初始化fragment
// if(f1 == null){
// f1 = new MyFragment("消息");
// }
// transaction.replace(R.id.main_frame_layout, f1);
//提交事务
transaction.commit();
}
//显示第二个fragment
private void initFragment2(){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if(f2 == null){
f2 = new MyFragment("联系人");
transaction.add(R.id.main_frame_layout,f2);
}
hideFragment(transaction);
transaction.show(f2);
// if(f2 == null) {
// f2 = new MyFragment("联系人");
// }
// transaction.replace(R.id.main_frame_layout, f2);
transaction.commit();
}
//显示第三个fragment
private void initFragment3(){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if(f3 == null){
f3 = new MyFragment("动态");
transaction.add(R.id.main_frame_layout,f3);
}
hideFragment(transaction);
transaction.show(f3);
// if(f3 == null) {
// f3 = new MyFragment("动态");
// }
// transaction.replace(R.id.main_frame_layout, f3);
transaction.commit();
}
//隐藏所有的fragment
private void hideFragment(FragmentTransaction transaction){
if(f1 != null){
transaction.hide(f1);
}
if(f2 != null){
transaction.hide(f2);
}
if(f3 != null){
transaction.hide(f3);
}
}
@Override
public void onClick(View v) {
if(v == foot1){
initFragment1();
}else if(v == foot2){
initFragment2();
}else if(v == foot3){
initFragment3();
}
}
}
- 方式1:add/show/hide
初始化时通过add加入Fragment,hide&show方式切换Fragment时将Fragment视图隐藏,所有的Fragment实例都会保存在内存,不会销毁与重建,不执行生命周期。
点击fragment文字改变后,切换到其他fragment并返回时,文字保持点击后改变的文字,而不是初始化的文字。即fragment没有重建,保持之前的fragment。
- 方式2:replace(+addToBackStack)
通过 replace 方法进行替换的时,Fragment 都是进行了销毁,重建的过程,相当于走了一整套的生命周期。
fragment1:onPause() -> onStop -> onDestroyView() -> onDestroy() -> onDetach()
fragment2:onAttach() -> onCreate() -> onCreateView() -> onActivityCreated() -> on Start() -> onResume()
点击fragment文字改变后,切换到其他fragment并返回时,文字恢复为初始化的文字。即fragment重新创建并初始化。
- 方式3:Fragment与ViewPager的搭配使用
通常情况下我们开发应用最常见的使用情况是TabLayout+ViewPager+Fragment的使用方式,下面通过一个实例展示:
- 步骤1:引入工具包
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support:support-v4:27.1.1'
- 步骤2:书写布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activities.TabLayoutActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tl_tabs"
android:layout_width="match_parent"
android:layout_height="40dp" />
<android.support.v4.view.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- 步骤3:实现TabLayout+ViewPager+Fragment
使用流程:
1、创建存储多个Fragment实例的列表
2、创建PagerAdapter实例并关联到Viewpager中
3、将ViewPager关联到Tablayout中
4、根据需求改写Tablayout属性
public class TabLayoutActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener {
TabLayout tabLayout;
ViewPager viewPager;
List<Fragment> fragments = new ArrayList<>();//步骤1:创建存储多个Fragment实例的列表
List<String> titles = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_layout);
tabLayout = findViewById(R.id.tl_tabs);
viewPager = findViewById(R.id.vp_content);
fragments.add(MyFragment.newInstance("11111", "11111"));
fragments.add(MyFragment.newInstance("22222", "22222"));
fragments.add(MyFragment.newInstance("33333", "33333"));
fragments.add(MyFragment.newInstance("44444", "44444"));
fragments.add(MyFragment.newInstance("55555", "55555"));
titles.add("fragment1");
titles.add("fragment2");
titles.add("fragment3");
titles.add("fragment4");
titles.add("fragment5");
//步骤2:创建PagerAdapter实例并关联到Viewpager中
viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
});
//步骤3:将ViewPager关联到Tablayout中
tabLayout.setupWithViewPager(viewPager);
}
@Override
public void onFragmentInteraction(Uri uri) {
}
}
- FragmentStatePagerAdapter与FragmentPagerAdapter
FragmentStatePagerAdapter与FragmentPagerAdapter用法类似,区别在于,卸载不需要的Fragment时,各自的处理方法不同。
Adapter | FragmentStatePagerAdapter | FragmentPagerAdapter |
---|---|---|
切换方式 | 会销毁不需要的Fragment,事务提交后,FragmentManager中的Fragment会被彻底移除,销毁时可在onSaveInstanceState方法中保存信息 | 对于不再需要的Fragment会调用事务的detach方法而非remove方法,仅仅是销毁Fragment的视图,而实例对象仍然保留 |
适用场景 | 更节省内存,当page页面较多时适合使用 | 界面只是少量固定页面,FragmentPagerAdapter更安全 |
ViewPager + Fragment结合使用会出现内存泄漏吗 & 如何解决?
- 原因:
一般ViewPager + Fragment结合使用出现内存泄漏的原因可能用某个集合存储了Fragment的实例,导致当用户滑动ViewPager的时候,某一个Fragment即将面临销毁的时候,由于这个集合持有的它的引用,因此不能被回收掉,如果Fragment里面有大量的数据占据内存,有可能会导致OOM。 - 解决方法:
尽量不要使用集合来存储Fragment实例对象,除非你有良好的二次封装。再就是要做好每一页Fragment的数据缓存问题。
懒加载(结合 ViewPager)
- ViewPager的缓存机制 —— 预加载
ViewPager为了让滑动的时候可以有很好的用户的体验,也就是防止出现卡顿现象,因此它有一个缓存机制。默认情况下,ViewPager会提前创建好当前Fragment旁的两个Fragment,举个例子说也就是如果你当前显示的是编号3的Fragment,那么其实编号2和4的Fragment也已经创建好了,也就是说这3个Fragment都已经执行完 onAttach() -> onResume() 这之间的生命周期函数了。
也可以通过下述函数设置缓存的页面数目:
viewPager.setOffscreenPageLimit(int limit);
- 为什么要懒加载?
Android的View绘制流程是最消耗CPU时间片的操作,尤其是在ViewPager+Fragment的情况下,会对所有的Fragment进行预加载。如果在View绘建的同时还进行多个Fragment的数据加载,那用户体验简直是爆炸(不仅浪费流量,而且还造成不必要的卡顿)因此,需要对Fragment们进行懒加载策略。 - 什么是懒加载?
被动加载,当Fragment页面可见时,才从网络加载数据并显示出来。 - 如何懒加载?
实行懒加载必须满足的条件
- View视图加载完毕,即onCreateView()执行完成
(setUserVisibleHint函数是游离在Fragment生命周期之外的,它的执行有可能早于onCreate和onCreateView,然而既然要时间数据的加载,就必须要在onCreateView创建完视图过后才能使用,不然就会返回空指针崩溃)
- 当前Fragment可见,即setUserVisibleHint()的参数为true
- 初次加载,即防止多次滑动重复加载
故在Fragment全局变量中增加对应的三个标志参数并设置初始值
boolean mIsPrepare = false; //是否加载完成 => onCreateView
boolean mIsVisible= false; //是否用户可见 => setUserVisibleHint
boolean mIsFirstLoad = true; //是否初次加载
当然在onCreateView中确保了View已经准备好时,将mPrepare置为true,在setUserVisibleHint中确保了当前可见时,mIsVisible置为true,第一次加载完毕后则将mIsFirstLoad置为false,避免重复加载。
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mIsPrepare = true;
lazyLoad();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
//isVisibleToUser这个boolean值表示:该Fragment的UI 用户是否可见
if (isVisibleToUser) {
mIsVisible = true;
lazyLoad();
} else {
mIsVisible = false;
}
}
最后,贴上懒加载的lazyLoad()代码(只要标志位改变,就要进行lazyLoad()函数的操作)
private void lazyLoad() {
//这里进行三个条件的判断,如果有一个不满足,都将不进行加载
if (!mIsPrepare || !mIsVisible||!mIsFirstLoad) {
return;
}
loadData();
//数据加载完毕,恢复标记,防止重复加载
mIsFirstLoad = false;
}
private void loadData() {
//这里进行网络请求和数据装载
}
最后,如果Fragment销毁的话,还应该将三个标志位进行默认值初始化:
@Override
public void onDestroyView() {
super.onDestroyView();
mIsFirstLoad=true;
mIsPrepare=false;
mIsVisible = false;
}
为什么在onDestroyView中进行而不是在onDestroy中进行呢?这又要提到之前Adapter的差异,onDestroy并不一定会调用。
Fragment 回退栈(结合replace)
Fragment的回退栈是用来保存每一次Fragment事务发生的变化。在Fragment的时候,如果你不是手动开启回退栈,若用replace方式切换时,是直接销毁再重建;但如果将Fragment任务添加到回退栈,情况就会不一样了,它就有了类似Activity的栈管理方式。
// Fragment1.java
// Fragment1 中按钮事件,切换到fragment2,并将当前事务添加到了回退栈
Fragment2 f2 = new Fragment2();
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.replace(R.id.fl, f2);
//将当前的事务添加到了回退栈
tx.addToBackStack(null);
tx.commit();
Fragment的点击事件里写的是replace方法,相当于remove和add的合体,并且如果不添加事务到回退栈,前一个Fragment实例会被销毁。
这里很明显,我们调用tx.addToBackStack(null)将当前的事务添加到了回退栈,所以FragmentOne实例不会被销毁,但是视图层次依然会被销毁,即会调用onDestoryView和onCreateView(但不会调用onDestroy()和onCreate())。
所以【请注意】,当之后我们从FragmentTwo返回到前一个页面的时候,视图层仍旧是重新按照代码绘制,这里仅仅是是实例没有销毁。
Fragment 与 Activity 通信方式
- 直接访问引用
如果你Activity中包含自己管理的Fragment的引用,可以通过引用直接访问所有的Fragment的public方法 - Activity向Fragment通信——FindFragmentById
若Fragment存在,则可通过getSupportFragmentManager().findFragmentById直接获得Fragment,调用它的共有方法获得数据。否则可通过setArguments(bundle)/getArguments方法传递bundle参数
MainActivity.java
public static class MainActivity extends Activity {
public void onClick(int position) {
MyFragment myFragment = (MyFragment)getSupportFragmentManager().findFragmentById(R.id.my_fragment);
if (articleFrag == null) {
myFragment = new MyFragment();
}
Bundle bundle = new Bundle();
bundle.putString("message", "Msg from Activity");
myFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
// Fragment的回退栈是用来保存每一次Fragment事务发生的变化 如果你将Fragment任务添加到回退栈,当用户点击后退按钮时,将看到上一次的保存的Fragment。
transaction.addToBackStack(null);
transaction.commit();
}
}
}
MyFragment.java
public class MyFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.fragment, container, false);
...
// 步骤1:通过getArgments()获取从Activity传过来的全部值
bundle = this.getArguments();
// 步骤2:获取某一值
message = bundle.getString("message");
System.out.println(message);
return contentView;
}
}
- Fragment向Activity通信——回调函数
在Fragment内定义回调函数,并在Activity中实现回调接口,可实现Fragment向Activity传递数据
step1: 在Menuragment中创建一个接口以及接口对应的set方法:
//MenuFragment.java文件中
public interface OnDataTransmissionListener {
public void dataTransmission(String data);
}
public void setOnDataTransmissionListener(OnDataTransmissionListener mListener) {
this.mListener = mListener;
}
step2: 在MenuFragment中的ListView条目点击事件中进行接口进行接口回调:
//MenuFragment.java文件中
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mListener != null) {
mListener.dataTransmission(mDatas.get(position));
}
}
});
step3: 在MainActivity中根据menuFragment获取到接口的set方法,在这个方法中进行进行数据传递,具体如下:
//在MainActivity.java中
menuFragment.setOnDataTransmissionListener(new MenuFragment.OnDataTransmissionListener() {
@Override
public void dataTransmission(String data) {
mainFragment.setData(data); //注:对应的mainFragment此时应该要用final进行修饰
}
});
- 第三方开源框架:EventBus
- Fragment之间通信——以宿主Activity为桥梁
综合上面两步,可得到Fragment之间的通信方式:
Fragment1通过getActivity获得宿主Activity,并通过Activity实例直接调用FindFragmentById获得Fragment2,并传递数据给Fragment2
遇见的坑
- getActivity空指针
调用getActivity时,当前Fragment已经onDetach宿主Activity,导致空指针异常。常见页面重启(因内存不足/按Home键/横竖屏切换)或pop了Fragment后,由于Fragment的异步任务仍执行,且执行时调用了getActivity方法,会报空指针异常。
应该在Fragment的基类设置一个宿主Activity的全局变量,并在onAttach赋值,使用该全局变量代替getActivity。保证Fragment在被onDetach后,仍有Activity的引用。 - 内存泄露
用集合保存Fragment数组时,销毁Fragment会因为集合中仍存在Fragment的引用而无法销毁,引起内存泄露。常见于ViewPager的使用。 - Can not perform this action after onSaveInstanceState异常
Activity在调用onSaveInstanceState()保存当前Activity的状态后,直到Activity状态恢复之前,若commit 一个FragmentTransaction,就会抛出该异常。因为onSaveInstanceState用于保存当前Activity的现场状态,若之后再调用FragmentTransaction.commit,则该事务没有被作为Activity的状态保存,导致意外的UI状态丢失。Android系统为了避免页面状态的丢失,抛出异常。
为了解决这个异常,应该谨慎地在Activity生命周期调用transaction的commit方法。确保在Activity状态恢复后才会调用。且避免在异步回调中处理transaction。使用commitAllowingStateLoss()虽然可以避免跑出异常,但是存在状态丢失的可能性。 - Fragment界面重叠
当使用add方式添加Fragment并使用hide|show切换时,如果发生页面重启,可能会导致Fragment重叠。这是因为Activity使用onSaveInstanceState方法时,系统保存了Fragment状态。在重启时,FragmentManager会从栈底向栈顶的顺序一次性恢复Fragment,但没有保存Fragment的mHidden属性,使所有的Fragment都以show的形式恢复,导致页面发生重叠。
故应该在创建时,判断savedInstanceState不为空时,通过findFragmentByTag找到对应的Fragment,show需要显示的项目并hide隐藏的项目。