barra delle azioni su navigazione con frammenti


88

Ho un schede ActionBar / viewpager disposizione con tre schede dicono A , B , e C . Nella scheda C scheda (frammento), sto aggiungendo un altro frammento diciamo frammento D . con

 DFragment f= new DFragment();
 ft.add(android.R.id.content, f, "");
 ft.remove(CFragment.this);
 ft.addToBackStack(null);
 ft.commit();

Modifico la barra delle azioni nel pulsante onResume di DFragment per aggiungere:

ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);

Ora in DFragment, quando premo il pulsante Indietro hardware (telefono), torno al layout a schede (ABC) originale con CFragment selezionato. Come posso ottenere questa funzionalità con il pulsante su della barra delle azioni?


Risposte:


186

Implementa OnBackStackChangedListenere aggiungi questo codice alla tua attività sui frammenti.

@Override
public void onCreate(Bundle savedInstanceState) {
    //Listen for changes in the back stack
    getSupportFragmentManager().addOnBackStackChangedListener(this);
    //Handle when activity is recreated like on orientation Change
    shouldDisplayHomeUp();
}

@Override
public void onBackStackChanged() {
    shouldDisplayHomeUp();
}

public void shouldDisplayHomeUp(){
   //Enable Up button only  if there are entries in the back stack
   boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
   getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}

@Override
public boolean onSupportNavigateUp() {
    //This method is called when the up button is pressed. Just the pop back stack.
    getSupportFragmentManager().popBackStack();
    return true;
}

21
A proposito onSupportNavigateUp(), "Il metodo non sovrascrive il metodo dalla sua superclasse".
Fred

10
Se hai già un onOptionsItemSelected, è anche possibile controllare l'itemId android.R.id.homeinvece di aggiungere onSupportNavigateUp.
domsom

1
Se la versione API> = 14, usa onNavigateUp invece di onSupportNavigateUp @Override public boolean onNavigateUp () {// Questo metodo viene chiamato quando viene premuto il pulsante su. Solo il pop back stack. getFragmentManager (). popBackStack (); restituire vero; }
Tejasvi Hegde

1
Dovrebbe esserci un cursore verso l'alto che viene visualizzato accanto all'icona della tua app in ActionBar? Non ne vedo uno quando implemento questo codice. Posso solo fare clic sull'icona, ma non fa nulla. Android 4.0+.
Azurespot

3
Se onBackStackChanged () non esegue l'override, assicurati che la tua attività implementi l'interfaccia FragmentManager.OnBackStackChangedListener.
CBA110

42

Capito. è sufficiente sovrascrivere onOptionsItemSelected nell'attività di hosting e visualizzare il backstack, ad es

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home: {
            FragmentManager fm = getSupportFragmentManager();
            if (fm.getBackStackEntryCount() > 0) {
                fm.popBackStack();
                return true;
            }
            break;
        }
    }
    return super.onOptionsItemSelected(item);
}

Chiamata getActionBar().setDisplayHomeAsUpEnabled(boolean); e getActionBar().setHomeButtonEnabled(boolean);in onBackStackChanged()come spiegato in una risposta di seguito.


3
devi anche chiamare getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); per rimuovere il pulsante su una volta
aperto lo

Questo non è il modo corretto per farlo. Mantiene abilitato il pulsante su.
Roger Garzon Nieto

1
Dovresti inserire quel codice all'interno switchdell'istruzione con il caso android.R.id.home.
Fred

18

Se hai un'attività per un genitore e desideri che questo pulsante su funzioni come pulsante Indietro, puoi utilizzare questo codice:

aggiungilo a onCreate nella tua classe di attività principale

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
            if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
                getSupportActionBar().setHomeButtonEnabled(true);
                getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            } else {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
                getSupportActionBar().setHomeButtonEnabled(false);
            }
        }

    });

e quindi aggiungi onOptionsItemSelected in questo modo:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
     ....
 }

Generalmente lo uso sempre e sembra abbastanza legittimo


1
Ho provato a utilizzare esattamente questo codice, in un Activitymio, nella speranza che tornasse al frammento da cui era partito. Il pulsante Indietro appare vicino all'icona della mia app quando vado al mio Activity, ma faccio clic sull'icona e non succede nulla (dovrebbe tornare al frammento). Qualche idea sul perché? Grazie.
Azurespot

1
@ Daniel il tuo codice è legittimo .. funziona. Potresti semplicemente circondarlo con l'opzione try catch solo in caso .. sai per evitare eccezioni impreviste e app che si bloccano
Zuko

1
@NoniA. Questo tornerà solo a un frammento precedente (ad esempio, frammento B -> frammento A), se stai gonfiando 1 frammento in una nuova attività, questo non tornerà all'attività precedente.
Daniel Jonker

11

