web-dev-qa-db-ja.com

フラグメントバックスタックでActionBarタイトルを処理しますか?

Activityをロードし、ListFragmentをロードすると、クリックするとレベルがドリルダウンされ、新しいタイプのListFragmentが表示されます。以下のshowFragmentメソッド)。これは、バックスタックに配置されます。

最初は、アクティビティのアクションバーにデフォルトのタイトルが表示されます(つまり、アプリケーションのAndroid:label)。

階層の次のレベルのリストを表示するとき、クリックされたアイテムの名前がアクションバーのタイトルになります。

ただし、押すと Back、元のデフォルトのタイトルを復元したいと思います。これはFragmentTransactionが知っているものではないため、タイトルは復元されません。

FragmentBreadCrumbsについて漠然と読んでいますが、これにはカスタムビューを使用する必要があるようです。私はActionBarSherlockを使用していますが、独自のカスタムタイトルビューを持たないことを好みます。

これを行う最善の方法は何ですか?定型コードの負荷がなく、途中で表示されるタイトルを追跡する必要はありませんか?


protected void showFragment(Fragment f) {
  FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  ft.replace(R.id.fragment_container, f);
  ft.addToBackStack(null);
  ft.commit();
}
72
Christopher Orr

すべてのフラグメントとすべてのアクティビティで、このようにタイトルを変更します。これにより、アクティブなタイトルは常に正しいものになります。

@Override
public void onResume() {
    super.onResume();
    // Set title
    getActivity().getActionBar()
        .setTitle(R.string.thetitle);
}

OnResumeがフラグメント内で呼び出されない場合があります。これらのケースのいくつかでは、以下を使用できます。

public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if(isVisibleToUser) {
        // Set title
        getActivity().getActionBar()
            .setTitle(R.string.thetitle);
    }
}
120
Warpzit

元の答えはかなり古いので、これも助けになるかもしれません。ドキュメントが述べているように、listenerを登録して、ホスティングActivityのバックスタックの変更をリッスンすることができます。

getSupportFragmentManager().addOnBackStackChangedListener(
        new FragmentManager.OnBackStackChangedListener() {
            public void onBackStackChanged() {
                // Update your UI here.
            }
        });

次に、コールバックメソッドで状況を特定し、ActionBarからFragmentにアクセスせずに適切なタイトルを設定します。

FragmentActionBarの存在を知る必要がなく、通常Activityがバックスタックを管理している場所であるため、これはよりエレガントなソリューションです。より適切です。 Fragmentは常に、周囲ではなく、自身のコンテンツによってのみ考慮されるべきです。

ドキュメント のトピックの詳細。

27
Maciej Pigulski

次のように、制御アクティビティにすべての作業を実行させます。

バックスタックイベント(アクティビティのonCreate()内)をリッスンします。

// Change the title back when the fragment is changed
    getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            Fragment fragment = getFragment();
            setTitleFromFragment(fragment);
        }
    });

コンテナから現在のフラグメントを取得します。

/**
 * Returns the currently displayed fragment.
 * @return
 *      Fragment or null.
 */
private Fragment getFragment() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.container);
    return fragment;
}

コンテンツビュー内にフラグメントを設定します。

private void setFragment(Fragment fragment, boolean addToBackStack) {
    // Set the activity title
    setTitleFromFragment(fragment);
    .
    .
    .
}
11
Meanman

Warpzitは正しい。これにより、デバイスの向きが変更されたときのタイトルの問題も解決されます。また、アクションバーにサポートv7を使用している場合、次のようなフラグメントからアクションバーを取得できます。

@Override
public void onResume() {
    super.onResume();
    ((ActionBarActivity)getActivity()).getSupportActionBar().setTitle("Home");
}
7

OSに可能な限り多くの作業を行わせるのが最善です。各フラグメントに.addToBackStack( "title")を使用して適切な名前が付けられていると仮定すると、onBackPressedを次のようにオーバーライドして、目的の動作を実現できます。

// this example uses the AppCompat support library
// and works for dynamic fragment titles
@Override
public void onBackPressed() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    int count = fragmentManager.getBackStackEntryCount();
    if (count <= 1) {
        finish();
    }
    else {
        String title = fragmentManager.getBackStackEntryAt(count-2).getName();
        if (count == 2) {
            // here I am using a NavigationDrawer and open it when transitioning to the initial fragment
            // a second back-press will result in finish() being called above.
            mDrawerLayout.openDrawer(mNavigationDrawerFragment.getView());
        }
        super.onBackPressed();
        Log.v(TAG, "onBackPressed - title="+title);
        getSupportActionBar().setTitle(title);
    }
}
6
Lee Hounshell

Lee approach と同様のソリューションを使用しますが、代わりにonBackStackChanged()メソッドを置き換えます。

最初に、トランザクションをバックスタックに追加するときにフラグメント名を設定します。

_getSupportFragmentManager().beginTransaction()
                .replace(R.id.frame_content, fragment)
                .addToBackStack(fragmentTitle)
                .commit();
_

次に、onBackStackChanged()メソッドをオーバーライドし、最後のバックスタックエントリ名でsetTitle()を呼び出します。

_@Override
public void onBackStackChanged() {
    int lastBackStackEntryCount = getSupportFragmentManager().getBackStackEntryCount() - 1;
    FragmentManager.BackStackEntry lastBackStackEntry =
            getSupportFragmentManager().getBackStackEntryAt(lastBackStackEntryCount);

    setTitle(lastBackStackEntry.getName());
}
_
5
Gnzlt

