向きの変更に対するAndroidフラグメントのライフサイクル


120

互換性パッケージを使用して、フラグメントを使用して2.2をターゲットにします。

アプリでフラグメントを使用するようにアクティビティを再コーディングした後、方向の変更/状態管理を機能させることができなかったため、単一のFragmentActivityと単一のFragmentを持つ小さなテストアプリを作成しました。

方向の変更からのログは奇妙で、フラグメントOnCreateViewへの複数の呼び出しがあります。

新しいインスタンスを作成するのではなく、フラグメントをデタッチして再アタッチするような何かが明らかに欠けていますが、どこが間違っているのかを示すドキュメントが表示されません。

誰かが私がここで間違っていることについていくつかの光を当てることができますか?ありがとう

向きを変えた後のログは以下の通りです。

Initial creation
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 1
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null


Orientation Change 2
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null

メインアクティビティ(FragmentActivity)

public class FragmentTestActivity extends FragmentActivity {
/** Called when the activity is first created. */

private static final String TAG = "FragmentTest.FragmentTestActivity";


FragmentManager mFragmentManager;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Log.d(TAG, "onCreate");

    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
}

そして断片

public class FragmentOne extends Fragment {

private static final String TAG = "FragmentTest.FragmentOne";

EditText mEditText;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    Log.d(TAG, "OnCreateView");

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false);

    // Retrieve the text editor, and restore the last saved state if needed.
    mEditText = (EditText)v.findViewById(R.id.editText1);

    if (savedInstanceState != null) {

        Log.d(TAG, "OnCreateView->SavedInstanceState not null");

        mEditText.setText(savedInstanceState.getCharSequence("text"));
    }
    else {
        Log.d(TAG,"OnCreateView->SavedInstanceState null");
    }
    return v;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Log.d(TAG, "FragmentOne.onSaveInstanceState");

    // Remember the current text, to restore if we later restart.
    outState.putCharSequence("text", mEditText.getText());
}

マニフェスト

<uses-sdk android:minSdkVersion="8" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:label="@string/app_name"
        android:name=".activities.FragmentTestActivity" 
        android:configChanges="orientation">
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

それが正しい答えかどうかはわかりませんが、フラグメント、add(R.id.fragment_container、fragment、 "MYTAG")を追加するときにタグを使用するか、それを失敗した、replace(R.id.fragment_container、fragment、 "MYTAG ")
Jason

2
いくつかの調査を行っています。メインアクティビティ(FragmentTestActivity)が方向の変更時に再開し、FragmentManagerの新しいインスタンスを取得したら、FindFragmentByTagを実行して、まだ存在するフラグメントを見つけます。これにより、メインアクティビティの再作成中にフラグメントが保持されます。フラグメントを見つけて何もしない場合は、とにかくMainActivityで再表示されます。
MartinS 2011

回答:


189

フラグメントを積み重ねます。

設定が変更されると、古いフラグメントは再作成されるときに新しいアクティビティに追加されます。これは、ほとんどの場合、背面の大きな痛みです。

新しいフラグメントを再作成するのではなく、同じフラグメントを使用してエラーの発生を停止できます。このコードを追加するだけです:

if (savedInstanceState == null) {
    // only create fragment if activity is started for the first time
    mFragmentManager = getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();

    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
} else {        
    // do nothing - fragment is recreated automatically
}

ただし注意が必要です。ライフサイクルが微妙に変化するため、フラグメント内からアクティビティビューにアクセスしようとすると問題が発生します。(フラグメントから親アクティビティからビューを取得するのは簡単ではありません)。


54
「これはほとんどの場合、後方の大きな痛みです」(
いいね

1
ViewPageをFragmentStatePagerAdapterで使用する場合、同じシナリオをどのように処理できますか?
CoDe 2014

5
公式ドキュメントにも同様の主張はありますか?これはガイドで述べられていることと矛盾していません"when the activity is destroyed, so are all fragments"か?以来"When the screen orientation changes, the system destroys and recreates the activity [...]"
cYrus、2015年

4
Cyrus-いいえ、アクティビティは実際に破棄されます。アクティビティに含まれているフラグメントは、アクティビティだけからではなくFragmentManagerで参照されるため、そのまま残り、再度読み込まれます。
Graeme 2015年

4
FragmentManagerで検索した後にフラグメントのonCreateメソッドとonDestroyメソッド、およびそのハッシュコードをログに記録すると、フラグメントが破棄されたことが明確に示されます。自動的に再作成および再接続されるだけです。フラグメントのonCreateメソッドにsetRetainInstance(true)を配置した場合のみ、実際には破棄されません
Lemao1981

87

この本を引用すると、「Androidは一貫したユーザーエクスペリエンスを確保するために、構成の変更によりアクティビティが再開されたときにフラグメントレイアウトと関連するバックスタックを保持します。(p。124)

そして、それに取り組む方法は、最初にフラグメントバックスタックがすでに読み込まれているかどうかを確認し、そうでない場合にのみ新しいフラグメントインスタンスを作成することです。

@Override
public void onCreate(Bundle savedInstanceState) {

        ...    

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) {
        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        fragmentTransaction.add(R.id.fragment_container, new FragmentOne());
        fragmentTransaction.commit();
    }
}

