177 votes

Basculement entre l'image du tiroir de navigation d'Android et le curseur vers le haut lors de l'utilisation de fragments

Lors de l'utilisation du tiroir de navigation, les développeurs d'Android recommandent que dans la barre d'action "seuls les écrans représentés dans le tiroir de navigation aient effectivement l'image du tiroir de navigation" et que "tous les autres écrans aient le traditionnel carat haut".

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 faire en sorte 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 la fonction ActionBarDrawerToggle setDrawerIndicatorEnabled(false) pour cacher l'image du tiroir de navigation et afficher le curseur vers le 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 est que, lorsque je retourne aux fragments de niveau supérieur, le carat Up s'affiche toujours à la place de l'image originale du tiroir de navigation. Avez-vous 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.

Lorsque je prépare de nouveaux fragments pour en remplacer d'autres, je règle le DrawerToggle setDrawerIndicatorEnabled(false) comme ça :

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 surcharge de onBackPressed J'ai inversé l'effet ci-dessus en définissant le DrawerToggle comme suit setDrawerIndicatorEnabled(true) comme ça :

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

Dans les fragments de niveau inférieur

Dans les fragments que j'ai modifiés onCreate y onOptionsItemSelected comme ça :

Sur onCreate ajouté setHasOptionsMenu(true) pour permettre de configurer le 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);
}

Ensuite, dans onOptionsItemSelected chaque fois que le < est pressé, il appelle le onBackPressed() de l'activité pour remonter 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;
        … 
    }

83voto

riwnodennyk Points 1181

C'est aussi simple que 1-2-3.

Si vous voulez réussir :

1) Indicateur de tiroir - lorsqu'il n'y a pas de fragments dans la pile arrière ou que le tiroir est ouvert.

2) Flèche - lorsque certains fragments se trouvent dans la pile arrière.

private FragmentManager.OnBackStackChangedListener
        mOnBackStackChangedListener = new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        setActionBarArrowDependingOnFragmentsBackStack();
    }
};

@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) {
            setActionBarArrowDependingOnFragmentsBackStack();
        }

        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 setActionBarArrowDependingOnFragmentsBackStack() {
    int backStackEntryCount = 
        getSupportFragmentManager().getBackStackEntryCount();
    mDrawerToggle.setDrawerIndicatorEnabled(backStackEntryCount == 0);
}

3) Les deux indicateurs doivent agir 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éer un tiroir de navigation sur Android Developers sur d'autres astuces concernant le comportement de l'indicateur à 3 lignes.

29voto

Tom Points 4834

Vous avez écrit que, pour mettre en œuvre des fragments de niveau inférieur, vous remplacez le fragment existant, par opposition à la mise en œuvre 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 appuie sur le bouton de retour, vous avez du code qui ouvre la pile (par exemple dans Activity::onBackPressed contournement). Donc, où que vous fassiez cela, vous pouvez inverser le setDrawerIndicatorEnabled .

10voto

dzeikei Points 696

Essayez de gérer la sélection de l'élément Accueil dans l'activité principale en fonction de l'état du DrawerToggle. De cette façon, vous n'aurez 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);
    }
}

6voto

HpTerm Points 3412

SUIVRE

La solution donnée par @dzeikei est soignée, mais elle peut être étendue, en utilisant des fragments, pour gérer automatiquement la remise en place de l'indicateur de tiroir lorsque la pile arrière 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 conservés/gérés dans une activité. Le code ci-dessus est écrit une fois dans cette activité, mais ne gère que le signe d'insertion de l'icône de l'utilisateur. onOptionsItemSelected .

Dans l'une de mes applications, j'avais également besoin de gérer le comportement du signe d'insertion lorsque le bouton retour était pressé. Ceci peut être géré en surchargeant 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 du code entre onOptionsItemSelected y onBackPressed ce qui peut être évité en créant une méthode et en appelant cette méthode aux deux endroits.

Notez aussi que j'ai ajouté deux autres fois executePendingTransactions ce qui, dans mon cas, était nécessaire, sinon j'avais parfois des comportements étranges du signe d'insertion.

2voto

Bill Mote Points 4926

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, j'ai défini le basculement à true et pour les fragments pour lesquels je veux afficher la flèche <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);
    }

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X