web-dev-qa-db-fra.com

Basculement entre Android Image du tiroir de navigation et Haut caret lors de l'utilisation de fragments

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 ActionBarDrawerTogglesetDrawerIndicatorEnabled(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?


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, 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 le LowerLevelFragments

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;
        … 
    }
177
EvilAsh

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.

28
Tom

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.

83
riwnodennyk

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);
                }
            }
        });
14
Yuriy Sych

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.

12
Burrich

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);
    }
}
10
dzeikei

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.

6
HpTerm

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

}
2
Bill Mote

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

    }
2

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;
}
1
kml_ckr

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

1
oskarko

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

  • deuxième fragment
  • thirdFragment et ainsi de suite ....

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

1
Tot

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();
    }
}
0
0101100101