actionbar w górę nawigacji z fragmentami

88

Mam kartami Actionbar / viewpager układ z trzema zakładkami powiedzieć A , B i C . Na karcie C zakładki (fragment), dodaję inny fragment powiedzmy fragment D . z

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

Modyfikuję pasek akcji w DFragment's onResume, aby dodać przycisk:

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

Teraz w DFragment, kiedy naciskam przycisk sprzętowy (telefon) Wstecz, wracam do oryginalnego układu z zakładkami (ABC) z zaznaczonym CFragment. Jak mogę osiągnąć tę funkcjonalność za pomocą przycisku Actionbar Up?

SohailAziz
źródło

Odpowiedzi:

185

Zaimplementuj OnBackStackChangedListeneri dodaj ten kod do swojego działania dotyczącego fragmentów.

@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
źródło
20
O onSupportNavigateUp(): „Metoda nie zastępuje metody ze swojej nadklasy”.
Fred
10
Jeśli już masz onOptionsItemSelected, możesz również sprawdzić itemId android.R.id.homezamiast dodawać onSupportNavigateUp.
domsom
1
Jeśli wersja API> = 14, użyj onNavigateUp zamiast onSupportNavigateUp @Override public boolean onNavigateUp () {// Ta metoda jest wywoływana po naciśnięciu przycisku w górę. Tylko stos pop-back. getFragmentManager (). popBackStack (); powrót prawda; }
Tejasvi Hegde
1
Czy obok ikony aplikacji powinien być wyświetlany znak ostrzegawczy w górę ActionBar? Nie widzę żadnego, kiedy implementuję ten kod. Mogę tylko kliknąć ikonę, ale to nic nie robi. Android 4.0+.
Azurespot
3
Jeśli onBackStackChanged () nie zastępuje, upewnij się, że Twoja aktywność implementuje interfejs FragmentManager.OnBackStackChangedListener.
CBA110
41

Mam to. po prostu zastąp onOptionsItemSelected w aktywności hostingu i wyskakuje backstack, np

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

Zadzwoń getActionBar().setDisplayHomeAsUpEnabled(boolean);i getActionBar().setHomeButtonEnabled(boolean);w onBackStackChanged()jak wyjaśniono w odpowiedzi poniżej.

SohailAziz
źródło
3
musisz także wywołać metodę getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); aby usunąć przycisk „w górę” po podniesieniu stosu
JoP,
To nie jest właściwy sposób. Utrzymuje włączony przycisk w górę.
Roger Garzon Nieto
1
Ten kod należy umieścić w switchinstrukcji wraz z obudową android.R.id.home.
Fred
18

Jeśli masz jedno działanie dla rodziców i chcesz, aby ten przycisk w górę działał jako przycisk Wstecz, możesz użyć tego kodu:

dodaj to do onCreate w swojej głównej klasie aktywności

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

    });

a następnie dodaj onOptionsItemSelected w następujący sposób:

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

Generalnie używam tego przez cały czas i wydaje się całkiem legalny

Daniel Jonker
źródło
1
Próbowałem użyć tego kodu dokładnie, w swoim Activity, w nadziei, że wróci do fragmentu, od którego się zaczął. Przycisk Wstecz pojawia się obok ikony mojej aplikacji, kiedy przechodzę do mojej Activity, ale klikam ikonę i nic się nie dzieje (powinien wrócić do fragmentu). Każdy pomysł, dlaczego? Dzięki.
Azurespot
1
@Daniel, twój kod jest legalny ... to działa. Możesz po prostu chcieć to otoczyć za pomocą opcji spróbuj złapać po prostu na wszelki wypadek ... wiesz, aby zapobiec nieprzewidzianym wyjątkom i awariom aplikacji
Zuko
1
@NoniA. To tylko wróci do poprzedniego fragmentu (np. Fragment B -> fragment A), jeśli nadmuchujesz 1 fragment w nowej aktywności, nie wróci to do poprzedniej czynności.
Daniel Jonker
10

możesz wrócić za pomocą przycisku w górę, takiego jak przycisk Wstecz;

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

Wiem, że to stare pytanie, ale może ktoś (taki jak ja) też go potrzebuje.

Jeśli Twoja aktywność rozszerza AppCompatActivity , możesz użyć prostszego (dwuetapowego) rozwiązania:

1 - Za każdym razem, gdy dodajesz fragment spoza domu, po prostu pokaż przycisk w górę, zaraz po zatwierdzeniu transakcji fragmentu. Lubię to:

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

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Następnie, gdy wciśnięty jest przycisk UP, ukrywasz go.

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

Otóż ​​to.

martom
źródło
7

Użyłem kombinacji Rogera Garzona Nieto i sohailaziza . Moja aplikacja ma jedną MainActivity i fragmenty A, B, C, które są do niej ładowane. Mój fragment „domowy” (A) implementuje OnBackStackChangedListener i sprawdza rozmiar backStack; jeśli jest mniej niż jeden, ukrywa przycisk W GÓRĘ. Fragmenty B i C zawsze ładują przycisk Wstecz (w moim projekcie B jest uruchamiany z A, a C z B). Sama MainActivity po prostu wyskakuje backstack po naciśnięciu przycisku UP i ma metody pokazania / ukrycia przycisku, który wywołują fragmenty:

Główna aktywność:

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 (implementuje 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
    ...
}
verboze
źródło
5

To zadziałało dla mnie. Zastąp na przykład onSupportNavigateUp i onBackPressed (kod w 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
}

Teraz we fragmencie, jeśli wyświetlisz strzałkę w górę

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Kliknięcie go powoduje powrót do poprzedniej czynności.

bebe
źródło
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
źródło
2

To bardzo dobre i niezawodne rozwiązanie: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

Facet stworzył abstrakcyjny fragment, który obsługuje zachowanie backPress i przełącza się między aktywnymi fragmentami za pomocą wzorca strategii.

Dla niektórych z was może być mała wada w klasie abstrakcyjnej ...

Krótko mówiąc, rozwiązanie z linku wygląda tak:

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

I wykorzystanie w działalności:

// 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
źródło
Chociaż ten link może odpowiedzieć na pytanie, lepiej jest to zasadnicze części odpowiedź tutaj i podaj link do odniesienia. Odpowiedzi zawierające tylko łącze mogą stać się nieprawidłowe, jeśli połączona strona ulegnie zmianie.
bummi
BTW: jeśli zidentyfikujesz duplikaty, oznacz je jako takie. dzięki.
bummi
czy ważne jest, aby setSelectedFragment wewnątrz onStart?
VLeonovs