barra de ação para cima navegação com fragmentos

88

I têm um tabulada ActionBar / viewpager disposição com três abas dizer Um , B , e C . No separador C separador (fragmento), estou adicionando outro fragmento digamos fragmento D . com

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

Eu modifico a barra de ação no onResume do DFragment para adicionar o botão:

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

Agora em DFragment, quando pressiono o botão Voltar do hardware (telefone), volto ao layout Tabbed (ABC) original com CFragment selecionado. Como posso obter essa funcionalidade com o botão da barra de ação para cima?

SohailAziz
fonte
Possível duplicata de How to implement onBackPressed () in Fragments?
Code-Apprentice

Respostas:

185

Implemente OnBackStackChangedListenere adicione este código à sua atividade de fragmento.

@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
fonte
18
Sobre onSupportNavigateUp(), "Método não sobrescreve método de sua superclasse".
Fred
9
Se você já tiver um onOptionsItemSelected, também é possível verificar o itemId em android.R.id.homevez de adicioná-lo onSupportNavigateUp.
domsom
1
Se a versão da API> = 14, use onNavigateUp em vez de onSupportNavigateUp @Override public boolean onNavigateUp () {// Este método é chamado quando o botão para cima é pressionado. Apenas a pilha de retorno pop. getFragmentManager (). popBackStack (); return true; }
Tejasvi Hegde
1
É suposto haver um acento circunflexo para cima exibido ao lado do ícone do aplicativo no ActionBar? Não vejo nenhum quando implemento este código. Só consigo clicar no ícone, mas não faz nada. Android 4.0+.
Azurespot
3
Se onBackStackChanged () não substituir, certifique-se de que sua atividade implemente a interface FragmentManager.OnBackStackChangedListener.
CBA110
40

Deixa comigo. basta substituir onOptionsItemSelected na atividade de hospedagem e abrir o backstack, por exemplo

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

Chamada getActionBar().setDisplayHomeAsUpEnabled(boolean);e getActionBar().setHomeButtonEnabled(boolean);em onBackStackChanged()como explicou em uma resposta abaixo.

SohailAziz
fonte
3
você também deve chamar getActivity (). getActionBar (). setDisplayHomeAsUpEnabled (false); para remover o botão para cima assim que abrir a pilha de volta
JoP de
Esta não é a maneira correta de fazer isso. Mantém habilitado o botão para cima.
Roger Garzon Nieto
1
Você deve colocar esse código dentro da switchinstrução com o caso android.R.id.home.
Fred de
18

Se você tem uma atividade principal e deseja que esse botão para cima funcione como um botão para voltar, você pode usar este código:

adicione isso ao onCreate em sua classe de atividade 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);
            }
        }

    });

e, em seguida, adicione onOptionsItemSelected assim:

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

Eu geralmente uso isso o tempo todo e parece bastante legítimo

Daniel Jonker
fonte
1
Tentei usar esse código exatamente, em um Activitymeu, na esperança de que ele voltasse ao fragmento de onde partiu. O botão Voltar aparece perto do ícone do meu aplicativo quando vou para o meu Activity, mas clico no ícone e nada acontece (ele deve voltar para o fragmento). Alguma ideia do porquê? Obrigado.
Azurespot
1
@Daniel, seu código é legítimo .. ele funciona. Você só pode querer envolvê-lo com a opção try catch apenas no caso ... você sabe para evitar quaisquer exceções imprevistas e aplicativos travando
Olu Smith
1
@NoniA. Isso só vai voltar para um fragmento anterior (por exemplo, fragmento B -> fragmento A), se você estiver inflando 1 fragmento em uma nova atividade, isso não vai voltar para a atividade anterior.
Daniel Jonker
10

você pode voltar com o botão para cima como o botão de voltar;

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

Eu sei que esta pergunta é antiga, mas pode ser alguém (como eu) também precisa dela.

Se sua Activity estende AppCompatActivity , você pode usar uma solução mais simples (duas etapas):

1 - Sempre que você adicionar um fragmento não doméstico, basta mostrar o botão para cima, logo após confirmar a transação do fragmento. Como isso:

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

    getSupportActionBar().setDisplayHomeAsUpEnabled(true);

2 - Aí quando o botão UP é pressionado, você esconde.

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

É isso aí.

Martom
fonte
7

Usei uma combinação de Roger Garzon Nieto e Sohailaziz respostas . Meu aplicativo tem uma única MainActivity e fragmentos A, B, C que são carregados nele. Meu fragmento "inicial" (A) implementa OnBackStackChangedListener e verifica o tamanho do backStack; se for menor que um, oculta o botão PARA CIMA. Os fragmentos B e C sempre carregam o botão Voltar (em meu projeto, B é iniciado de A e C é iniciado de B). A própria MainActivity apenas exibe o backstack no toque do botão UP, e tem métodos para mostrar / ocultar o botão, que os fragmentos chamam:

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

fragmentB, fragmentC:

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

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

Isso funcionou para mim. Substitua onSupportNavigateUp e onBackPressed, por exemplo (código em 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
}

Agora no fragmento, se você exibir a seta para cima

activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)

Clicar nele leva você de volta à atividade anterior.

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

Esta é uma solução muito boa e confiável: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/

O cara fez um fragmento abstrato que lida com o comportamento backPress e está alternando entre os fragmentos ativos usando o padrão de estratégia.

Para alguns de vocês, talvez haja uma pequena desvantagem na classe abstrata ...

Em breve, a solução do link é assim:

// 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 uso na atividade:

// 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
fonte
Embora este link possa responder à pergunta, é melhor incluir as partes essenciais da resposta aqui e fornecer o link para referência. As respostas somente com link podem se tornar inválidas se a página vinculada mudar.
bummi
BTW: se você identificar duplicatas, sinalize-as como tal. THX.
bummi
é importante setSelectedFragment dentro de onStart?
VLeonovs de