web-dev-qa-db-fra.com

Barre d'outils - Passage du tiroir au bouton Précédent avec une seule activité

Je cherche depuis un moment à changer d'icône d'ouverture/fermeture de tiroir (passant d'un hamburger à la flèche) à une simple flèche vers l'arrière. Mon application pour le moment n'a qu'une activité qui permute entre plusieurs fragments. À un moment donné, je souhaite faire la transition entre l’un des fragments principaux (c’est-à-dire un des fragments du tiroir) et un fragment hiérarchiquement situé sous le fragment précédent (c.-à-d. Un fragment "Ajouter un nouveau"). Dans ce nouveau fragment, je souhaite que la barre d’outils affiche le bouton Précédent au lieu du bouton Tiroir.

Cela fait un moment que je regarde et que j'essaie différentes solutions. Voici les plus notables:

Pour le moment, je pense à une méthode longue et ardue pour créer une icône personnalisée que je cache et que je la montre (et que je cache/montre l’icône du tiroir natif). Cependant, existe-t-il un meilleur moyen de basculer entre les boutons du tiroir et du dos? 

En tant que question parallèle, j’ai examiné les documents relatifs à la conception de matériaux et quelques exemples ont un X dans le coin supérieur gauche. Dans quelle mesure est-ce différent d'implémenter le tiroir par rapport aux boutons Précédent/Suivant?

Merci ~

Modifier:

Je peux comprendre comment remplacer l'icône, mais comment puis-je obtenir l'événement click?

Jusqu'ici, c'était ma meilleure piste:

Ce que j'ai essayé maintenant:

  • Désactiver le DrawerToggle si nécessaire (c.-à-d. mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);)
  • Ajout de journaux dans onOptionsItemSelected dans mon NavigationDrawerFragment, mon activité, ainsi que le DialogFragment que je suis en train de tester, qui sont exécutés si item.getItemId() == Android.R.id.home est true. Aucune de ces instructions de journal ne s'éteint

Pour un meilleur contexte, j'ai maintenant un fragment en plein écran qui ajoute un bouton "Enregistrer" au menu et modifie l'icône du tiroir en "X". Le fragment peut obtenir l'événement de menu de sauvegarde, mais même l'activité et le tiroir ne peuvent pas l'être lorsque le X est exploité.

Edit2:

Comme demandé, voici du code. Notez que tout cela vient de ce dépôt Github , sur lequel je travaille activement (notez que j’ai quelques fonctions inutiles ici ou là à partir de tests rapides).

ActivityMain:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Add the toolbar
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    if (mToolbar != null) {
        setSupportActionBar(mToolbar);
    }

    // Initialize the drawer
    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);

    // Set up the drawer
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout),
            mToolbar);

    // TODO: Check if this helps to catch the main toolbar button click
    getSupportActionBar().setDisplayShowHomeEnabled(true);

    // Get the titles for the Toolbar
    mTitles = getResources().getStringArray(R.array.drawer_items);

    mDrawerPosition = -1;
    if (savedInstanceState == null) {
        // If there was no saved position, then the default, starting position should be used
        forceChangeItemSelected(0);
    }
    else {
        // Otherwise, get the saved position from the bundle
        int position = savedInstanceState.getInt(KEY_DRAWERPOS);
        mNavigationDrawerFragment.setSelectedItem(position);
        // Title needs to be re-set
        getSupportActionBar().setTitle(mTitles[position]);
    }

    // If I include the below bit, then the DrawerToggle doesn't function
        // I don't know how to switch it back and forth
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(LOG_TAG, "Navigation was clicked");

        }
    });
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    Log.d(LOG_TAG, "Activity responding to menu click...");
    if(item.getItemId() == Android.R.id.home) Log.d(LOG_TAG, "Activity got it....");

    // If the fragment is supposed to handle things, then let it
    if(mIsFragmentHandlingMenus) return false;

    int id = item.getItemId();
    if(id == R.id.save) {
        // This isn't implemented! If chosen, then there's a bug!
        Log.e(LOG_TAG, "onOptionsItemSelected: Save was selected!");
    }

    return super.onOptionsItemSelected(item);
}

@Override
public void fragmentHandlingMenus(boolean isFragmentHandlingMenus) {
    // Simply store the setting
    mIsFragmentHandlingMenus = isFragmentHandlingMenus;

    // Toggle the drawer as necessary
    mNavigationDrawerFragment.toggleDrawerUse(!isFragmentHandlingMenus);
}

NavigationDrawerFragment :

public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // TODO: Enable/Disable the drawer even being able to open/close
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Drawer responding to menu click...");
    if(item.getItemId() == Android.R.id.home) Log.d(LOGTAG, "Drawer got it....");
    if (mDrawerToggle.onOptionsItemSelected(item)) {
        return true;
    }

    return super.onOptionsItemSelected(item);
}

