Fragment 重疊(重影)問題

在最近做的項目中,遇到了 Fragment 重疊的問題。在用Fragment做Tab頁面,發(fā)現(xiàn)有時候進入應用會同時顯示多個Tab內容,UI發(fā)生重疊。直接back鍵退出應用再進入時,則沒有出現(xiàn)該問題。

后面才知道,當應用被強行關閉后(通過手機管家軟件手動強關,或系統(tǒng)為節(jié)省內存自動關閉應用),再次進入應用時,每次都有這現(xiàn)象。正常情況下顯示是對的,現(xiàn)象發(fā)生在我切換到其他的app,操作一會之后,再回到當前的app,有一定幾率會出現(xiàn) Fragment 重疊的現(xiàn)象。具體的情況是,app 需要在多個 Fragment 間切換,并且保存每個 Fragment 的狀態(tài)。官方的方法是使用 replace() 來替換 Fragment,但是 replace() 的調用會導致 Fragment 的 onCreateView() 被調用,所以切換界面時會無法保存當前的狀態(tài)。因此一般采用 add()、hide()與 show()配合,來達到保存 Fragment 的狀態(tài)。

可以通過下面的方式進行手動重現(xiàn)這個BUG:

1.手機的 “設置” - “開發(fā)者選項” - 打開”不保留活動”(主要用于模擬Activity被及時回收)

2.把 app 切換到后臺,再重新打開,通過點按不同的 tab 來切換 Fragment

起初我以為是我在使用add()、hide() 、show() 切換 Fragment 的時候有什么地方使用的不對,嘗試去解決重疊的 bug,無果后,還是通過 google 找出了原因和解決方案。

原因

使用 Fragment 的狀態(tài)保存,當系統(tǒng)內存不足,F(xiàn)ragment 的宿主 Activity 回收的時候,F(xiàn)ragment 的實例并沒有隨之被回收。Activity 被系統(tǒng)回收時,會主動調用 onSaveInstance() 方法來保存視圖層(View Hierarchy),所以當 Activity 通過導航再次被重建時,之前被實例化過的 Fragment 依然會出現(xiàn)在 Activity 中,此時的 FragmentTransaction 中的相當于又再次 add 了 fragment 進去的,hide()和show()方法對之前保存的fragment已經失效了。綜上這些因素導致了多個Fragment重疊在一起。

通過分析發(fā)現(xiàn),正常back鍵退出應用時,Activity及Fragment對象會被銷毀,因此再次進入時會在切換到Tab時創(chuàng)建對應的Fragment對象。

但是當強行關閉應用后,Activity雖然被回收,但Fragment對象仍然保持,再次進入應用時,系統(tǒng)會分別調用Fragment的onAttach方法將其附加到Activity上,

這里對應的就是強行關閉應用前的fragment對象,

后面會分別調用兩個fragment的onCreateView方法,因此這兩個Fragment對應的View層次結構都會加到Activity的View層次中。

雖然setSelection方法會把所有fragment先隱藏再顯示選中的對象,但由于此時Activity中Fragment對象的成員變量還未初始化,因此會再次實例化fragment對象,

之后add、show及hide的都是在第二次創(chuàng)建的對象上操作的,而之前被保持的fragment對象的視圖層次已經反映到Activity視圖中并且不會被hide,因此發(fā)生了上述重疊現(xiàn)象。

解決方法:

方案一:

在Activity的onAttachFragment方法中,有一個fragment參數(shù),它就是onAttach方法對應的Fragment對象,

通過判斷這個fragment對象,如果屬于我們的FragmentTabX類并且該類還未被實例化過,則將Activity的成員變量mFragmentTabX指向該fragment對象,這樣就可以在原來的fragment對象上操作add/show/hide,因此不會有重疊現(xiàn)象

方案二:

Activity 中的 onSaveInstanceState() 里面有一句super.onSaveInstanceState(outState);,Google 對于這句話的解釋是 “Always call the superclass so it can save the view hierarchy state”,大概意思是“總是執(zhí)行這句代碼來調用父類去保存視圖層的狀態(tài)”。通過注釋掉這句話,這樣主 Activity 因為種種原因被回收的時候就不會保存之前的 fragment state,也可以成功解決重疊的問題。


