barra de acción hasta la navegación con fragmentos

88

Tengo un pestañas actionbar / ViewPager diseño con tres pestañas dicen A , B , y C . En la ficha C pestaña (fragmento), añado otro fragmento digamos fragmento 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 de acción en onResume de DFragment para agregar el botón:

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

Ahora, en DFragment, cuando presiono el botón Atrás del hardware (teléfono), regreso al diseño original con pestañas (ABC) con CFragment seleccionado. ¿Cómo puedo lograr esta funcionalidad con el botón arriba de la barra de acciones?

SohailAziz
fuente
Posible duplicado de ¿Cómo implementar onBackPressed () en Fragmentos?
Code-Apprentice

Respuestas:

185

Implemente OnBackStackChangedListenery agregue este código a su actividad de fragmentos.

@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 Garzón Nieto
fuente
20
Acerca de onSupportNavigateUp(), "El método no anula el método de su superclase".
Fred
10
Si ya tiene un onOptionsItemSelected, también es posible verificar el itemId en android.R.id.homelugar de agregar onSupportNavigateUp.
domsom
1
Si la versión de API> = 14, use onNavigateUp en lugar de onSupportNavigateUp @Override public boolean onNavigateUp () {// Este método se llama cuando se presiona el botón arriba. Solo la pila trasera emergente. getFragmentManager (). popBackStack (); devuelve verdadero; }
Tejasvi Hegde
1
¿Se supone que debe haber un signo de intercalación hacia arriba que se muestre junto al ícono de su aplicación en el ActionBar? No veo uno cuando implemento este código. Solo puedo hacer clic en el icono, pero no hace nada. Android 4.0 o superior.
Azurespot
3
Si onBackStackChanged () no se anula, asegúrese de que su actividad implemente la interfaz FragmentManager.OnBackStackChangedListener.
CBA110
41

Lo tengo. simplemente anule onOptionsItemSelected en la actividad de alojamiento y muestre el backstack, por ejemplo

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

Llame getActionBar().setDisplayHomeAsUpEnabled(boolean);y getActionBar().setHomeButtonEnabled(boolean);entre onBackStackChanged()como se explica en una respuesta a continuación.

SohailAziz
fuente
3
también tienes que llamar a getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); para quitar el botón de arriba una vez que
abre la
Ésta no es la forma correcta de hacer esto. Mantiene habilitado el botón arriba.
Roger Garzon Nieto
1
Debería poner ese código dentro de la switchdeclaración con el caso android.R.id.home.
Fred
18

Si tiene una actividad principal y desea que este botón de arriba funcione como botón de retroceso, puede usar este código:

agregue esto a onCreate en su clase de actividad principal

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

    });

y luego agregue onOptionsItemSelected así:

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

Generalmente uso esto todo el tiempo y parece bastante legítimo.

Daniel Jonker
fuente
1
Intenté usar este código exactamente, en uno Activityde los míos, con la esperanza de que volviera al fragmento desde el que comenzó. El botón de retroceso aparece cerca del ícono de mi aplicación cuando voy a mi Activity, pero hago clic en el ícono y no pasa nada (debería volver al fragmento). ¿Alguna idea de por qué? Gracias.
Azurespot
1
@Daniel tu código es legítimo ... funciona. Es posible que desee rodearlo con la opción de captura de prueba por si acaso ... ya sabe para evitar excepciones imprevistas y fallas de aplicaciones
Zuko
1
@NoniA. Esto solo volverá a un fragmento anterior (por ejemplo, fragmento B -> fragmento A), si está inflando 1 fragmento en una nueva actividad, esto no volverá a la actividad anterior.
Daniel Jonker
10

puedes volver con el botón arriba como el botón atrás;

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

Sé que esta pregunta es antigua, pero es posible que alguien (como yo) también la necesite.

Si su actividad extiende AppCompatActivity , puede usar una solución más simple (dos pasos):

1 - Siempre que agregue un fragmento que no sea de inicio, simplemente muestre el botón arriba, justo después de realizar la transacción del fragmento. Me gusta esto:

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

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Luego, cuando se presiona el botón ARRIBA, lo oculta.

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

Eso es.

martom
fuente
7

Usé una combinación de las respuestas de Roger Garzon Nieto y sohailaziz . Mi aplicación tiene una sola MainActivity y fragmentos A, B, C que se cargan en ella. Mi fragmento de "casa" (A) implementa OnBackStackChangedListener y comprueba el tamaño de la backStack; si es menos de uno, oculta el botón ARRIBA. Los fragmentos B y C siempre cargan el botón de retroceso (en mi diseño, B se inicia desde A y C se inicia desde B). La MainActivity en sí misma simplemente abre el backstack al tocar el botón ARRIBA, y tiene métodos para mostrar / ocultar el botón, que los fragmentos llaman:

Actividad principal:

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

fragmentoB, fragmentoC:

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

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

Esto funcionó para mí. Anular onSupportNavigateUp y onBackPressed, por ejemplo (código en 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
}

Ahora en el fragmento, si muestra la flecha hacia arriba

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Al hacer clic en él, vuelve a la actividad anterior.

bebe
fuente
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
fuente
2

Esta es una solución muy buena y confiable: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

El tipo ha creado un fragmento abstracto que maneja el comportamiento de backPress y está cambiando entre los fragmentos activos usando el patrón de estrategia.

Para algunos de ustedes, tal vez haya un pequeño inconveniente en la clase abstracta ...

En breve, la solución del enlace es la siguiente:

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

Y uso en la actividad:

// 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
fuente
Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada.
bummi
Por cierto: si identifica duplicados, márquelos como tales. gracias.
bummi
¿Es importante establecer el fragmentoSeleccionado dentro de onStart?
VLeonovs