GoalAdderFragment :

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    // Allow this fragment to handle toolbar menu items
    setHasOptionsMenu(true);

    // Set up the toolbar
    ((ActionBarActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setHomeAsUpIndicator(Android.R.drawable.ic_menu_close_clear_cancel);
    ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(getResources().getString(R.string.title_addgoal));
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    // Cache the Activity as the frag handler if necessary
    if(mFragHandler == null)
        mFragHandler = (TransactionHandler.FragmentTransactionHandler) getActivity();
    // Tell the Activity to let fragments handle the menu events
    mFragHandler.fragmentHandlingMenus(true);
}

@Override
public void onDetach() {
    super.onDetach();

    // Tell the Activity that it can now handle menu events once again
    mFragHandler.fragmentHandlingMenus(false);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.save_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    Log.d(LOGTAG, "Item id: " + item.getItemId() + " | Save id: " + R.id.save);
    Toast.makeText(getActivity(), "Fragment activated!", Toast.LENGTH_SHORT).show();

    switch (item.getItemId()) {
        case R.id.save:
            return true;
        case Android.R.id.home:
            return true;
        default:
            break;
    }

    return false;
}

Solution:

C'est la solution ultime à laquelle j'ai abouti, à l'aide de la réponse acceptée ci-dessous:

NavigationDrawerFragment:

private View.OnClickListener mOriginalListener;

public void setUp(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
     /* Rest of setting up code */

     // Save the default listener after setting everything else up
     mOriginalListener = mDrawerToggle.getToolbarNavigationClickListener();
}

// Tells the toolbar+drawer to switch to the up button or switch back to the normal drawer
public void toggleDrawerUse(boolean useDrawer) {
    // Enable/Disable the icon being used by the drawer
    mDrawerToggle.setDrawerIndicatorEnabled(useDrawer);

    // Switch between the listeners as necessary
    if(useDrawer)
        mDrawerToggle.setToolbarNavigationClickListener(mOriginalListener);
    else
        mDrawerToggle.setToolbarNavigationClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "Custom listener", Toast.LENGTH_SHORT).show();
            }
        });
}
39
Jawad

Ce n'est probablement pas ce que vous voudriez entendre, mais même d'un point de vue conceptuel, je choisirais une nouvelle activité plutôt qu'un fragment. 

Votre activité principale étant strictement liée au tiroir, le chargement d'un nouveau fragment sans accès au tiroir n'a aucun sens pour moi (mais n'hésitez pas à attendre d'autres réponses si vous le pensez). Une nouvelle activité résoudrait les deux problèmes, puisqu'elle n'aurait pas de tiroir et pourrait être un enfant du principal.

Votre question secondaire est également très pertinente. Une activité "Ajouter un nouveau" pourrait parfaitement s'intégrer au modèle visuel de "dialogue en plein écran" des directives. Voir:

http://www.google.com/design/spec/components/dialogs.html#dialogs-full-screen-dialogs

Ce modèle comporte un bouton "Enregistrer", positif en haut à droite et un X. Sur le plan conceptuel, le bouton X consiste à annuler/abandonner un processus, plutôt que de naviguer dans le backstack. Cela signifie que vous rejetez quelque chose sans laisser aucune action se produire. Cela correspond bien à ce que vous voulez faire.

Du point de vue de la conception, il est facile à créer avec une nouvelle Activity, qui peut rester au-dessus des autres. En outre, si le but des fragments est de pouvoir représenter deux ou plusieurs à la fois dans des tablettes et un écran plus grand - encore une fois - je ne serais pas aussi heureux avec un vieux fragment à ma gauche et un fragment "Ajouter un nouveau" à droite .

Plutôt - sur des tablettes - je choisirais un dialogue flottant, comme suggéré par les instructions.

http://www.google.com/design/spec/components/dialogs.html#dialogs-confirmation-dialogs

Donc, activité en plein écran avec un bouton X pour les téléphones et dialogue flottant (avec des boutons en bas) pour les tablettes. C’est pour moi l’approche la plus cohérente.


Je recommande de lire tout le lien. Sur la différence entre <- et X,

Le X diffère de la flèche vers le haut, qui est utilisée lorsque l’état de la vue est constamment sauvegardé ou lorsque les applications disposent de fonctionnalités de brouillon ou de sauvegarde automatique. Par exemple, une flèche vers le haut est utilisée dans les paramètres, car toutes les modifications sont validées immédiatement. 

Et aussi

Toucher le X dans cet exemple de configuration annulera toutes les modifications. Les modifications seront enregistrées uniquement en touchant Enregistrer.

12
natario

Mettez ce code dans onCreate() de votre Activity. Fonctionne bien pour moi. Même en utilisant compileSdk 23 et supérieur.

    drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    if(toolbar != null) {
        toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        toggle.syncState();
        drawer.setDrawerListener(toggle);
        getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
            @Override
            public void onBackStackChanged() {
                if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
                    getSupportActionBar().setDisplayHomeAsUpEnabled(true); // show back button
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            onBackPressed();
                        }
                    });
                } else {
                    //show hamburger
                    getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                    toggle.syncState();
                    toolbar.setNavigationOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            drawer.openDrawer(GravityCompat.START);
                        }
                    });
                }
            }
        });
