Commutazione tra l'immagine del Navigatore di navigazione Android e il cursore verso l'alto quando si utilizzano frammenti


177

Quando si utilizza il cassetto di navigazione, gli sviluppatori Android raccomandano che nell'ActionBar "solo le schermate rappresentate nel cassetto di navigazione dovrebbero effettivamente avere l'immagine del cassetto di navigazione" e che "tutte le altre schermate abbiano il carato tradizionale".

Vedi qui per i dettagli: http://youtu.be/F5COhlbpIbY

Sto usando un'attività per controllare più livelli di frammenti e riesco a far visualizzare e funzionare l'immagine del Navigatore a tutti i livelli.

Quando si creano frammenti di livello inferiore, posso chiamare ActionBarDrawerToggle setDrawerIndicatorEnabled(false)per nascondere l'immagine del cassetto di navigazione e visualizzare il cursore Su

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

Il problema che sto riscontrando è quando torno indietro ai frammenti di livello superiore che mostra ancora il carato invece dell'immagine originale del Navigatore. Qualche suggerimento su come "aggiornare" la ActionBar sui frammenti di livello superiore per visualizzare nuovamente l'immagine del Navigatore?


Soluzione

Il suggerimento di Tom ha funzionato per me. Ecco cosa ho fatto:

Attività principale

Questa attività controlla tutti i frammenti nell'app.

Quando preparo nuovi frammenti per sostituirne altri, imposto DrawerToggle in setDrawerIndicatorEnabled(false)questo modo:

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

Successivamente, in una sostituzione di onBackPressed, ho ripristinato quanto sopra impostando DrawerToggle in setDrawerIndicatorEnabled(true)questo modo:

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

In LowerLevelFragments

Nei frammenti ho modificato onCreatee onOptionsItemSelectedcosì:

In onCreateaggiunta setHasOptionsMenu(true)per abilitare la configurazione del menu delle opzioni. Impostare anche setDisplayHomeAsUpEnabled(true)per abilitare < nella barra delle azioni:

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

Quindi onOptionsItemSelectedogni volta che si preme < chiama onBackPressed()dall'attività per salire di un livello nella gerarchia e visualizzare l'immagine del cassetto di navigazione:

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

2
Inoltre nel tuo metodo onBackPressed () puoi controllare quante voci sono nello stack posteriore con il metodo getFragmentManager (). GetBackStackEntryCount () e abilitare l'indicatore del cassetto solo se il risultato è 0. In quel caso non è necessario abilitare homeAsUpIndicator in ciascun LowerLevelFragments.
pvshnik,

2
Questo è molto utile! Dovresti spostare la parte "soluzione" del tuo post e renderla una vera "risposta". Otterrai più punti per le votazioni ed è una risposta dopo tutto
Oleksiy,

Perché si sta sostituendo il frammento qui: .replace(R.id.frag_layout. Se questo è un altro livello di gerarchia, mi aspetterei che tu .addsia nel backstack.
JJD,

5
Fratello, come riferisci l' theDrawerToggle.setDrawerIndicatorEnabled(false);interno del frammento? Penso che sia dichiarato nel file di classe Main Activity. Non riesco a trovare un modo per fare riferimento a questo. Qualche suggerimento?
Skynet,

1
Quando ho usato una barra degli strumenti, nel frattempo ho dovuto cambiare le opzioni di visualizzazione per non usare la casa come su. Altrimenti il setDisplayOptions()metodo all'interno del ToolbarWidgetWrapper(del pacchetto interno android.support.v7.internal.widget) non ricreare l'icona quando si inserisce lo stesso frammento una seconda volta. Lasciandolo qui per quando anche altri si imbattono in questo problema.
Wolfram Rittmeyer,

Risposte:


29

Hai scritto che, per implementare frammenti di livello inferiore, stai sostituendo il frammento esistente, invece di implementare il frammento di livello inferiore in una nuova attività.

Penserei che dovresti quindi implementare manualmente la funzionalità di back: quando l'utente ha premuto di nuovo hai un codice che apre lo stack (ad esempio in Activity::onBackPressedoverride). Quindi, ovunque tu lo faccia, puoi invertire il setDrawerIndicatorEnabled.


Grazie Tom, ha funzionato! Ho aggiornato il post originale con la soluzione che ho usato.
EvilAsh

6
Come fare riferimento a theDrawerToggle in un frammento di livello inferiore? È stato definito nell'attività principale, non riesco a capirlo!
Skynet,

1
È possibile ottenere l'attività dal frammento. Quindi aggiungi semplicemente un getter nella tua attività principale per accedere al toggle del cassetto.
Raphael Royer-Rivard,

83

È facile come 1-2-3.

Se vuoi raggiungere:

1) Indicatore del cassetto : quando non vi sono frammenti nella pila posteriore o il cassetto è aperto

