web-dev-qa-db-fra.com

Gestion du titre ActionBar avec le fragment de pile arrière?

J'ai un Activity où je charge un ListFragment et, en cliquant dessus, un niveau plus bas et un nouveau type de ListFragment s'affiche, remplaçant celui d'origine (en utilisant la méthode showFragment ci-dessous). Ceci est placé sur la pile arrière.

Au début, l’activité affiche le titre par défaut dans la barre d’action (c’est-à-dire qu’il est défini automatiquement en fonction du Android:label).

Lors de l'affichage de la liste du niveau suivant dans la hiérarchie, le nom de l'élément sur lequel l'utilisateur a cliqué doit devenir le titre de la barre d'actions.

Cependant, lorsque vous appuyez sur Back, Je voudrais que le titre par défaut original soit restauré. Ce n'est pas quelque chose que FragmentTransaction sait, donc le titre n'est pas restauré.

J'ai lu vaguement à propos de FragmentBreadCrumbs, mais cela semble nécessiter l'utilisation d'une vue personnalisée. J'utilise ActionBarSherlock et préférerais ne pas avoir ma propre vue de titre personnalisée.

Quelle est la meilleure façon de faire cela? Est-il possible sans charge de code passe-partout et de devoir garder une trace des titres affichés en cours de route?


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

Dans chaque fragment et chaque activité, je change le titre de cette manière. De cette façon, le titre actif sera toujours correct:

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

Dans certains cas, onResume n'est pas appelé à l'intérieur des fragments. Dans certains cas, nous pouvons utiliser:

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

Comme la réponse initiale est assez ancienne, cela pourrait également être utile. Comme indiqué dans la documentation, on peut vouloir enregistrer un listener pour écouter les modifications de la pile arrière dans l'hébergement Activity:

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

Identifiez ensuite la situation dans la méthode de rappel et définissez un titre propre, sans accéder à la ActionBar à partir de la Fragment.

Ceci est une solution plus élégante car le Fragment ne doit pas nécessairement connaître l'existence de ActionBar et Activity est généralement l'endroit où le backstack est géré. il semble être plus approprié. Fragment devrait à tout moment être considéré que par son propre contenu, pas son environnement.

Plus d'informations sur le sujet dans le documentation .

27
Maciej Pigulski

Laissez l'activité de contrôle faire tout le travail comme suit:

Écoutez les événements de pile (dans onCreate () of activity):

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

Obtenir le fragment actuel du conteneur:

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

Définissez le fragment dans la vue du contenu:

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

Warpzit a raison. Cela résout également le problème du titre lorsque l'orientation de l'appareil est modifiée. De plus, si vous utilisez la prise en charge v7 pour la barre d'action, vous pouvez obtenir la barre d'action d'un fragment comme ceci:

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

Il est préférable de laisser le système d'exploitation effectuer le plus de travail possible. En supposant que chaque fragment soit correctement nommé en utilisant .addToBackStack ("title"), vous pouvez alors remplacer onBackPressed pour obtenir le comportement souhaité:

// 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

J'utilise une solution similaire à la méthode Lee , mais en remplaçant la méthode onBackStackChanged().

Tout d'abord, je définis le nom du fragment lors de l'ajout de la transaction à la pile arrière.

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

Ensuite, je substitue la méthode onBackStackChanged() et j'appelle setTitle() avec le dernier nom d'entrée de pile.

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

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

Utilisez la méthode des fragments:

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

Il est appelé à chaque apparition de fragment, mais onResume ne l’est pas.

4
Jurijs Turjanskis

Vous pouvez résoudre avec onKeyDown! J'ai un bool mainisopen = true <- MainFragment est visible autre Fragment mainisopen = false

et voici mon code:

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

La meilleure approche consiste à utiliser la méthode onBackStackChanged () fournie par Interface OnBackStackChangedListener Android fournie).

Disons que nous avons un tiroir de navigation avec 4 options vers lesquelles l'utilisateur peut naviguer. Dans ce cas, nous aurons 4 fragments. Voyons le code en premier et ensuite je vais expliquer le fonctionnement.

    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++;
    }

}   

Dans le code affiché, nous avons la méthode displayFragment (). J'affiche ici le fragment sur la base d'une option choisie dans le tiroir de navigation. La position variable correspond à la position de l'élément sur lequel l'utilisateur a cliqué depuis ListView ou RecyclerView dans le tiroir de navigation. Je règle le titre de la barre d’action en conséquence avec getSupportActionBar.setTitle (title), où le titre stocke le nom du titre approprié.

Chaque fois que nous cliquons sur l'élément du tiroir de navigation, un fragment s'affiche en fonction de l'élément sur lequel l'utilisateur a cliqué. Mais à l'arrière, ce fragment est ajouté à la pile de disques et la méthode onBackStachChanged () est touchée. Ce que j'ai fait est que j'ai créé une variable mPreviousBackStackCount et que je l'ai initialisée à 0. J'ai également créé une pile supplémentaire qui stockera les noms de titre de la barre d'action. Chaque fois que j'ajoute un nouveau fragment au backstack, j'ajoute le nom du titre correspondant à la pile créée. Sur le côté opposé, chaque fois que j'appuie sur le bouton Précédent, onBackStackChanged () est appelé et je supprime le dernier nom du titre de ma pile et le définit sur le nom dérivé de la méthode peek () de la pile.

Exemple:

Disons que notre Android backstack est vide:

Appuyez sur le choix 1 dans le tiroir de navigation: onBackStachChanged () est appelé et le fragment 1 est ajouté à Android backstack, backStackEntryCount est défini sur 1 et Frag1 est ajouté à ma pile et la taille de mFragPositionTitleDisplayed devient 1.

Appuyez sur le choix 2 dans le tiroir de navigation: onBackStachChanged () est appelé et le fragment 2 est ajouté à Android backstack, backStackEntryCount est défini sur 2 et Frag2 est transféré dans la pile et la taille de mFragPositionTitleDisplayed devient 2.

Maintenant, nous avons 2 éléments dans la pile Android et dans ma pile. Lorsque vous appuyez sur le bouton Précédent, onBackStackChanged () est appelé et la valeur de backStackEntryCount est 1. Le code entre la partie if et sort. la dernière entrée de ma pile. Ainsi, le Android backstack ne contient qu'un fragment - "Fragment 1" et ma pile ne contient qu'un titre - "Frag1". Maintenant, je jette un œil () au titre. de ma pile et définir la barre d'action à ce titre.

N'oubliez pas: pour définir le titre de la batte d'action, utilisez peek () et non pas pop (), sinon votre application se bloquerait lorsque vous ouvririez plus de 2 fragments et tenteriez de revenir en arrière en appuyant sur le bouton Précédent.

1
Abhishek