Aktionsleiste Navigation mit Fragmenten

88

Ich habe ein gestapeltes ActionBar / viewpager Layout mit drei Zungen sagen , A , B , und C . In Tab C Registerkarte (Fragment), ich bin das Hinzufügen eines weiteren Fragment sagen Fragment D . mit

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

Ich ändere die Aktionsleiste in DFragments onResume, um die Schaltfläche hinzuzufügen:

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

Wenn ich jetzt in DFragment die Zurück-Taste für Hardware (Telefon) drücke, kehre ich mit ausgewähltem CFragment zum ursprünglichen Layout mit Registerkarten (ABC) zurück. Wie kann ich diese Funktionalität mit der Aktionsleiste erreichen?

SohailAziz
quelle
Mögliches Duplikat von Wie implementiere ich onBackPressed () in Fragmenten?
Code-Apprentice

Antworten:

185

Implementieren OnBackStackChangedListenerSie diesen Code und fügen Sie ihn Ihrer Fragmentaktivität hinzu.

@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;
}
Roger Garzon Nieto
quelle
18
Info onSupportNavigateUp()"Methode überschreibt Methode nicht aus ihrer Oberklasse".
Fred
9
Wenn Sie bereits eine haben onOptionsItemSelected, können Sie auch nach der itemId suchen, android.R.id.homeanstatt sie hinzuzufügen onSupportNavigateUp.
Domsom
1
Wenn die API-Version> = 14 ist, verwenden Sie onNavigateUp anstelle von onSupportNavigateUp @Override public boolean onNavigateUp () {// Diese Methode wird aufgerufen, wenn die Aufwärts-Schaltfläche gedrückt wird. Nur der Pop-Back-Stack. getFragmentManager (). popBackStack (); return true; }
Tejasvi Hegde
1
Soll es ein Up Caret geben, das neben Ihrem App-Symbol im angezeigt wird ActionBar? Ich sehe keinen, wenn ich diesen Code implementiere. Ich kann nur auf das Symbol klicken, aber es macht nichts. Android 4.0+.
Azurespot
3
Wenn onBackStackChanged () nicht überschreibt, stellen Sie sicher, dass Ihre Aktivität die FragmentManager.OnBackStackChangedListener-Schnittstelle implementiert.
CBA110
40

Ich habe es verstanden. Überschreiben Sie einfach onOptionsItemSelected in der Hosting-Aktivität und öffnen Sie den Backstack, z

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

Rufen Sie an getActionBar().setDisplayHomeAsUpEnabled(boolean);und melden Sie sich getActionBar().setHomeButtonEnabled(boolean);an, onBackStackChanged()wie in einer Antwort unten erläutert.

SohailAziz
quelle
3
Sie müssen auch getActivity (). getActionBar () aufrufen. setDisplayHomeAsUpEnabled (false); um den Aufwärtsknopf zu entfernen, sobald Sie den hinteren Stapel
platzen lassen
Dies ist nicht der richtige Weg, dies zu tun. Die Aufwärts-Taste bleibt aktiviert.
Roger Garzon Nieto
1
Sie sollten diesen Code in die switchAnweisung mit dem Fall einfügen android.R.id.home.
Fred
18

Wenn Sie eine übergeordnete Aktivität haben und möchten, dass diese Aufwärtsschaltfläche als Zurückschaltfläche fungiert, können Sie diesen Code verwenden:

Fügen Sie dies zu onCreate in Ihrer Hauptaktivitätsklasse hinzu

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

    });

und fügen Sie dann onOptionsItemSelected wie folgt hinzu:

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

Ich benutze das im Allgemeinen die ganze Zeit und scheint ziemlich legitim zu sein

Daniel Jonker
quelle
1
Ich habe versucht, diesen Code genau in einem Activityvon mir zu verwenden, in der Hoffnung, dass er zu dem Fragment zurückkehren würde, von dem er ausgegangen ist. Die Schaltfläche "Zurück" wird neben meinem App-Symbol angezeigt, wenn ich zu "Mein" gehe. ActivityIch klicke jedoch auf das Symbol und es passiert nichts (es sollte zum Fragment zurückkehren). Irgendeine Idee warum? Vielen Dank.
Azurespot
1
@ Daniel Ihr Code ist echt .. es funktioniert. Vielleicht möchten Sie es mit der Try-Catch-Option umrunden, nur für den Fall, dass Sie unvorhergesehene Ausnahmen und Abstürze von Apps verhindern können
Olu Smith,
1
@NoniA. Dies wird nur zu einem vorherigen Fragment zurückkehren (z. B. Fragment B -> Fragment A). Wenn Sie 1 Fragment in einer neuen Aktivität aufblasen, wird dies nicht zur vorherigen Aktivität zurückkehren.
Daniel Jonker
10

Sie können mit der Aufwärts-Taste wie der Zurück-Taste zurückgehen.

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

Ich weiß, dass diese Frage alt ist, aber vielleicht braucht sie auch jemand (wie ich).

Wenn Ihre Aktivität AppCompatActivity erweitert , können Sie eine einfachere (zweistufige) Lösung verwenden:

1 - Wenn Sie ein Nicht-Home-Fragment hinzufügen, zeigen Sie direkt nach dem Festschreiben der Fragmenttransaktion die Schaltfläche Nach oben an. So was:

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

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Wenn Sie dann die UP-Taste drücken, verstecken Sie sie.

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

Das ist es.

Martom
quelle
7

Ich habe eine Kombination aus den Antworten von Roger Garzon Nieto und Sohailaziz verwendet . Meine App verfügt über eine einzelne MainActivity und Fragmente A, B, C, die in sie geladen werden. Mein "Home" -Fragment (A) implementiert OnBackStackChangedListener und überprüft die Größe des BackStacks. Wenn es weniger als eins ist, wird die UP-Taste ausgeblendet. Die Fragmente B und C laden immer den Zurück-Button (in meinem Design wird B von A und C von B aus gestartet). Die MainActivity selbst öffnet den Backstack beim Tippen auf die UP-Schaltfläche und verfügt über Methoden zum Anzeigen / Ausblenden der Schaltfläche, die die Fragmente aufrufen:

Hauptaktivität:

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

Fragment B, Fragment C:

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

    // other fragment init stuff
    ...
}
verboze
quelle
5

Das hat bei mir funktioniert. Überschreiben Sie beispielsweise onSupportNavigateUp und onBackPressed (Code 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
}

Nun im Fragment, wenn Sie den Aufwärtspfeil anzeigen

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Wenn Sie darauf klicken, kehren Sie zur vorherigen Aktivität zurück.

bebe
quelle
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 }

    ...
}
fada21
quelle
2

Dies ist eine sehr gute und zuverlässige Lösung: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

Der Typ hat ein abstraktes Fragment erstellt, das das backPress-Verhalten behandelt und mithilfe des Strategiemusters zwischen den aktiven Fragmenten wechselt.

Für einige von euch gibt es vielleicht einen kleinen Nachteil in der abstrakten Klasse ...

In Kürze sieht die Lösung aus dem Link folgendermaßen aus:

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

Und Verwendung in der Aktivität:

// 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;
    }
}
Amio.io
quelle
Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Bummi
Übrigens: Wenn Sie Duplikate identifizieren, kennzeichnen Sie diese bitte als solche. Vielen Dank.
Bummi
ist es wichtig ,SelectedFragment in onStart zu setzen?
VLeonovs