2) Freccia - quando alcuni frammenti sono nella pila posteriore

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) Entrambi gli indicatori agiscono in base alla loro forma

@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 Vedi Creazione di un cassetto di navigazione per sviluppatori Android su altri suggerimenti sul comportamento dell'indicatore a 3 righe.


3
Ho finito per qualcosa di simile al tuo. Penso che questa soluzione (usando BackStackChangedListener per alternare ciò che viene mostrato) sia la più elegante. Tuttavia ho due modifiche: 1) Non chiamo / cambio l'indicatore del cassetto nelle chiamate onDrawerClosed / onDrawerOpened - non è necessario, e 2) Lascio che i frammenti stessi gestiscano la navigazione verso l'alto facendo un AbstractFragment che ereditano tutti quali implementa onOptionsItemSelected (..) e che chiama sempre setHasOptionsMenu (true);
Espen Riskedal,

2
Dovresti anche bloccare il gesto di scorrimento del cassetto di navigazione in modalità freccia: mDrawerLayout.setDrawerLockMode (DrawerLayout.LOCK_MODE_LOCKED_CLOSED); e
riattivalo in

5
Ho finito per fare tutto questo nel mio frammento di NavigationDrawer. Funziona come un fascino tra l'altro, grazie.
frostymarvelous,

1
setActionBarArrowDependingOnFragmentsBackStack()... che nome lungo: P
S. Thiongane il

2
Questo non mostra il pulsante su per me quando passo dal frammento A a B e aggiungo A allo zaino. :(
aandis,

14

Ho usato la prossima cosa:

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

1
GRAZIE TANTO QUESTO è l'unico che ha funzionato davvero. Una volta aggiunto questo, drawerToggle.setToolbarNavigationClickListener(questo ascoltatore viene chiamato quando si fa clic sulla freccia
EpicPandaForce,

Grazie @Yuriy, questo mi ha aiutato a risolvere il mio problema
Muhammad Shoaib Murtaza il

12

Se il pulsante su della barra delle azioni non funziona, non dimenticare di aggiungere il listener:

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

Ho dei problemi nell'implementare una navigazione del cassetto con un pulsante home, tutto ha funzionato tranne il pulsante di azione.


10

Prova a gestire la selezione dell'elemento Home in MainActivity in base allo stato di DrawerToggle. In questo modo non è necessario aggiungere lo stesso codice ad ogni frammento.

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

+1 soluzione pulita. Affinché la tua risposta sia pienamente utilizzabile, devi aggiungere un segno di spunta sul backstack. Quando è vuoto, imposta automaticamente l'indicatore del cassetto su true.
HpTerm

@HpTerm Gestisco lo zaino onBackPressed()perché volevo lo stesso comportamento per entrambi.
dzeikei,

6

AZIONE SUPPLEMENTARE

La soluzione fornita da @dzeikei è chiara, ma può essere estesa, quando si usano frammenti, per gestire automaticamente il settaggio dell'indicatore del cassetto quando lo zaino è vuoto.

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

MODIFICARE

Per la domanda di @JJD.

I frammenti sono mantenuti / gestiti in un'attività. Il codice sopra è scritto una volta in quell'attività, ma gestisce solo il cursore verso l'alto per il onOptionsItemSelected.

In una delle mie app avevo anche bisogno di gestire il comportamento del cursore verso l'alto quando veniva premuto il pulsante Indietro. Questo può essere gestito sostituendo 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);
        }
    }
};

