Lorsqu’ils utilisent le tiroir de navigation, les développeurs Android recommandent que, dans la barre d’action, "seuls les écrans représentés dans le tiroir de navigation aient réellement l’image du tiroir de navigation" . "
Voir ici pour plus de détails: http://youtu.be/F5COhlbpIbY
J'utilise une activité pour contrôler plusieurs niveaux de fragments et je peux obtenir que l'image du tiroir de navigation s'affiche et fonctionne à tous les niveaux.
Lors de la création de fragments de niveau inférieur, je peux appeler le ActionBarDrawerToggle
setDrawerIndicatorEnabled(false)
pour masquer l'image du tiroir de navigation et afficher le caret haut.
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 je rencontre, c’est que lorsque je retourne aux fragments de niveau supérieur, le haut de carat reste affiché au lieu de l’image de tiroir de navigation originale. Avez-vous des suggestions pour "actualiser" la barre d’action sur les fragments de niveau supérieur afin de ré-afficher l’image du tiroir de navigation?
La suggestion de Tom a fonctionné pour moi. Voici ce que j'ai fait:
Cette activité contrôle tous les fragments de l'application.
Lors de la préparation de nouveaux fragments pour en remplacer d'autres, j'ai défini le paramètre DrawerToggle setDrawerIndicatorEnabled(false)
comme suit:
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 une substitution de onBackPressed
, j'ai inversé 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 fragments, j'ai modifié onCreate
et onOptionsItemSelected
comme ceci:
Dans onCreate
, a ajouté setHasOptionsMenu(true)
pour permettre la configuration du menu des options. Définissez également setDisplayHomeAsUpEnabled(true)
pour activer le < dans la barre d'action:
@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);
}
Puis dans onOptionsItemSelected
à chaque fois que le < est pressé, il appelle la onBackPressed()
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 devrez alors implémenter la fonctionnalité de retour manuellement: lorsque l'utilisateur appuie en retour, vous avez un code qui ouvre la pile (par exemple, dans Activity::onBackPressed
override). Ainsi, partout où vous faites cela, vous pouvez inverser la setDrawerIndicatorEnabled
.
C'est simple comme 1-2-3.
Si vous voulez atteindre:
1) Indicateur de tiroir - si aucun fragment ne se trouve dans la pile arrière ou si le tiroir est ouvert
2) Flèche - lorsque certains fragments sont dans la pile 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);
}
}
P.S. Voir Création d'un tiroir de navigation sur Android Développeurs pour d'autres astuces sur le comportement de l'indicateur à 3 lignes.
J'ai utilisé la chose suivante:
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 montante ne fonctionne pas, n’oubliez pas d’ajouter le programme d’écoute:
// Navigation back icon listener
mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onBackPressed();
}
});
J'ai eu quelques problèmes pour mettre en place une navigation dans les tiroirs avec un bouton d'accueil, tout fonctionnait sauf le bouton d'action.
Essayez de gérer la sélection d'élément Accueil dans MainActivity en fonction de l'état du DrawerToggle. De cette façon, vous n'avez pas à ajouter 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);
}
}
SUIVI
La solution proposée par @dzeikei est soignée, mais elle peut être étendue, lorsque vous utilisez des fragments, pour gérer automatiquement le réglage de l'indicateur du 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);
}
}
EDIT
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 signe haut pour le onOptionsItemSelected
.
Dans l'une de mes applications, je devais également gérer le comportement du curseur lorsque l'utilisateur appuyait sur le bouton Précédent. Cela peut être géré en remplaçant 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 encore deux fois executePendingTransactions
qui, dans mon cas, étaient obligatoires, sinon j’ai eu des comportements parfois étranges du haut du corps.
J'ai créé une interface pour l'activité d'hébergement afin de mettre à jour l'état d'affichage du menu hamburger. Pour les fragments de niveau supérieur, je règle la bascule sur true
et pour les fragments pour lesquels je souhaite afficher la flèche vers le haut <, je règle la bascule sur 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.toString() + " 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 cela. La getSupportActionBar().setDisplayHomeAsUpEnabled(false)
n'était pas appelée explicitement et causait le masquage de l'icône du tiroir, même s'il n'y avait aucun élément dans le dossier. Changer la méthode setActionBarArrowDependingOnFragmentsBackStack()
fonctionnait 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. Afficher le bouton Précédent si la pile arrière du fragment est vide. Afficher l'animation matérielle du hamburger en arrière si la pile de fragments 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;
}
Vous pouvez regarder ce petit exemple! https://github.com/oskarko/NavDrawerExample
Si vous jetez un oeil à l'application GMAIL et venez ici pour rechercher l'icône Carret/abordable.
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 ---> cela aura des sous-fragments -> fragment
Dans firstFragment, 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 le tiroir en position True pour activer à nouveau l'icône de la liste de navigation.
Merci Pusp
IMO, en utilisant onNavigateUp () (comme indiqué ici ) dans la solution de riwnodennyk ou Tom est plus propre et semble mieux fonctionner. Il suffit de remplacer le code onOptionsItemSelected par ceci:
@Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
// handle up navigation
return true;
} else {
return super.onSupportNavigateUp();
}
}