Fragmentオーバーラップ問題

17552 ワード

最近のプロジェクトでは、Fragmentが重なる問題に遭遇しました.FragmentでTabページを作成すると,アプリケーションに入ると同時に複数のTabコンテンツが表示され,UIが重なることがある.直接backキーがアプリケーションを終了してからアクセスすると、この問題は発生しません.アプリケーションが強制的にシャットダウンされた後(携帯電話の執事ソフトで手動でスイッチを入れたり、メモリを節約するために自動的にアプリケーションをシャットダウンしたり)、再びアプリケーションに入ったとき、毎回この現象が発生していることが後で分かった.通常の表示は正しいですが、現象は私が他のappに切り替えて、しばらく操作した後、現在のappに戻って、Fragmentが重なる可能性があります.具体的には、appは複数のFragment間を切り替え、各Fragmentの状態を保存する必要がある.公式の方法はreplace()を使用してFragmentを置き換えることですが、replace()の呼び出しはFragmentのonCreateView()が呼び出されるため、インタフェースを切り替えると現在の状態を保存できません.そのため、一般的にadd()、hide()とshow()を組み合わせて、Fragmentを保存する状態にします.
このBUG:1は以下のように手動で再現することができる.携帯電話の「設定」-「開発者オプション」-「開く」は活動を保留しない(主にActivityがタイムリーに回収されるシミュレーションに用いる).アプリをバックグラウンドに切り替えて再度開き、ポイントを押してFragmentを切り替えます
最初はadd()、hide()、show()を使ってFragmentを切り替えたときに何か間違っていたのかと思って、重なったバグを解決しようとしたが、結果が出なかった後、googleを通じて原因と解決策を見つけた.
の原因となる
Fragmentの状態で保存すると,システムメモリが不足し,FragmentのホストActivityが回収されると,Fragmentのインスタンスは回収されない.Activityがシステムによって回収されると、onSaveInstance()メソッドがアクティブに呼び出されてビューレイヤ(View Hierarchy)が保存されるので、Activityがナビゲーションによって再構築されると、以前にインスタンス化されたFragmentは依然としてActivityに表示され、このときのFragmentTransactionでは再びfragmentをaddしたものに相当し、hide()とshow()メソッドは以前に保存したfragmentに対して失効している.これらの要因により複数のFragmentが重なった.
解析により,通常のbackキーがアプリケーションを終了するとActivityおよびFragmentオブジェクトが破棄されるため,再びアクセスするとTabに切り替わったときに対応するFragmentオブジェクトが作成されることが分かった.しかし、強制的にアプリケーションを閉じるとActivityは回収されるが、Fragmentオブジェクトは保持され、再びアプリケーションに入ると、システムはFragmentのonAttachメソッドをそれぞれ呼び出してActivityに付加し、
ここでは、適用前のfragmentオブジェクトを強制的に閉じ、2つのfragmentのonCreateViewメソッドが呼び出されるので、この2つのFragmentに対応するView階層がActivityのView階層に追加されます.setSelectionメソッドでは、すべてのfragmentを非表示にしてから選択したオブジェクトが表示されますが、ActivityではFragmentオブジェクトのメンバー変数が初期化されていないため、fragmentオブジェクトが再びインスタンス化され、add、show、hideは2回目に作成されたオブジェクトで操作され、以前保持されていたfragmentオブジェクトのビュー階層はActivityビューに反映され、hideは表示されません.このため,上記のオーバーラップ現象が発生した.
解決方法:
シナリオ1:ActivityのonAttachFragmentメソッドには、onAttachメソッドに対応するFragmentオブジェクトであるfragmentパラメータがあり、このfragmentオブジェクトを判断することによって、我々のFragmentTabXクラスに属し、クラスがまだインスタンス化されていない場合、Activityのメンバー変数mFragmentTabXをそのfragmentオブジェクトに指向することで、元のfragmentオブジェクト上でadd/show/hideを操作することができるので、重複現象はない
シナリオ2:ActivityのonSaveInstanceState()にsuperという文があります.onSaveInstanceState(outState);,Googleはこの言葉について「Always call the superclass so it can save the view hierarchy state」と説明し、「親を呼び出してビューレイヤの状態を保存する」という意味だろう.この言葉を注釈することで,主Activityが様々な理由で回収された場合には以前のfragment stateを保存することなく,重複する問題の解決にも成功する.
  // , 1
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // , tab
        //super.onSaveInstanceState(outState);
    }

    // , 2
    @Override
    public void onAttachFragment(Fragment fragment){

        // , 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;
        }
    }
以上からも分かるように、メモリを節約するためにActivityを回収する必要がある場合、Activity内部に保持されているfragmentは破棄されず、データの保存/復元に使用できる場合があります.次はすべてのコードです.
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) {
        // , tab
//        super.onSaveInstanceState(outState);
    }

    // , 2
    /* @Override
    public void onAttachFragment(Fragment fragment){

        // , 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;
        }
    }*/

}