puoi tornare indietro con il pulsante su come il pulsante indietro;

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case android.R.id.home:
            super.onBackPressed();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

8

So che questa domanda è vecchia, ma potrebbe anche essere qualcuno (come me) che ne ha bisogno.

Se la tua attività estende AppCompatActivity , puoi utilizzare una soluzione più semplice (in due passaggi):

1 - Ogni volta che aggiungi un frammento non domestico mostra semplicemente il pulsante su, subito dopo aver eseguito il commit della transazione del frammento. Come questo:

    // ... add a fragment
    // Commit the transaction
    transaction.commit();

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Quindi, quando viene premuto il pulsante SU, lo nascondi.

@Override
public boolean onSupportNavigateUp() {
    getSupportActionBar().setDisplayHomeAsUpEnabled(false);        
    return true;
}

Questo è tutto.


7

Ho usato una combinazione di Roger Garzon Nieto e sohailaziz risposte . La mia app ha un'unica MainActivity e i frammenti A, B, C che vengono caricati al suo interno. Il mio frammento "home" (A) implementa OnBackStackChangedListener e controlla la dimensione del backStack; se è inferiore a uno, nasconde il pulsante SU. I frammenti B e C caricano sempre il pulsante Indietro (nel mio progetto, B viene lanciato da A e C viene lanciato da B). La MainActivity stessa apre semplicemente il backstack al tocco del pulsante SU e ha metodi per mostrare / nascondere il pulsante, che i frammenti chiamano:

Attività principale:

public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        // Respond to the action bar's Up/Home button
        case android.R.id.home:
            getSupportFragmentManager().popBackStack();
            return true;
    }
    return super.onOptionsItemSelected(item);
}

public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }

fragmentA (implementa FragmentManager.OnBackStackChangedListener):

public void onCreate(Bundle savedinstanceSate) {
    // listen to backstack changes
    getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);

    // other fragment init stuff
    ...
}

public void onBackStackChanged() {
    // enable Up button only  if there are entries on the backstack
    if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
        ((MainActivity)getActivity()).hideUpButton();
    }
}

fragmentB, fragmentC:

public void onCreate(Bundle savedinstanceSate) {
    // show the UP button
    ((MainActivity)getActivity()).showUpButton();

    // other fragment init stuff
    ...
}

5

Questo ha funzionato per me. Override onSupportNavigateUp e onBackPressed, ad esempio (codice in Kotlin);

override fun onBackPressed() {
    val count = supportFragmentManager.backStackEntryCount
    if (count == 0) {
        super.onBackPressed()
    } else {
        supportFragmentManager.popBackStack()
    }
}

override fun onSupportNavigateUp(): Boolean {
    super.onSupportNavigateUp()
    onBackPressed()
    return true
}

Ora nel frammento, se visualizzi la freccia su

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Cliccandoci si ritorna all'attività precedente.


5

Kotlin:

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
        setupHomeAsUp()
    }

    private fun setupHomeAsUp() {
        val shouldShow = 0 < supportFragmentManager.backStackEntryCount
        supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
    }

    override fun onSupportNavigateUp(): Boolean = 
        supportFragmentManager.popBackStack().run { true }

    ...
}

2

Questa è una soluzione molto buona e affidabile: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

Il ragazzo ha creato un frammento astratto che gestisce il comportamento di backPress e sta passando da un frammento attivo all'altro usando il modello di strategia.

Per alcuni di voi forse c'è un piccolo inconveniente nella classe astratta ...

In breve, la soluzione dal collegamento è questa:

// Abstract Fragment handling the back presses

public abstract class BackHandledFragment extends Fragment {
    protected BackHandlerInterface backHandlerInterface;
    public abstract String getTagText();
    public abstract boolean onBackPressed();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if(!(getActivity()  instanceof BackHandlerInterface)) {
            throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
        } else {
            backHandlerInterface = (BackHandlerInterface) getActivity();
        }
    }

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

        // Mark this fragment as the selected Fragment.
        backHandlerInterface.setSelectedFragment(this);
    }

    public interface BackHandlerInterface {
        public void setSelectedFragment(BackHandledFragment backHandledFragment);
    }
}   

E utilizzo nell'attività:

// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS 
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment

public class TheActivity extends FragmentActivity implements BackHandlerInterface {
    private BackHandledFragment selectedFragment;

    @Override
    public void onBackPressed() {
        if(selectedFragment == null || !selectedFragment.onBackPressed()) {
            // Selected fragment did not consume the back press event.
            super.onBackPressed();
        }
    }

    @Override
    public void setSelectedFragment(BackHandledFragment selectedFragment) {
        this.selectedFragment = selectedFragment;
    }
}

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
bummi

BTW: se si identificano duplicati, contrassegnarli come tali. grazie.
bummi

è importante impostareSelectedFragment all'interno di onStart?
VLeonovs
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.