Basculer entre l’image de Android Navigation Drawer et Up caret lors de l’utilisation de fragments

Lorsque vous utilisez le tiroir de navigation, les développeurs Android recommandent que dans l’ActionBar “seuls les écrans représentés dans le tiroir de navigation aient réellement l’image du tiroir de navigation” et que “tous les autres écrans aient le carat traditionnel”.

Voir ici pour plus de détails: http://youtu.be/F5COhlbpIbY

J’utilise une activité pour contrôler plusieurs niveaux de fragments et obtenir l’affichage de l’image du tiroir de navigation et son fonctionnement à tous les niveaux.

Lors de la création de fragments de niveau inférieur, je peux appeler ActionBarDrawerToggle setDrawerIndicatorEnabled(false) pour masquer l’image du tiroir de navigation et afficher le setDrawerIndicatorEnabled(false)

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag, "lowerFrag").addToBackStack(null).commit(); 

Le problème que j’ai est quand je retourne aux fragments de haut niveau que le carat supérieur montre toujours au lieu de l’image originale du tiroir de navigation. Des suggestions sur la façon de “rafraîchir” la barre d’action sur les fragments de niveau supérieur pour ré-afficher l’image du tiroir de navigation?


Solution

La suggestion de Tom a fonctionné pour moi. Voici ce que j’ai fait:

Activité principale

Cette activité contrôle tous les fragments de l’application.

Lors de la préparation de nouveaux fragments pour en remplacer d’autres, je mets le DrawerToggle setDrawerIndicatorEnabled(false) comme setDrawerIndicatorEnabled(false) :

 LowerLevelFragment lowFrag = new LowerLevelFragment(); //disable the toggle menu and show up carat theDrawerToggle.setDrawerIndicatorEnabled(false); getSupportFragmentManager().beginTransaction().replace(R.id.frag_layout, lowFrag).addToBackStack(null).commit(); 

Ensuite, dans un remplacement de onBackPressed , j’ai onBackPressed ce qui précède en définissant DrawerToggle sur setDrawerIndicatorEnabled(true) comme ceci:

 @Override public void onBackPressed() { super.onBackPressed(); // turn on the Navigation Drawer image; // this is called in the LowerLevelFragments setDrawerIndicatorEnabled(true) } 

Dans les LowerLevelFragments

Dans les fragments, j’ai modifié onCreate et onOptionsItemSelected comme ceci:

Dans onCreate ajouté setHasOptionsMenu(true) pour activer la configuration du menu d’options. setDisplayHomeAsUpEnabled(true) également setDisplayHomeAsUpEnabled(true) pour activer le < dans la barre d’actions:

 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // needed to indicate that the fragment would // like to add items to the Options Menu setHasOptionsMenu(true); // update the actionbar to show the up carat/affordance getActivity().getActionBar().setDisplayHomeAsUpEnabled(true); } 

Ensuite, dans onOptionsItemSelected chaque fois que vous onOptionsItemSelected <, il appelle onBackPressed() partir de l’activité pour monter d’un niveau dans la hiérarchie et afficher l’image du tiroir de navigation:

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed getActivity().onBackPressed(); return true; … } 

Vous avez écrit que, pour implémenter des fragments de niveau inférieur, vous remplacez le fragment existant, par opposition à l’implémentation du fragment de niveau inférieur dans une nouvelle activité.

Je pense que vous devriez alors implémenter la fonctionnalité de retour manuellement: lorsque l’utilisateur a reculé, vous avez du code qui affiche la stack (par exemple, dans la substitution Activity::onBackPressed ). Donc, où que vous fassiez cela, vous pouvez inverser setDrawerIndicatorEnabled .

C’est facile comme 1-2-3.

Si vous voulez réaliser:

1) Indicateur de tiroir – si aucun fragment ne se trouve dans la stack arrière ou si le tiroir est ouvert

