1. ホーム
  2. android

[解決済み] BackStackから既存のFragmentをレジュームする方法

2022-04-25 18:53:16

質問

フラグメントの使い方を勉強中です。私は、3つのインスタンスの Fragment で初期化されているものです。このようなアクティビティにフラグメントを追加しています。

宣言して初期化する。

Fragment A = new AFragment();
Fragment B = new BFragment();
Fragment C = new CFragment();

置き換える/追加する

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, A);
ft.addToBackStack(null);
ft.commit();

これらのスニペットは正しく動作しています。すべてのフラグメントはアクティビティにアタッチされ、問題なくバックスタックに保存されます。

で、起動すると A , C で、次に B の場合、スタックはこのようになります。

| |
|B|
|C|
|A|
___

そして「戻る」ボタンを押すと B は破棄され C が再開される。

しかし、フラグメントを起動すると A 二度目は、バックスタックから再開するのではなく、バックスタックの一番上に追加されます。

| |
|A|
|C|
|A|
___

しかし、私は再開したい A で、その上にあるすべてのフラグメントを破棄します (ある場合)。実は、私はデフォルトのバックスタック動作が好きなだけなのです。

どうすれば実現できるのでしょうか?

期待すること: ( A を再開し、トップフラグメントを破棄する必要があります)

| |
| |
| |
|A|
___

編集:(提案者:A--Cさん)

これは私が試したコードです。

private void selectItem(int position) {
        Fragment problemSearch = null, problemStatistics = null;
        FragmentManager manager = getSupportFragmentManager();
        FragmentTransaction ft = manager.beginTransaction();
        String backStateName = null;
        Fragment fragmentName = null;
        boolean fragmentPopped = false;
        switch (position) {
        case 0:
            fragmentName = profile;
            break;
        case 1:
            fragmentName = submissionStatistics;
            break;
        case 2:
            fragmentName = solvedProblemLevel;
            break;
        case 3:
            fragmentName = latestSubmissions;
            break;
        case 4:
            fragmentName = CPExercise;
            break;
        case 5:
            Bundle bundle = new Bundle();
            bundle.putInt("problem_no", problemNo);
            problemSearch = new ProblemWebView();
            problemSearch.setArguments(bundle);
            fragmentName = problemSearch;
            break;
        case 6:
            fragmentName = rankList;
            break;
        case 7:
            fragmentName = liveSubmissions;
            break;
        case 8:
            Bundle bundles = new Bundle();
            bundles.putInt("problem_no", problemNo);
            problemStatistics = new ProblemStatistics();
            problemStatistics.setArguments(bundles);
            fragmentName = problemStatistics;
        default:
            break;
        }
        backStateName = fragmentName.getClass().getName();
        fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
        if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
        }
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();

        // I am using drawer layout
        mDrawerList.setItemChecked(position, true);
        setTitle(title[position]);
        mDrawerLayout.closeDrawer(mDrawerList);
    }

問題は、起動時に A で、次に B で、'戻る'を押してください。 B が削除され A が再開され、2回目の「戻る」を押すとアプリが終了するはずです。しかし、空白のウィンドウが表示され、閉じるには3回目の「戻る」を押さなければなりません。

また、起動時に A そして B では C では B また...

期待される。

| |
| |
|B|
|A|
___

実際の

| |
|B|
|B|
|A|
___

をオーバーライドする必要があります。 onBackPressed() それとも、何か見落としているのでしょうか?

解決方法は?

を読むと ドキュメント トランザクション名かcommitで提供されるidに基づいてバックスタックをポップする方法があります。名前 かもしれない というのは、変更される可能性のある番号を追跡する必要がなく、quot; unique back stack entry"のロジックを強化することができるからです。

バックスタックエントリーは、1つの Fragment で、バックステート名をフラグメントのクラス名とします。 getClass().getName() ). そして Fragment を使用します。 popBackStackImmediate() メソッドを使用します。もし true を返したら、バックスタックに Fragment のインスタンスが存在することを意味します。そうでない場合は、実際にFragmentの置換ロジックを実行します。

private void replaceFragment (Fragment fragment){
  String backStateName = fragment.getClass().getName();

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment);
    ft.addToBackStack(backStateName);
    ft.commit();
  }
}


EDIT

問題は、Aを起動し、次にBを起動し、戻るボタンを押すと、Bが表示されることです。 が削除され、Aが再開される。そして、もう一度戻るボタンを押すことで アプリを終了します。しかし、空白のウィンドウが表示され、もう一回押す必要があります。 をクリックすると閉じます。

これは FragmentTransaction がバックスタックに追加されるのは、後でフラグメントを上にポップできるようにするためです。これを手っ取り早く解決するには onBackPressed() が1つしかない場合、アクティビティを終了させます。 Fragment

@Override
public void onBackPressed(){
  if (getSupportFragmentManager().getBackStackEntryCount() == 1){
    finish();
  }
  else {
    super.onBackPressed();
  }
}

バックスタックエントリーの重複について、フラグメントがポップされていない場合に置き換えるという条件文は、明らかに 異なる 私の元のコードスニペットのものよりも。あなたがやっていることは、バックスタックがポップされたかどうかに関係なく、バックスタックに追加しているのです。

このようなものが、あなたの望むものに近いはずです。

private void replaceFragment (Fragment fragment){
  String backStateName =  fragment.getClass().getName();
  String fragmentTag = backStateName;

  FragmentManager manager = getSupportFragmentManager();
  boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);

  if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
    FragmentTransaction ft = manager.beginTransaction();
    ft.replace(R.id.content_frame, fragment, fragmentTag);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
  } 
}

表示されている状態で同じフラグメントを選択すると、エントリーの重複も発生するため、条件を少し変更しました。

実装しています。

私は、更新された replaceFragment() メソッドを分解してください。すべてのロジックはこのメソッドに含まれており、パーツを動かすと問題が発生する可能性があります。

つまり、更新された replaceFragment() メソッドを自分のクラスに追加し

backStateName = fragmentName.getClass().getName();
fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
if (!fragmentPopped) {
            ft.replace(R.id.content_frame, fragmentName);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(backStateName);
ft.commit();

ということで、単純に

replaceFragment (fragmentName);


EDIT #2

バックスタックが変更されたときにドロワーを更新するには、Fragmentを受け取ってクラス名を比較するメソッドを作ります。一致するものがあれば、タイトルと選択範囲を変更する。また OnBackStackChangedListener で、有効なFragmentがあれば、updateメソッドを呼び出すようにします。

例えば、Activityの onCreate() を追加します。

getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {

  @Override
  public void onBackStackChanged() {
    Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
    if (f != null){
      updateTitleAndDrawer (f);
    }

  }
});

そしてもう一つの方法。

private void updateTitleAndDrawer (Fragment fragment){
  String fragClassName = fragment.getClass().getName();

  if (fragClassName.equals(A.class.getName())){
    setTitle ("A");
    //set selected item position, etc
  }
  else if (fragClassName.equals(B.class.getName())){
    setTitle ("B");
    //set selected item position, etc
  }
  else if (fragClassName.equals(C.class.getName())){
    setTitle ("C");
    //set selected item position, etc
  }
}

これで、バックスタックが変更されるたびに、タイトルとチェック位置が可視化されます。 Fragment .