//解決重疊,方法1
@Override
protectedvoidonSaveInstanceState(Bundle outState) {
//如果用以下這種做法則不保存狀態(tài),再次進來的話會顯示默認tab
//super.onSaveInstanceState(outState);
}
//解決重疊,方法2
@Override
publicvoidonAttachFragment(Fragment fragment){
//當前的界面的保存狀態(tài),只是重新讓新的Fragment指向了原本未被銷毀的fragment,它就是onAttach方法對應的Fragment對象
if(FragmentA ==null&& fragmentinstanceofFragmentTabA)
{

FragmentA = (FragmentTabA)fragment;

}else if(FragmentB ==null&& fragmentinstanceofFragmentTabB){

FragmentB = (FragmentTabB)fragment;

}else if(FragmentC ==null&& fragmentinstanceofFragmentTabC){

FragmentC = (FragmentTabC)fragment;

}else if(FragmentD ==null&& fragmentinstanceofFragmentTabD){

FragmentD = (FragmentTabD)fragment;

}

}

通過以上也可知,某些情況當系統(tǒng)需要將Activity回收以便節(jié)省內存時,Activity內部保持的fragment不會被銷毀,可用于保存/恢復數(shù)據(jù)。

下面是全部的代碼:

package com.mobile.margaret.margaretapplication.activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import com.mobile.margaret.margaretapplication.R;
import com.mobile.margaret.margaretapplication.fragment.FragmentTabA;
import com.mobile.margaret.margaretapplication.fragment.FragmentTabB;
import com.mobile.margaret.margaretapplication.fragment.FragmentTabC;
import com.mobile.margaret.margaretapplication.fragment.FragmentTabD;

/** * @author Liz_Miller 
* @Title: TabActivity_Fragment_RadioGroup
 * @Package com.mobile.margaret.margaretapplication.activity 
* @Description: ${todo}(用一句話描述該文件做什么)
 * @date 2016/5/30 15:07 */

public class TabActivity_Fragment_RadioGroup extends FragmentActivity{ 

private static final String TAG = "Liz_Miller"; 
private FrameLayout mHomeContent;
 private RadioGroup mHomeRadioGroup;
 private RadioButton mHomeA;
 private RadioButton mHomeB; 
private RadioButton mHomeC;
 private RadioButton mHomeD; 
private Fragment FragmentA; 
private Fragment FragmentB; 
private Fragment FragmentC; 
private Fragment FragmentD; 
private int tabIds[] = new int[]{ R.id.id_tab_weixin, R.id.id_tab_frd, R.id.id_tab_address, R.id.id_tab_setting, };

 @Override
 protected void onCreate(Bundle savedInstanceState){
   
     super.onCreate(savedInstanceState);
     Log.d(TAG,"onCreate");     
    getWindow() .setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,               
    WindowManager.LayoutParams.FLAG_FULLSCREEN);   
    requestWindowFeature(Window.FEATURE_NO_TITLE);       
    setContentView(R.layout.layout_fragment_radiogroup_maintab); 
    initViews();
    initEvents();
 }

 //初始化控件
 public void initViews()
{ 
   mHomeContent = (FrameLayout)findViewById(R.id.fragment_content); 
   mHomeRadioGroup = (RadioGroup)findViewById(R.id.mHomeRadioGroup);   
   mHomeA = (RadioButton)findViewById(R.id.id_tab_weixin);
  mHomeB = (RadioButton)findViewById(R.id.id_tab_frd); 
  mHomeC = (RadioButton)findViewById(R.id.id_tab_address);
  mHomeD = (RadioButton)findViewById(R.id.id_tab_setting);
 } 
@Override 
public void onAttachFragment(Fragment fragment) {
    // TODO Auto-generated method stub 
        super.onAttachFragment(fragment);
        Log.d(TAG,"onAttachFragment");
 } 

