Fragment的应用场景有很多,比如我们底部一个导航栏,点击导航项显示不同的fragment,或者和ViewPager配合使用等.
比如这样:
package com.example.yzq.testfragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private FrameLayout contentLayout;//容器
private BottomNavigationView mainBottomView;//底部导航
private HomeFragment homeFragment;
private DashboardFragment dashboardFragment;
private NoticeFragment noticeFragment;
private List fragmentList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
L.i("onCreate");
initView();
initFragment();
}
private void initView() {
contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);
mainBottomView.setOnNavigationItemSelectedListener(this);
}
private void initFragment() {
/* 默认显示home fragment*/
homeFragment = new HomeFragment();
addFragment(homeFragment);
showFragment(homeFragment);
}
/*添加fragment*/
private void addFragment(Fragment fragment) {
/*判断该fragment是否已经被添加过 如果没有被添加 则添加*/
if (!fragment.isAdded()) {
getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
/*添加到 fragmentList*/
fragmentList.add(fragment);
}
}
/*显示fragment*/
private void showFragment(Fragment fragment) {
for (Fragment frag : fragmentList) {
if (frag != fragment) {
/*先隐藏其他fragment*/
getSupportFragmentManager().beginTransaction().hide(frag).commit();
}
}
getSupportFragmentManager().beginTransaction().show(fragment).commit();
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
if (homeFragment == null) {
homeFragment = new HomeFragment();
}
addFragment(homeFragment);
showFragment(homeFragment);
break;
case R.id.navigation_dashboard:
if (dashboardFragment == null) {
dashboardFragment = new DashboardFragment();
}
addFragment(dashboardFragment);
showFragment(dashboardFragment);
break;
case R.id.navigation_notifications:
if (noticeFragment == null) {
noticeFragment = new NoticeFragment();
}
addFragment(noticeFragment);
showFragment(noticeFragment);
break;
}
return true;
}
}
上面的代码很简单,就是一个对fragment的添加,隐藏,显示的操作。
示例图:
但是当我们横竖屏切换时,或者锁屏过段时间再进入app时,很可能会遇见重叠的问题.
比如这样:
旋转了个屏幕,发现就已经重叠了。
出现这种问题的原因是:当我们旋转屏幕的时候,activity会被销毁并重新创建,并且在销毁之前执行了onSaveInstanceState(Bundle outState)这个方法。这个方法会保存activity的一些信息,其中就包括添加过的fragment,当activity被重新创建时,会初始化其中的变量,这个时候点击底部导航的话会重新去添加fragment,也就导致了重叠的问题。
首先,我们来打印一下生命周期方法的log,看看是不是这样的。
package com.example.yzq.testfragment;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private FrameLayout contentLayout;//容器
private BottomNavigationView mainBottomView;//底部导航
private HomeFragment homeFragment;
private DashboardFragment dashboardFragment;
private NoticeFragment noticeFragment;
private List fragmentList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
L.i("MainActivity onCreate");
initView();
initFragment();
}
private void initView() {
contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);
mainBottomView.setOnNavigationItemSelectedListener(this);
}
private void initFragment() {
/* 默认显示home fragment*/
homeFragment = new HomeFragment();
addFragment(homeFragment);
showFragment(homeFragment);
}
/*添加fragment*/
private void addFragment(Fragment fragment) {
/*判断该fragment是否已经被添加过 如果没有被添加 则添加*/
if (!fragment.isAdded()) {
getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
/*添加到 fragmentList*/
fragmentList.add(fragment);
}
}
/*显示fragment*/
private void showFragment(Fragment fragment) {
L.i("fragmentList數量:" + fragmentList.size());
for (Fragment frag : fragmentList) {
if (frag != fragment) {
/*先隐藏其他fragment*/
L.i("隱藏" + fragment);
getSupportFragmentManager().beginTransaction().hide(frag).commit();
}
}
getSupportFragmentManager().beginTransaction().show(fragment).commit();
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
if (homeFragment == null) {
L.i("homeFragment 为空 创建");
homeFragment = new HomeFragment();
}
addFragment(homeFragment);
showFragment(homeFragment);
break;
case R.id.navigation_dashboard:
if (dashboardFragment == null) {
L.i("dashboardFragment 为空 创建");
dashboardFragment = new DashboardFragment();
}
addFragment(dashboardFragment);
showFragment(dashboardFragment);
break;
case R.id.navigation_notifications:
if (noticeFragment == null) {
L.i("noticeFragment 为空 创建");
noticeFragment = new NoticeFragment();
}
addFragment(noticeFragment);
showFragment(noticeFragment);
break;
}
return true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
L.i("MainActivity onSaveInstanceState");
super.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
super.onStart();
L.i("MainActivity onStart");
}
@Override
protected void onRestart() {
super.onRestart();
L.i("MainActivity onRestart");
}
@Override
protected void onResume() {
super.onResume();
L.i("MainActivity onResume");
}
@Override
protected void onPause() {
super.onPause();
L.i("MainActivity onPause");
}
@Override
protected void onStop() {
super.onStop();
L.i("MainActivity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
L.i("MainActivity onDestroy");
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
L.i("MainActivity onConfigurationChanged");
}
}
上面的代码只是添加了打印的log。我们来看看打印的日志。
动图有点看不清,我们来看看截屏。
一开始进去时走的就是正常的oncreate到onresume方法。
旋转屏幕时,activity是会被onDestory的,在onDestory之前执行了onSaveInstanceState(Bundle outState)这个方法,这个方法保存了activity的一些状态信息,其中就包括所添加过的fragment。
然后,activity会被重新创建,同样的,之前所添加过的fragment也会被重新创建,而且是默认显示的,当我们点击底部导航的时候,由于activity重新创建了,所以其中的fragment变量是为空的,此时点击导航的时候还会创建新的fragment,这样 ,就导致了重叠的问题。
解决的办法:
其实解决办法很简单,上面的一堆只是让我们了解为什么会出现这种问题。
1.想办法不让activity保存信息。(不推荐)
@Override
protected void onSaveInstanceState(Bundle outState) {
L.i("MainActivity onSaveInstanceState");
//super.onSaveInstanceState(outState);
}
我们可以直接注释掉super.onSaveInstanceState(outState);
这样一来activity就不会保存信息了,但是,从图片上也可以看到,当屏幕旋转时,每次都会跟重新打开一样。如果你旋转屏幕之前显示的是noticeFragment时,屏幕切换后就会自动切换到默认的homeFragment上。这并不是我们想要的结果。
2。旋转屏幕时不让activity走生命周期方法(推荐)
这个方法最简单也最省事,只需要在相应的activity中声明
android:configChanges=“keyboardHidden|orientation|screenSize”> 即可。
声明这个属性后,当我们切换屏幕时,也就不会在走activity的生命周期方法了,也就不会造成fragment重叠的问题了。
还有一种可能也会造成fragment重叠的问题,就是当内存不足时activity被系统回收时,再次进入也会造成重叠的问题,原因也是因为onSaveInstanceState(outState);方法保存了activity的一些数据。
因为现在的手机内存已经很大了,可以说出现这种问题的几率也不是很大了,我们就模拟一下,将手机开发者选项中的不保留活动打开,这样一来,当我们按home键时,activity就会被回收掉。
进入开发者选项很简单, 设置–>关于手机–>连续点击版本号六七次,即可。
再次运行我们来看看结果:
可以看到,但我们点击home键再重新进入app时,就出现了重叠的问题。
因为是系统回收的activity,所以,我们就没法去控制activity不让他走生命周期方法,我们可以从另一个方面着手去解决。
解决办法:
在onSaveInstanceState(outState);中去保存fragment,当activity被恢复时,取出这些fragment即可。
代码如下:
package com.example.yzq.testfragment;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.FrameLayout;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private FrameLayout contentLayout;//容器
private BottomNavigationView mainBottomView;//底部导航
private HomeFragment homeFragment;
private DashboardFragment dashboardFragment;
private NoticeFragment noticeFragment;
private static final String HOME_FRAGMENT_KEY = "homeFragment";
private static final String DASHBOARD_FRAGMENT_KEY = "DashboardFragment";
private static final String NOTICE_FRAGMENT_KEY = "NoticeFragment";
private List fragmentList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
L.i("MainActivity onCreate");
initView();
if (savedInstanceState != null) {
/*获取保存的fragment 没有的话返回null*/
homeFragment = (HomeFragment) getSupportFragmentManager().getFragment(savedInstanceState, HOME_FRAGMENT_KEY);
dashboardFragment = (DashboardFragment) getSupportFragmentManager().getFragment(savedInstanceState, DASHBOARD_FRAGMENT_KEY);
noticeFragment = (NoticeFragment) getSupportFragmentManager().getFragment(savedInstanceState, NOTICE_FRAGMENT_KEY);
addToList(homeFragment);
addToList(dashboardFragment);
addToList(noticeFragment);
} else {
initFragment();
}
}
private void addToList(Fragment fragment) {
if (fragment != null) {
fragmentList.add(fragment);
}
L.i("fragmentList数量" + fragmentList.size());
}
private void initView() {
contentLayout = (FrameLayout) findViewById(R.id.bottom_nav_content);
mainBottomView = (BottomNavigationView) findViewById(R.id.mainBottomView);
mainBottomView.setOnNavigationItemSelectedListener(this);
}
private void initFragment() {
/* 默认显示home fragment*/
homeFragment = new HomeFragment();
addFragment(homeFragment);
showFragment(homeFragment);
}
/*添加fragment*/
private void addFragment(Fragment fragment) {
/*判断该fragment是否已经被添加过 如果没有被添加 则添加*/
if (!fragment.isAdded()) {
getSupportFragmentManager().beginTransaction().add(R.id.bottom_nav_content, fragment).commit();
/*添加到 fragmentList*/
fragmentList.add(fragment);
}
}
/*显示fragment*/
private void showFragment(Fragment fragment) {
L.i("fragmentList數量:" + fragmentList.size());
for (Fragment frag : fragmentList) {
if (frag != fragment) {
/*先隐藏其他fragment*/
L.i("隱藏" + fragment);
getSupportFragmentManager().beginTransaction().hide(frag).commit();
}
}
getSupportFragmentManager().beginTransaction().show(fragment).commit();
}
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
if (homeFragment == null) {
L.i("homeFragment 为空 创建");
homeFragment = new HomeFragment();
}
addFragment(homeFragment);
showFragment(homeFragment);
break;
case R.id.navigation_dashboard:
if (dashboardFragment == null) {
L.i("dashboardFragment 为空 创建");
dashboardFragment = new DashboardFragment();
}
addFragment(dashboardFragment);
showFragment(dashboardFragment);
break;
case R.id.navigation_notifications:
if (noticeFragment == null) {
L.i("noticeFragment 为空 创建");
noticeFragment = new NoticeFragment();
}
addFragment(noticeFragment);
showFragment(noticeFragment);
break;
}
return true;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
L.i("MainActivity onSaveInstanceState");
/*fragment不为空时 保存*/
if (homeFragment != null) {
getSupportFragmentManager().putFragment(outState, HOME_FRAGMENT_KEY, homeFragment);
}
if (dashboardFragment != null) {
getSupportFragmentManager().putFragment(outState, DASHBOARD_FRAGMENT_KEY, dashboardFragment);
}
if (noticeFragment != null) {
getSupportFragmentManager().putFragment(outState, NOTICE_FRAGMENT_KEY, noticeFragment);
}
super.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
super.onStart();
L.i("MainActivity onStart");
}
@Override
protected void onRestart() {
super.onRestart();
L.i("MainActivity onRestart");
}
@Override
protected void onResume() {
super.onResume();
L.i("MainActivity onResume");
}
@Override
protected void onPause() {
super.onPause();
L.i("MainActivity onPause");
}
@Override
protected void onStop() {
super.onStop();
L.i("MainActivity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
L.i("MainActivity onDestroy");
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
L.i("MainActivity onConfigurationChanged");
}
}
主要就是在onSaveInstanceState中将fragment保存起来。
在oncreate的时候判断 一下savedInstanceState 是为空,不为空的话就是有保存的fragment信息,然后将保存的fragment取出来赋给对您的变量即可。
我们再来看看效果图(依旧是在不保留活动的情况下运行):
可以看到,既保留了activity视图状态,也完美解决了fragment的重叠问题。
下面是Demo,有详细注释。需要的话可以下载。
Fragment重叠解决方案Demo
如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!