2) Flèche – quand certains fragments sont dans la stack arrière

 private FragmentManager.OnBackStackChangedListener mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } }; @Override protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mDrawerToggle = new ActionBarDrawerToggle( this, mDrawerLayout, R.drawable.ic_navigation_drawer, 0, 0 ) { public void onDrawerClosed(View view) { syncActionBarArrowState(); } public void onDrawerOpened(View drawerView) { mDrawerToggle.setDrawerIndicatorEnabled(true); } }; mDrawerLayout.setDrawerListener(mDrawerToggle); getSupportFragmentManager().addOnBackStackChangedListener(mOnBackStackChangedListener); } @Override protected void onDestroy() { getSupportFragmentManager().removeOnBackStackChangedListener(mOnBackStackChangedListener); super.onDestroy(); } private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0); } 

3) Les deux indicateurs agissent en fonction de leur forme

 @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } else if (item.getItemId() == android.R.id.home && getSupportFragmentManager().popBackStackImmediate()) { return true; } else { return super.onOptionsItemSelected(item); } } 

PS Voir Création d’un tiroir de navigation sur les développeurs Android sur d’autres conseils concernant le comportement de l’indicateur à 3 lignes.

J’ai utilisé la prochaine chose:

 getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if(getSupportFragmentManager().getBackStackEntryCount() > 0){ mDrawerToggle.setDrawerIndicatorEnabled(false); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } else { getSupportActionBar().setDisplayHomeAsUpEnabled(false); mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

Si le bouton de la barre d’action ne fonctionne pas, n’oubliez pas d’append l’écouteur:

 // Navigation back icon listener mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); 

J’ai du mal à implémenter une navigation dans un tiroir avec un bouton d’accueil, tout fonctionnait sauf le bouton d’action.

Essayez de gérer la sélection d’éléments Home dans MainActivity en fonction de l’état du DrawerToggle. De cette façon, vous n’avez pas besoin d’append le même code à chaque fragment.

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: onBackPressed(); return true; default: return super.onOptionsItemSelected(item); } } 

SUIVRE

La solution donnée par @dzeikei est soignée, mais il peut être étendu, lors de l’utilisation de fragments, pour gérer automatiquement la restauration de l’indicateur de tiroir lorsque le backstack est vide.

 @Override public boolean onOptionsItemSelected(MenuItem item) { // Only handle with DrawerToggle if the drawer indicator is enabled. if (mDrawerToggle.isDrawerIndicatorEnabled() && mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { // Handle home button in non-drawer mode case android.R.id.home: // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.popBackStack(); // Make sure transactions are finished before reading backstack count fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } return true; default: return super.onOptionsItemSelected(item); } } 

MODIFIER

Pour la question de @JJD.

Les fragments sont détenus / gérés dans une activité. Le code ci-dessus est écrit une fois dans cette activité, mais ne gère que le onOptionsItemSelected pour onOptionsItemSelected .

Dans l'une de mes applications, je devais également gérer le comportement du curseur supérieur lorsque le bouton Retour était pressé. Cela peut être onBackPressed .

 @Override public void onBackPressed() { // Use getSupportFragmentManager() to support older devices FragmentManager fragmentManager = getFragmentManager(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ super.onBackPressed(); } else { fragmentManager.executePendingTransactions(); fragmentManager.popBackStack(); fragmentManager.executePendingTransactions(); if (fragmentManager.getBackStackEntryCount() < 1){ mDrawerToggle.setDrawerIndicatorEnabled(true); } } }; 

Notez la duplication de code entre onOptionsItemSelected et onBackPressed qui peut être évitée en créant une méthode et en appelant cette méthode aux deux endroits.

Notez également que j'ajoute deux autres fois executePendingTransactions qui, dans mon cas, étaient obligatoires ou alors j'avais parfois des comportements étranges du haut vers le bas.

J’ai créé une interface pour l’activité d’hébergement pour mettre à jour l’état d’affichage du menu hamburger. Pour les fragments de niveau supérieur, je mets la bascule sur true et pour les fragments pour lesquels je veux afficher la flèche haut false .

 public class SomeFragment extends Fragment { public interface OnFragmentInteractionListener { public void showDrawerToggle(boolean showDrawerToggle); } private OnFragmentInteractionListener mListener; @Override public void onAttach(Activity activity) { super.onAttach(activity); try { this.mListener = (OnFragmentInteractionListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toSsortingng() + " must implement OnFragmentInteractionListener"); } } @Override public void onResume() { super.onResume(); mListener.showDrawerToggle(false); } } 

Puis dans mon activité …

 public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener { private ActionBarDrawerToggle mDrawerToggle; public void showDrawerToggle(boolean showDrawerIndicator) { mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator); } } 

Cette réponse fonctionnait mais il y avait un petit problème avec elle. Le getSupportActionBar().setDisplayHomeAsUpEnabled(false) n’a pas été appelé explicitement et il a masqué l’icône du tiroir même s’il n’y avait aucun élément dans le backstack, donc changer la méthode setActionBarArrowDependingOnFragmentsBackStack() fonctionné pour moi.

 private void setActionBarArrowDependingOnFragmentsBackStack() { int backStackEntryCount = getSupportFragmentManager() .getBackStackEntryCount(); // If there are no items in the back stack if (backStackEntryCount == 0) { // Please make sure that UP CARAT is Hidden otherwise Drawer icon // wont display getSupportActionBar().setDisplayHomeAsUpEnabled(false); // Display the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(true); } else { // Show the Up carat getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Hide the Drawer Icon mDrawerToggle.setDrawerIndicatorEnabled(false); } } 

La logique est claire. Affiche le bouton retour si la stack arrière du fragment est claire. Afficher l’animation de hamburger-back matériel si la stack de fragment n’est pas claire.

 getSupportFragmentManager().addOnBackStackChangedListener( new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { syncActionBarArrowState(); } } ); private void syncActionBarArrowState() { int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); mNavigationDrawerFragment.setDrawerIndicatorEnabled(backStackEntryCount == 0); } //add these in Your NavigationDrawer fragment class public void setDrawerIndicatorEnabled(boolean flag){ ActionBar actionBar = getActionBar(); if (!flag) { mDrawerToggle.setDrawerIndicatorEnabled(false); actionBar.setDisplayHomeAsUpEnabled(true); mDrawerToggle.setHomeAsUpIndicator(getColoredArrow()); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } mDrawerToggle.syncState(); getActivity().supportInvalidateOptionsMenu(); } //download back button from this(https://www.google.com/design/icons/) website and add to your project private Drawable getColoredArrow() { Drawable arrowDrawable = ContextCompat.getDrawable(getActivity(), R.drawable.ic_arrow_back_black_24dp); Drawable wrapped = DrawableCompat.wrap(arrowDrawable); if (arrowDrawable != null && wrapped != null) { // This should avoid tinting all the arrows arrowDrawable.mutate(); DrawableCompat.setTint(wrapped, Color.GRAY); } return wrapped; } 