2
あなたはおそらくこれで私に多くの時間を節約してくれました...どうもありがとう。この回答をGraemeの回答と組み合わせると、構成の変更とフラグメントを処理するための完璧なソリューションを得ることができます。
azpublic 2013

10
これは実際には正しい答えであり、マークされたものではありません。どうもありがとうございました!
Uriel Frankel

ViewPager Fragment実装の場合、同じシナリオをどのように処理できるか。
CoDe 2014

この小さな宝石は、私が数日間見てきた問題を解決しました。ありがとうございました!これは間違いなく解決策です。
Whome

1
@SharpEdge複数のフラグメントがある場合、コンテナに追加するときにそれらにタグを付けてから、mFragmentManager.findFragmentByTag(findFragmentByIdではなく)を使用してそれらへの参照を取得する必要があります。これにより、各フラグメントのクラスがわかり、次のことが可能になります。正しくキャスト
k29

10

アクティビティのonCreate()メソッドは、これまで見てきたように、向きが変わった後に呼び出されます。そのため、アクティビティの向きが変わった後にFragmentを追加するFragmentTransactionを実行しないでください。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState==null) {
        //do your stuff
    }
}

Fragmentsは変更する必要があります。


フラグメントが作成および追加された後にインスタンスが保存されることを知っていますか?Fragmentが追加される直前にユーザーが回転した場合、私は帽子を意味しますか?フラグメントの状態を含まないnull以外のsavedInstanceStateがまだあります
ファリッド

4

@OverrideFragmentActivityはを使用して作成できonSaveInstanceState()ます。super.onSaveInstanceState()メソッドでを呼び出さないようにしてください。


2
これは、アクティビティのライフサイクルを壊す可能性が最も高く、この非常に厄介なプロセスに潜在的な問題がさらに発生します。FragmentActivityのソースコードを見てください。すべてのフラグメントの状態をそこに保存しています。
Brian

向きによってアダプタの数が異なるという問題がありました。だから私はいつもデバイスを回転させていくつかのページをスワイプした後、奇妙な状況にありました、私は古いものと間違ったものを得ました。savedInstanceをオンにすると、メモリリークなしで最も効果的に機能します(以前はsetSavedEnabled(false)を使用し、方向が変わるたびに大きなメモリリークが発生しました)
Informatic0re

0

nullpointer例外を回避するように常に努める必要があるため、最初にsaveinstanceメソッドでバンドル情報を確認する必要があります。このブログのリンクを確認するための簡単な説明

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
            == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    } 
}

0

構成を変更すると、フレームワークによってフラグメントの新しいインスタンスが作成され、アクティビティに追加されます。これの代わりに:

FragmentOne fragment = new FragmentOne();

fragmentTransaction.add(R.id.fragment_container, fragment);

これを行う:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) {
    FragmentOne fragment = new FragmentOne();

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG);
}

フレームワークは、setRetainInstance(true)を呼び出さない限り、向きの変更時にFragmentOneの新しいインスタンスを追加することに注意してください。この場合、FragmentOneの古いインスタンスが追加されます。


-1

プロジェクトを行うだけの場合、プロジェクトマネージャーは切り替え機能の画面を実現する必要があると言いますが、画面切り替えロードの異なるレイアウトは必要ありません(レイアウトとレイアウトポートシステムを作成できます)。

あなたは自動的に画面の状態を決定し、対応するレイアウトをロードします)、アクティビティまたはフラグメントを再初期化する必要があるため、ユーザーエクスペリエンスは良好ではなく、画面スイッチでは直接ではありません。Url = YgNfP-vHy-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki&fdf8585a108fide85ff2e4e9a9f9ffqtytJ2

以下のように、layout_weightのレイアウトの方法の重みを使用するレイアウトが前提です。

<LinearLayout
Android:id= "@+id/toplayout"
Android:layout_width= "match_parent"
Android:layout_height= "match_parent"
Android:layout_weight= "2"
Android:orientation= "horizontal" >

したがって、私のアプローチは、画面の切り替え時にビューファイルの新しいレイアウトをロードする必要がなく、onConfigurationChangedダイナミックウェイトでレイアウトを変更することです。次の手順に従います。1最初のセット:アクティビティ属性のAndroidManifest.xml:android:configChanges = "keyboardHidden | orientation | screenSize"画面が切り替わらないようにするには、再読み込みを避けて、onConfigurationChangedメソッドでonConfigurationChanged 2書き換えアクティビティまたはフラグメントを監視できるようにします。

@Override
Public void onConfigurationChanged (Configuration newConfig) {
    Super.onConfigurationChanged (newConfig);
    SetContentView (R.layout.activity_main);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        //On the layout / / weight adjustment
        LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
        LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
        Toplayout.setLayoutParams (LP);
        LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout);
        LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
        Tradespace_layout.setLayoutParams (LP3);
    }
    else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
    {
        //On the layout / / weight adjustment
        LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout);
        LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f);
        Toplayout.setLayoutParams (LP);
        LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout);
        LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f);
        Tradespace_layout.setLayoutParams (LP3);
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.