fragment重疊的完美解決方案
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<Fragment> 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<Fragment> 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
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
宣告這個屬性後,當我們切換螢幕時,也就不會在走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<Fragment> 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的重疊問題。
好了,希望能幫到你,如果你覺得對你有幫助,麻煩動動手指頂一下,算是對我的一個鼓勵,謝謝!