Si vous regardez l’application GMAIL et venez ici pour rechercher l’icône carret / budget.

Je vous demanderais de le faire, aucune des réponses ci-dessus n’était claire. J’ai pu modifier la réponse acceptée.

  • NavigationDrawer -> Listview contient des sous-fragments.


  • les sous-fragments seront listés comme ceci

  • firstFragment == position 0 —> ceci aura des sous-fragments -> fragment

  • deuxièmeFragment
  • troisième fragment et ainsi de suite ….

En premier lieu, vous avez un autre fragment.

Appelez ceci sur DrawerActivity

 getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { @Override public void onBackStackChanged() { if (getFragmentManager().getBackStackEntryCount() > 0) { mDrawerToggle.setDrawerIndicatorEnabled(false); } else { mDrawerToggle.setDrawerIndicatorEnabled(true); } } }); 

et en fragment

  setHasOptionsMenu(true); @Override public boolean onOptionsItemSelected(MenuItem item) { // Get item selected and deal with it switch (item.getItemId()) { case android.R.id.home: //called when the up affordance/carat in actionbar is pressed activity.onBackPressed(); return true; } return false; } 

Sur la méthode d’activité du tiroir OnBackPressed, définissez la bascule du tiroir sur true pour activer à nouveau l’icône de la liste de navigation.

Merci Pusp

Vous pouvez regarder ce petit exemple! https://github.com/oskarko/NavDrawerExample

IMO, en utilisant onNavigateUp () (comme montré ici ) dans la solution de riwnodennyk ou de Tom est plus propre et semble fonctionner mieux. Remplacez simplement le code onOptionsItemSelected par ceci:

 @Override public boolean onSupportNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { // handle up navigation return true; } else { return super.onSupportNavigateUp(); } }