フラグメント法を使用:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)

Fragmentが出現するたびに呼び出されますが、onResumeは呼び出されません。

4

OnKeyDownで解決できます! bool mainisopen = true <-MainFragment is Visible other Fragment mainisopen = false

ここに私のコードがあります:

public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == false) {
        mainisopen = true;
        HomeFrag fragment = new HomeFrag();
        FragmentTransaction fragmentTransaction =
                getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.fragmet_cont, fragment);
        fragmentTransaction.commit();
        navigationView = (NavigationView) findViewById(R.id.nav_view);
        navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
        navigationView.setNavigationItemSelectedListener(this);
        this.setTitle("Digi - Home"); //Here set the Title back
        return true;
    } else {
        if (keyCode == KeyEvent.KEYCODE_BACK && mainisopen == true) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Wollen sie die App schliessen!");
            builder.setCancelable(true);

            builder.setPositiveButton("Ja!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    System.exit(1);
                }
            });

            builder.setNegativeButton("Nein!", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    Toast.makeText(getApplicationContext(), "Applikation wird fortgesetzt", Toast.LENGTH_SHORT).show();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

}
1
BitDEVil2K16

最良のアプローチは、Android提供されたインターフェースOnBackStackChangedListenerメソッドonBackStackChanged()を使用することです。

ユーザーがナビゲートできる4つのオプションを備えたナビゲーションドロワーがあるとします。その場合、4つのフラグメントがあります。最初にコードを見てから、動作を説明します。

    private int mPreviousBackStackCount = 0;
    private String[] title_name = {"Frag1","Frag2","Frag3","Frag4"};
    Stack<String> mFragPositionTitleDisplayed;

    public class MainActivity extends ActionBarActivity implements FragmentManager.OnBackStackChangedListener
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ....
    ....
    ....
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    mFragPositionTitleDisplayed = new Stack<>();
}

public void displayFragment() {
    Fragment fragment = null;
    String title = getResources().getString(R.string.app_name);
    switch (position) {
        case 0:
            fragment = new Fragment1();
            title = title_name[position];
            break;
        case 1:
            fragment = new Fragment2();
            title = title_name[position];
            break;
        case 2:
            fragment = new Fragment3();
            title = title_name[position];
            break;
        case 3:
            fragment = new Fragment4();
            title = title_name[position];
            break;
        default:
            break;
    }
    if (fragment != null) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction()
                .replace(R.id.container_body, fragment)
                .addToBackStack(null)
                .commit();
        getSupportActionBar().setTitle(title);
    }
}

@Override
public void onBackStackChanged() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    if(mPreviousBackStackCount >= backStackEntryCount) {
        mFragPositionTitleDisplayed.pop();
        if (backStackEntryCount == 0)
            getSupportActionBar().setTitle(R.string.app_name);
        else if (backStackEntryCount > 0) {
            getSupportActionBar().setTitle(mFragPositionTitleDisplayed.peek());
        }
        mPreviousBackStackCount--;
    }
    else{
        mFragPositionTitleDisplayed.Push(title_name[position]);
        mPreviousBackStackCount++;
    }

}   

示されているコードには、displayFragment()メソッドがあります。ここでは、ナビゲーションドロワーから選択されたオプションに基づいてフラグメントを表示します。変数の位置は、ナビゲーションドロワーのListViewまたはRecyclerViewからクリックされたアイテムの位置に対応します。 getSupportActionBar.setTitle(title)でアクションバーのタイトルを適宜設定します。タイトルには適切なタイトル名が格納されます。

ナビゲーションドロワーからアイテムをクリックするたびに、ユーザーがクリックしたアイテムに応じてフラグメントが表示されます。ただし、バックエンド側では、このフラグメントがバックスタックに追加され、メソッドonBackStachChanged()がヒットします。私がしたことは、変数mPreviousBackStackCountを作成し、それを0に初期化したことです。アクションバーのタイトル名を格納する追加のスタックも作成しました。新しいフラグメントをバックスタックに追加するたびに、作成したスタックに対応するタイトル名を追加します。反対側でonBackStackChanged()の戻るボタンを押すたびに、スタックから最後のタイトル名をポップし、タイトルをスタックのpeek()メソッドによって派生した名前に設定します。

例:

Androidバックスタックが空の場合:

ナビゲーションドロワーから選択肢1を押します:onBackStachChanged()が呼び出され、フラグメント1がAndroidバックスタックに追加され、backStackEntryCountが1に設定され、Frag1がスタックにプッシュされ、mFragPositionTitleDisplayedのサイズが1になります。

ナビゲーションドロワーから選択肢2を押します:onBackStachChanged()が呼び出され、フラグメント2がAndroidバックスタックに追加され、backStackEntryCountが2に設定され、Frag2がスタックにプッシュされ、mFragPositionTitleDisplayedのサイズが2になります。

これでAndroidスタックと私のスタックの両方に2つの要素があります。戻るボタンを押すとonBackStackChanged()が呼び出され、backStackEntryCountの値が1になります。スタックの最後のエントリです。つまり、Androidバックスタックには1つのフラグメント-「フラグメント1」のみがあり、スタックには1つのタイトル-「Frag1」しかありません。スタックからアクションバーをそのタイトルに設定します。

要確認:アクションバットタイトルを設定するには、pop()ではなくpeek()を使用します。そうしないと、2つ以上のフラグメントを開いて[戻る]ボタンを押して戻るときにアプリケーションがクラッシュします。

1
Abhishek