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?


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

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.


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


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

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í.


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
    ...
}

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.


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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.