43
matusalem

Cela devrait fonctionner même pour les dernières API 24 .

Dans votre activité onCreate() procédez comme suit:

final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
final DrawerLayout drawer = (DrawerLayout) view.findViewById(R.id.drawer_layout);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);

final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, 
    R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    @Override
    public void onBackStackChanged() {
        if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
            toggle.setDrawerIndicatorEnabled(false);
            toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    getSupportFragmentManager().popBackStack();
                }
            });
        } else {
            toggle.setDrawerIndicatorEnabled(true);
            toggle.setToolbarNavigationClickListener(originalToolbarListener);
        }
    }
});
27
mixel

La réponse de @matusalem fonctionne très bien. Il ne me reste qu’un élément à ajouter: soyez prudent, vous pouvez également ouvrir le tiroir en le glissant du côté gauche de l’écran. Pour certains, cela peut être souhaité, mais pour moi, je désactivais le tiroir car cela n’avait pas de sens dans un fragment autre que mon fragment principal. Le balayage est facilement désactivé ici - Tiroir de navigation - Désactiver le balayage

Cela appartient probablement à un commentaire pour la réponse, mais je n'ai pas assez de réputation. Mes excuses.

4
tpankake

J'ai eu le même problème avec la permutation entre le menu hamburger et la flèche arrière dans la même activité lors du changement de fragments. Voici ma solution de travail, espérons que cela aide quelqu'un.

Auditeur dans votre activité:

private View.OnClickListener toolbarMenuListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //will be called only if toggle.setDrawerIndicatorEnabled(false); !
            Log.v(tag,"toggle onClick:"+v.getId()+" Android.R.id.home:"+Android.R.id.home);
            onBackPressed();
        }
    };

Code onCreate () quelque chose comme:

...
...
setSupportActionBar(toolbar);
toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();

//set listener so you know when back on arrow is pressed
toggle.setToolbarNavigationClickListener(toolbarMenuListener);
...
...

Partie qui vous intéresse avec des commentaires (la classe retournée est une partie de la classe de la mine, peut être annulée):

/**
     * Method to set up action bar drawer.
     * @param enableBackDrawerIcon set true if want to show drawer back arrow,
     *                             false to show hamburger menu.
     * @param title shown next to drawer icon
     */
    public BaseMenusActivity drawerSetupToggle(boolean enableBackDrawerIcon, String title) {
        //NOTE: order of methods call is important!
        // If you change order order of setDrawerIndicatorEnabled and setDisplayHomeAsUpEnabled
        // method calls it won't work, weird bugs will happen (like no icon at all)
        if(enableBackDrawerIcon){
            Log.v(tag,"show drawer back icon");
            //hides hamburger menu and enables View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(false);
            //show back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        } else {
            Log.v(tag,"show hamburger menu");
            //hide back arrow
            if(getSupportActionBar()!=null)
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            //shows hamburger menu and prevents View.OnClickListener to be called
            toggle.setDrawerIndicatorEnabled(true);
        }

        setTitle(title);
        return this;
    }

NOTE: l'ordre des méthodes appelées est important! Ce serait mieux si vous pouviez simplement l'écrire en 2 lignes comme ceci mais NE FONCTIONNERA PAS (du moins pour moi):

toggle.setDrawerIndicatorEnabled(!enableBackDrawerIcon);
     getSupportActionBar().setDisplayHomeAsUpEnabled(enableBackDrawerIcon);

Si vous êtes intéressé par la raison pour laquelle l'ordre des méthodes appelle des erreurs, examinez la mise en œuvre de ces méthodes.

0
user1540907
//This if block makes the menu back button to respond to clicks
    //The onOptionsItemSelected fun for whatever reason was not capturing back menu clicks
    if (toolbar != null) {
       /* toggle = ActionBarDrawerToggle(
                this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
        toggle.syncState()
        drawer_layout.setDrawerListener(toggle)*/
        supportFragmentManager.addOnBackStackChangedListener(object : FragmentManager.OnBackStackChangedListener {
            override fun onBackStackChanged() {
                if (supportFragmentManager.backStackEntryCount > 0) {
                    supportActionBar?.setDisplayHomeAsUpEnabled(true) // show back button
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            onBackPressed()
                        }
                    })
                } else {
                    //show hamburger
                    supportActionBar?.setDisplayHomeAsUpEnabled(false)
                    toggle.syncState()
                    toolbar.setNavigationOnClickListener(object : View.OnClickListener {
                        override fun onClick(v: View) {
                            drawer_layout.openDrawer(GravityCompat.START)
                        }
                    })
                }
            }
        })

    }

Vous devez commenter "toggle = ActionBarDrawerToggle ( This, tiroir_layout, barre d'outils, R.string.navigation_drawer_open, R.string.navigation_drawer_close) 4-7 lignes) si vous utilisez la disposition de navigation générée automatiquement dans Android Studio, sinon le comportement du bouton de menu arrière sera erratique. C'est ce que j'ai fait et cela a parfaitement fonctionné pour moi. J'espère que cela aide quelqu'un

0
defemz