@Override 
protected void onDestroy() { 
  // TODO Auto-generated method stub
      super.onDestroy(); 
      Log.d(TAG,"onDestroy");
 }
 @Override
 protected void onPause() {
   // TODO Auto-generated method stub
       super.onPause(); 
       Log.d(TAG,"onPause"); 
}

 @Override
 protected void onResume() { 
   // TODO Auto-generated method stub 
       super.onResume();
       Log.d(TAG,"onResume");
 } 

@Override 
protected void onStart() { 
// TODO Auto-generated method stub
    super.onStart();
    Log.d(TAG, "onStart"); 
} 

@Override  
protected void onStop() { 
// TODO Auto-generated method stub
     super.onStop();
    Log.d(TAG, "onStop"); 
} 

public void initEvents()
{
    mHomeRadioGroup.setOnCheckedChangeListener(
    new RadioGroup.OnCheckedChangeListener() 
    {
          @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
                     for (int i = 0; i < tabIds.length; i++) { 
                        if (tabIds[i] == checkedId) {
                               setSelection(i);
                               break; 
                        }
        } 
     } 
  });
 setSelection(0);
} 

private void setSelection(int position){
  FragmentManager fm = getSupportFragmentManager(); 
  FragmentTransaction mfragmentTransaction = fm.beginTransaction();          
  hideAllFragments(mfragmentTransaction);
 switch (position){
      case 0:
          mHomeA.setSelected(true); 
          if (FragmentA == null) {
               FragmentA = new FragmentTabA();          
               mfragmentTransaction.add(R.id.fragment_content, FragmentA);
          }
         else { 
            mfragmentTransaction.show(FragmentA); 
          } 
          break; 
      case 1:
           mHomeB.setSelected(true); 
           if (FragmentB == null) { 
                FragmentB = new FragmentTabB();  
               mfragmentTransaction.add(R.id.fragment_content, FragmentB);
            }
            else {
                 mfragmentTransaction.show(FragmentB); 
             }
             break; 
      case 2: 
          mHomeC.setSelected(true); 
          if (FragmentC == null) {
               FragmentC = new FragmentTabC();       
              mfragmentTransaction.add(R.id.fragment_content, FragmentC);
           }
           else {
               mfragmentTransaction.show(FragmentC);
            } 
            break;
     case 3: 
           mHomeD.setSelected(true);
           if (FragmentD == null) { 
                FragmentD = new FragmentTabD();           
                mfragmentTransaction.add(R.id.fragment_content, FragmentD); 
           }
           else { 
              mfragmentTransaction.show(FragmentD); 
            } 
            break; 
     default: 
          break;
  } 
  mfragmentTransaction.commit();
}

 private void hideAllFragments(FragmentTransaction ft){
       if (FragmentA != null) 
       { 
          ft.hide(FragmentA); 
          mHomeA.setSelected(false); 
       } 
       if (FragmentB != null) 
      {
         ft.hide(FragmentB); 
         mHomeB.setSelected(false);
      } 
      if (FragmentC != null)
      {
          ft.hide(FragmentC);
          mHomeC.setSelected(false); 
      } 
       if (FragmentD != null)
       {
           ft.hide(FragmentD);
           mHomeD.setSelected(false);
       }
 } 

//解決重疊,方法1
 @Override 
protected void onSaveInstanceState(Bundle outState) { 
         //如果用以下這種做法則不保存狀態(tài),再次進來的話會顯示默認的tab
          // super.onSaveInstanceState(outState); 
}


 //解決重疊,方法2
 /* @Override
 public void onAttachFragment(Fragment fragment){
 //當前的界面的保存狀態(tài),只是從新讓新的Fragment指向了原本未被銷毀的fragment,它就是onAttach方法對應的Fragment對象
 if(FragmentA == null && fragment instanceof FragmentTabA)
  { 
        FragmentA = (FragmentTabA)fragment;
  }else if(FragmentB == null && fragment instanceof FragmentTabB)
  {      
       FragmentB = (FragmentTabB)fragment;
 }else if(FragmentC == null && fragment instanceof FragmentTabC)
  { 
      FragmentC = (FragmentTabC)fragment; 
}else if(FragmentD == null && fragment instanceof FragmentTabD)
{  
     FragmentD = (FragmentTabD)fragment;
} 
}
*/
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容