Notare la duplicazione del codice tra onOptionsItemSelectede onBackPressedche può essere evitata creando un metodo e chiamando quel metodo in entrambe le posizioni.

Nota anche che aggiungo altre due volte executePendingTransactionsche nel mio caso erano necessarie, oppure a volte avevo comportamenti strani nel caret up.


E 'questo il codice che ho bisogno di aggiungere completo per implementare il Up comportamento accento circonflesso o stai riferendosi ad uno degli altri posti? Si prega di chiarire questo.
JJD

Grazie per la modifica. In realtà mantengo mDrawerToggleall'interno di una NavigationDrawerFragmentclasse. Per farlo funzionare devo anche cambiare lo stato del tasto home / Indicatore - vedi: NavigationDrawerFragment#toggleDrawerIndicator. Inoltre, non sono sicuro che sia necessario il check-in iniziale onOptionsItemSelected: l'ho decommentato. - Esempio semplificato
JJD,

Implementazione rivista : hai ragione sul check-in iniziale onOptionsItemSelected. Ciò garantisce che il cassetto di navigazione si apra ancora nella gerarchia di livello superiore. Tuttavia, ho spostato il codice in NavigationDrawerFragment#onOptionsItemSelected. Questo mi aiuta a non esporre mDrawerToggleal MainActivity.
JJD,

@JJD Mi sono ricordato di aver lottato un po 'avendo tutto funzionato per il caret up per ogni livello della gerarchia. Finché funziona anche per te va bene. Ovviamente, come dici tu, puoi spostare il codice altrove per non esporlo.
HpTerm,

2

Ho creato un'interfaccia per l'attività di hosting per aggiornare lo stato di visualizzazione del menu hamburger. Per i frammenti di livello superiore ho impostato l'interruttore su truee per i frammenti per i quali voglio visualizzare la freccia su <ho impostato l'interruttore su 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);
    }
}

Quindi nella mia attività ...

public class MainActivity extends Activity implements SomeFragment.OnFragmentInteractionListener {

    private ActionBarDrawerToggle mDrawerToggle;

    public void showDrawerToggle(boolean showDrawerIndicator) {
        mDrawerToggle.setDrawerIndicatorEnabled(showDrawerIndicator);
    }

}

2

Questa risposta funzionava ma c'era un piccolo problema. Non è getSupportActionBar().setDisplayHomeAsUpEnabled(false)stato chiamato in modo esplicito e stava facendo sì che l'icona del cassetto fosse nascosta anche quando non c'erano oggetti nello zaino, quindi cambiando il setActionBarArrowDependingOnFragmentsBackStack()metodo ha funzionato per me.

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

    }

nel mio caso la soluzione rigth stava usando solo actionBarDrawerToggle.setDrawerIndicatorEnabled (getSupportFragmentManager (). getBackStackEntryCount () <0);
Gorets il

1

La logica è chiara. Mostra il pulsante Indietro se lo stack posteriore del frammento è chiaro. Mostra l'animazione materiale hamburger-back se la pila di frammenti non è chiara.

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

Se dai un'occhiata all'app GMAIL e vieni qui per cercare l'icona del carret / accessorio.

Ti chiederei di farlo, nessuna delle risposte sopra era chiara. sono stato in grado di modificare la risposta accettata.

  • NavigationDrawer -> Listview contiene subframment.


  • le subframmenti verranno elencate in questo modo

  • firstFragment == position 0 ---> questo avrà dei frammenti secondari -> frammento

  • secondFragment
  • thirdFragment e così via ....

In firstFragment hai altri frammenti.

Chiamalo su DrawerActivity

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

e in frammento

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

Sul metodo di attività del cassetto OnBackPressed impostare l'interruttore del cassetto su true per abilitare nuovamente l'icona dell'elenco di navigazione.

Grazie, Pusp



0

IMO, usando onNavigateUp () (come mostrato qui ) nella soluzione di riwnodennyk o Tom è più pulito e sembra funzionare meglio. Sostituisci semplicemente il codice onOptionsItemSelected con questo:

@Override
public boolean onSupportNavigateUp() {
    if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
        // handle up navigation
        return true;
    } else {
        return super.onSupportNavigateUp();
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.