Como implementar onBackPressed () em fragmentos?

460

Existe uma maneira de implementarmos onBackPressed() no Android Fragment semelhante à maneira como implementamos na Atividade Android?

Como o ciclo de vida do fragmento não possui onBackPressed(). Existe algum outro método alternativo para substituir os onBackPressed()fragmentos do Android 3.0?

Android_programmer_camera
fonte
13
IMHO, um fragmento não deve saber nem se importar com o botão VOLTAR. Uma atividade pode se importar com o botão VOLTAR, embora com fragmentos normalmente tratados pelo FragmentManager. Mas como o fragmento não conhece seu ambiente maior (por exemplo, se é ou não um dos vários na atividade), não é realmente seguro que um fragmento esteja tentando determinar o que é a funcionalidade BACK adequada.
CommonsWare 27/03
61
Que tal quando todo o fragmento exibe um WebView e você deseja que o WebView "volte" para a página anterior quando o botão Voltar é pressionado?
mharper
A resposta de Michael Herbig foi perfeita para mim. Mas para aqueles que não podem usar getSupportFragmentManager (). Use getFragmentManager ().
Dantalian
A resposta de Dmitry Zaitsev em [Pergunta semelhante] [1] funciona muito bem. [1]: stackoverflow.com/a/13450668/1372866
ashakirov
6
Para responder a mharper, você pode fazer algo como webview.setOnKeyListener (novo OnKeyListener () {@Override public boolean onKey (Exibir v, int keyCode, evento KeyEvent) {if (keyCode == KeyEvent.KEYCODE_BACK && webview.canGoBack ()) {webview.goBack (); return true;} return false;}});
Omid Aminiva

Respostas:

307

Eu resolvi dessa maneira substituir onBackPressedna Atividade. Todos os FragmentTransactionsão addToBackStackantes de confirmar:

@Override
public void onBackPressed() {

    int count = getSupportFragmentManager().getBackStackEntryCount();

    if (count == 0) {
        super.onBackPressed();
        //additional code
    } else {
        getSupportFragmentManager().popBackStack();
    }

}
Hw.Master
fonte
mesmo problema stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
4
count == 1 permitirá fechar o primeiro fragmento pressionando um único botão Voltar. Mas caso contrário, a melhor solução
Kunalxigxag
2
Se você estiver usando a biblioteca de suporte v7 e sua Atividade se estender de FragmentActivity (ou uma subclasse, como AppCompatActivity), isso acontecerá por padrão. Veja FragmentActivity # onBackPressed
Xiao
3
mas você não pode usar variáveis, classes e funções da classe do fragmento neste código
user25
2
@Prabs, se você estiver usando o fragmento support v4, verifique se está usando getSupportFragmentManager (). GetBackStackEntryCount ();
precisa saber é o seguinte
152

Na minha opinião, a melhor solução é:

SOLUÇÃO JAVA

Crie uma interface simples:

public interface IOnBackPressed {
    /**
     * If you return true the back press will not be taken into account, otherwise the activity will act naturally
     * @return true if your processing has priority if not false
     */
    boolean onBackPressed();
}

E na sua atividade

public class MyActivity extends Activity {
    @Override public void onBackPressed() {
    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.main_container);
       if (!(fragment instanceof IOnBackPressed) || !((IOnBackPressed) fragment).onBackPressed()) {
          super.onBackPressed();
       }
    } ...
}

Finalmente em seu fragmento:

public class MyFragment extends Fragment implements IOnBackPressed{
   @Override
   public boolean onBackPressed() {
       if (myCondition) {
            //action not popBackStack
            return true; 
        } else {
            return false;
        }
    }
}

SOLUÇÃO KOTLIN

1 - Criar interface

interface IOnBackPressed {
    fun onBackPressed(): Boolean
}

2 - Prepare sua atividade

class MyActivity : AppCompatActivity() {
    override fun onBackPressed() {
        val fragment =
            this.supportFragmentManager.findFragmentById(R.id.main_container)
        (fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let {
            super.onBackPressed()
        }
    }
}

3 - Implemente no seu fragmento alvo

class MyFragment : Fragment(), IOnBackPressed {
    override fun onBackPressed(): Boolean {
        return if (myCondition) {
            //action not popBackStack
            true
        } else {
            false
        }
    }
}
Maxime Jallu
fonte
1
O que é R.id.main_container? Esse é o ID do FragmentPager?
Nathan F.
1
O @MaximeJallu na solução Kotlin, misturar chamadas seguras ( .?) e aplicar não nulos ( !!) pode levar a NPEs - o elenco nem sempre é seguro. Prefiro escrever if ((fragment as? IOnBackPressed)?.onBackPressed()?.not() == true) { ... }ou mais kotliney(fragment as? IOnBackPressed)?.onBackPressed()?.not()?.let { ... }
Antek
1
@ Antek Olá, Obrigado pelo seu retorno, você poderia ter editado a postagem, para fazer sua correção. Não julgue severamente a solução geral.
Maxime jallu
4
Não deveria ser .onBackPressed()?.takeIf { !it }?.let{...}? .not()apenas retorna o inverso.
David Miguel
1
val fragment = this.supportFragmentManager.findFragmentById(R.id.flContainer) as? NavHostFragment val currentFragment = fragment?.childFragmentManager?.fragments?.get(0) as? IOnBackPressed currentFragment?.onBackPressed()?.takeIf { !it }?.let{ super.onBackPressed() }Isto é para pessoas que usam Kotlin e NavigationController
Pavle Pavlov
97

De acordo com a resposta @HaMMeRed, aqui está o pseudocódigo como deve funcionar. Digamos que sua atividade principal seja chamada BaseActivitycom fragmentos filhos (como no exemplo da SlidingMenu lib). Aqui estão os passos:

Primeiro precisamos criar interface e classe que implementa sua interface para ter um método genérico

  1. Criar interface de classe OnBackPressedListener

    public interface OnBackPressedListener {
        public void doBack();
    }
  2. Crie uma classe que implemente habilidades de OnBackPressedListener

    public class BaseBackPressedListener implements OnBackPressedListener {
        private final FragmentActivity activity;
    
        public BaseBackPressedListener(FragmentActivity activity) {
            this.activity = activity;
        }
    
        @Override
        public void doBack() {
            activity.getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
        }
    }
  3. Desde agora, trabalharemos em nosso código BaseActivitye seus fragmentos

  4. Crie um ouvinte privado no topo da sua turma BaseActivity

    protected OnBackPressedListener onBackPressedListener;
  5. criar método para definir o ouvinte BaseActivity

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }
  6. em substituir onBackPressedimplementar algo parecido

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
  7. no seu fragmento onCreateViewvocê deve adicionar o nosso ouvinte

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        activity = getActivity();
    
        ((BaseActivity)activity).setOnBackPressedListener(new BaseBackPressedListener(activity));
    
        View view = ... ;
    //stuff with view
    
        return view;
    }

Agora, quando você clica novamente em fragmento, deve capturar seu método personalizado de volta.

peixe morto
fonte
Se mais de um fragmento é um ouvinte, como você determina, em onBackPressed()qual fragmento estava sendo exibido quando o botão Voltar foi pressionado?
Al Lelopath
2
você pode personalizar clique ouvinte ao invés nova baseBackPressedListener e chamar lá como anônimo para definir próprio behaviuor, algo como isto:((BaseActivity)activity).setOnBackPressedListener(new OnBackpressedListener(){ public void doBack() { //...your stuff here }});
deadfish
Obrigado, eu mudei de posição sobre isso, no entanto, recomendo a dissociação com mensagens de transmissão local agora. Eu acho que produziria componentes de maior qualidade e altamente dissociados.
HaMMeReD 24/03
isue aqui stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
Isso funcionou perfeitamente para mim, eu reiniciar o Activity
raphaelbgr
86

Isso funcionou para mim: https://stackoverflow.com/a/27145007/3934111

@Override
public void onResume() {
    super.onResume();

    if(getView() == null){
        return;
    }

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                // handle back button's click listener
                return true;
            }
            return false;
        }
    });
}
Rodrigo Rodrigues
fonte
2
Trabalhou na perfeição! Mas você também deve lidar com a ação padrão, escrevendo provavelmente mais uma condição SE. Desde então, eu estava abaixando meu slideUpPanel se ele estivesse expandido. Então, primeiro tive que marcar uma verificação se (o painel está
aberto
18
O problema com esta solução é que o getView () retorna apenas a visualização principal (e não a subvisão, se eles obtiverem foco). Portanto, se você possui um EditText e digita algum texto, pressione "back" uma vez para ocultar o teclado, o segundo toque em "back" é iniciado a partir do "EditText" (uma exibição em si) e esse método parece não pegar o " botão "voltar" pressione então (já que ele está capturando apenas a visualização principal). Pelo que entendi, você pode capturar os eventos de pressionamento de tecla no seu "EditText", bem como em outras visualizações, mas se você tiver um formulário "complexo", isso não será tão limpo e sustentável quanto poderia ser.
30615 Pelpotronic
Esta é uma ótima solução simples para o pesadelo que é fragmentos. Se você possui um formulário edittext 'complexo', provavelmente deve excluir seu projeto e começar de novo. Retorne false quando desejar usar o comportamento padrão. Retornar verdadeiro quando você ter interceptado a imprensa volta e ter feito algo com ele
behelit
65

Se você quisesse esse tipo de funcionalidade, seria necessário substituí-lo em sua atividade e, em seguida, adicione uma YourBackPressedinterface a todos os seus fragmentos, que você chama no fragmento relevante sempre que o botão Voltar é pressionado.

Editar: gostaria de anexar minha resposta anterior.

Se eu fizesse isso hoje, usaria uma transmissão, ou possivelmente uma transmissão ordenada, se esperasse que outros painéis fossem atualizados em uníssono para o painel de conteúdo principal / principal.

LocalBroadcastManagerna Biblioteca de suporte pode ajudar com isso, e você apenas envia a transmissão onBackPressede assina seus fragmentos que se importam. Penso que o Messaging é uma implementação mais dissociada e seria melhor dimensionada, por isso seria minha recomendação oficial de implementação agora. Basta usar a Intentação do como um filtro para sua mensagem. envie o seu recém-criado ACTION_BACK_PRESSED, envie-o da sua atividade e ouça nos fragmentos relevantes.

HaMMeReD
fonte
4
Idéia bastante bacana! algumas idéias adicionais: + é importante perceber essa lógica com a transmissão saindo dos fragmentos de atividade (e não o contrário). O OnBackPressed está disponível apenas no nível da atividade, portanto, prender o evento lá e transmiti-lo a todos os fragmentos de "escuta" é fundamental aqui. + O uso de um EventBus (como o Otto da Square) torna a implementação de um caso como esse trivial!
Kaushik Gopal
2
Ainda não usei o EventBus da Square, mas estarei em produtos futuros. Eu acho que é uma solução melhor para java puro, e se você pode derrubar as seções da sua arquitetura do Android, tanto melhor.
21714 HaMMeReD
Como você deixa apenas os principais fragmentos para lidar com o evento? por exemplo, você iria querer destruir o momento mostrando fragmento
Eugene
O receptor de transmissão deve estar vinculado ao ciclo de vida se você estiver usando isso; portanto, quando não estiver visível, não deverá receber o evento.
HaMMeReD
Observe também que LocalBroadcastManagernão pode fazer transmissões encomendados
Xiao
46

Nada disso é fácil de implementar, nem funcionará da maneira ideal.

Os fragmentos têm uma chamada de método onDetach que fará o trabalho.

@Override
    public void onDetach() {
        super.onDetach();
        PUT YOUR CODE HERE
    }

ISSO FAZ O TRABALHO.

Sterling Diaz
fonte
20
mas o problema aqui é que você não sabe se o fragmento foi desanexado devido a uma contrapressão ou devido a alguma outra ação, que levou o usuário a executar alguma outra atividade ou fragmento.
Sunny
7
onDetach () é chamado quando o fragmento não está mais anexado à sua atividade. Isso é chamado após onDestroy (). Embora você chegue a esse código ao pressionar o botão Voltar, também o receberá ao mudar de fragmento para fragmento. Você poderia fazer algo bacana para fazer isso funcionar ...
apmartin1991
Funciona bem para um DialogFragment.
CoolMind 11/05
2
Não se esqueça de adicionar isRemoving()como descrito em stackoverflow.com/a/27103891/2914140 .
CoolMind 3/08
1
isto está errado. Ele pode ser chamado por várias razões
Bali
40

Se você estiver usando androidx.appcompat:appcompat:1.1.0ou acima, poderá adicionar um OnBackPressedCallbackao seu fragmento da seguinte maneira

requireActivity()
    .onBackPressedDispatcher
    .addCallback(this, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            Log.d(TAG, "Fragment back pressed invoked")
            // Do custom work here    

            // if you want onBackPressed() to be called as normal afterwards
            if (isEnabled) {
                isEnabled = false
                requireActivity().onBackPressed()
            }
        }
    }
)

Consulte https://developer.android.com/guide/navigation/navigation-custom-back

aaronmarino
fonte
Se você estiver usando androidx-core-ktx, você pode usarrequireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { /* code to be executed when back is pressed */ }
Max
É importante observar que o LifecycleOwnerparâmetro deve ser adicionado como neste exemplo. Sem ele, qualquer fragmento iniciado depois será chamado handleBackPressed()se o botão Voltar for pressionado.
Eric B.
este método deve estar com a biblioteca de navegação?
Silêncio
25

Basta adicionar addToBackStackenquanto você está fazendo a transição entre seus fragmentos, como abaixo:

fragmentManager.beginTransaction().replace(R.id.content_frame,fragment).addToBackStack("tag").commit();

se você escrever addToBackStack(null), ele cuidará sozinho, mas se você der uma tag, deverá manipulá-la manualmente.

Rudi
fonte
Há mais alguma coisa que precisa ser feita ou a adição do método addToBackStack é suficiente?
Sreecharan Desabattula
1
se você apenas adicionar que deve funcionar, se ainda não funcionar, deve haver algo no seu código. Deixe o código em algum lugar para ver, por favor @SreecharanDesabattula
Rudi
@Rudi, você pode dizer ao stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
20

Como esta pergunta e algumas das respostas têm mais de cinco anos, deixe-me compartilhar minha solução. Este é um acompanhamento e modernização da resposta de @oyenigun

ATUALIZAÇÃO: No final deste artigo, adicionei uma implementação alternativa usando uma extensão abstrata de fragmento que não envolverá a atividade, o que seria útil para qualquer pessoa com uma hierarquia de fragmentos mais complexa que envolva fragmentos aninhados que exigem comportamento reverso diferente.

Eu precisava implementar isso porque alguns dos fragmentos que utilizo têm visões menores que eu gostaria de descartar com o botão voltar, como pequenas visões de informações que aparecem, etc., mas isso é bom para quem precisa substituir o comportamento de o botão voltar dentro dos fragmentos.

Primeiro, defina uma interface

public interface Backable {
    boolean onBackPressed();
}

Essa interface, que eu chamo Backable(sou defensora das convenções de nomenclatura), tem um método único onBackPressed()que deve retornar um booleanvalor. Precisamos impor um valor booleano, pois precisamos saber se o pressionamento do botão voltar "absorveu" o evento back. Retornar truesignifica que sim, e nenhuma ação adicional é necessária; caso contrário, falsediz que a ação de retorno padrão ainda deve ocorrer. Essa interface deve ser seu próprio arquivo (de preferência em um pacote separado chamado interfaces). Lembre-se, separar suas classes em pacotes é uma boa prática.

Segundo, encontre o fragmento superior

Eu criei um método que retorna o último Fragmentobjeto na pilha de volta. Eu uso tags ... se você usa IDs, faça as alterações necessárias. Eu tenho esse método estático em uma classe de utilitário que lida com estados de navegação, etc ... mas é claro, coloque-o onde melhor lhe convier. Para edificação, coloquei o meu em uma classe chamada NavUtils.

public static Fragment getCurrentFragment(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    if (fragmentManager.getBackStackEntryCount() > 0) {
        String lastFragmentName = fragmentManager.getBackStackEntryAt(
                fragmentManager.getBackStackEntryCount() - 1).getName();
        return fragmentManager.findFragmentByTag(lastFragmentName);
    }
    return null;
}

Verifique se a contagem da pilha traseira é maior que 0, caso contrário, ArrayOutOfBoundsExceptionpoderá ser lançada em tempo de execução. Se não for maior que 0, retorne nulo. Mais tarde, verificaremos um valor nulo ...

Terceiro, implemente em um fragmento

Implemente a Backableinterface em qualquer fragmento em que você precise substituir o comportamento do botão Voltar. Adicione o método de implementação.

public class SomeFragment extends Fragment implements 
        FragmentManager.OnBackStackChangedListener, Backable {

...

    @Override
    public boolean onBackPressed() {

        // Logic here...
        if (backButtonShouldNotGoBack) {
            whateverMethodYouNeed();
            return true;
        }
        return false;
    }

}

Na onBackPressed()substituição, coloque qualquer lógica que você precisar. Se você deseja que o botão voltar não apareça a pilha de retorno (o comportamento padrão), retorne verdadeiro , pois seu evento de retorno foi absorvido. Caso contrário, retorne false.

Por fim, em sua atividade ...

Substitua o onBackPressed()método e adicione esta lógica a ele:

@Override
public void onBackPressed() {

    // Get the current fragment using the method from the second step above...
    Fragment currentFragment = NavUtils.getCurrentFragment(this);

    // Determine whether or not this fragment implements Backable
    // Do a null check just to be safe
    if (currentFragment != null && currentFragment instanceof Backable) {

        if (((Backable) currentFragment).onBackPressed()) {
            // If the onBackPressed override in your fragment 
            // did absorb the back event (returned true), return
            return;
        } else {
            // Otherwise, call the super method for the default behavior
            super.onBackPressed();
        }
    }

    // Any other logic needed...
    // call super method to be sure the back button does its thing...
    super.onBackPressed();
}

Colocamos o fragmento atual na pilha de trás, fazemos uma verificação nula e determinamos se ele implementa nossa Backableinterface. Se isso acontecer, determine se o evento foi absorvido. Nesse caso, terminamos onBackPressed()e podemos retornar. Caso contrário, trate-o como uma pressão normal de retorno e chame o super método.

Segunda opção para não envolver a atividade

Às vezes, você não quer que a Atividade lide com isso e precisa lidar diretamente com ela dentro do fragmento. Mas quem disse que você não pode ter fragmentos com uma API de contrapressão? Apenas estenda seu fragmento para uma nova classe.

Crie uma classe abstrata que estenda o fragmento e implemente a View.OnKeyListnerinterface ...

import android.app.Fragment;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;

public abstract class BackableFragment extends Fragment implements View.OnKeyListener {

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        view.setFocusableInTouchMode(true);
        view.requestFocus();
        view.setOnKeyListener(this);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_UP) {
            if (keyCode == KeyEvent.KEYCODE_BACK) {
                onBackButtonPressed();
                return true;
            }
        }

        return false;
    }

    public abstract void onBackButtonPressed();
}

Como você pode ver, qualquer fragmento que se estenda BackableFragmentcaptura automaticamente os cliques anteriores usando a View.OnKeyListenerinterface. Basta chamar o onBackButtonPressed()método abstrato de dentro do onKey()método implementado usando a lógica padrão para discernir um pressionamento do botão Voltar. Se você precisar registrar cliques de teclas que não sejam o botão Voltar, chame o supermétodo ao substituir onKey()seu fragmento, caso contrário, você substituirá o comportamento na abstração.

Simples de usar, basta estender e implementar:

public class FragmentChannels extends BackableFragment {

    ...

    @Override
    public void onBackButtonPressed() {
        if (doTheThingRequiringBackButtonOverride) {
            // do the thing
        } else {
            getActivity().onBackPressed();
        }
    }

    ...
}

Como o onBackButtonPressed()método na superclasse é abstrato, depois de estendido, você deve implementar onBackButtonPressed(). Ele retorna voidporque só precisa executar uma ação dentro da classe de fragmento e não precisa retransmitir a absorção da impressora de volta para a Atividade. Certifique-se de chamar o onBackPressed()método Activity se o que você estiver fazendo com o botão voltar não exigir manipulação, caso contrário, o botão voltar será desativado ... e você não quer isso!

Advertências Como você pode ver, isso define o ouvinte principal para a visualização raiz do fragmento, e precisaremos focalizá-lo. Se houver textos de edição envolvidos (ou qualquer outra exibição de roubo de foco) em seu fragmento que estenda essa classe (ou outros fragmentos ou exibições internas que tenham a mesma), será necessário lidar com isso separadamente. Há um bom artigo sobre a extensão de um EditText para perder o foco em uma contrapressão.

Espero que alguém ache isso útil. Feliz codificação.

mwieczorek
fonte
Obrigado, é uma boa solução. Você pode dizer, por que você liga super.onBackPressed();duas vezes?
CoolMind
É chamado apenas uma vez por cenário em relação ao estado nulo de currentFragment. Se o fragmento não for nulo e o fragmento implementar a Backableinterface, nenhuma ação será tomada. Se o fragmento não é nulo e NÃO é implementado Backable, chamamos o super método. Se o fragmento for nulo, ele pulará para a última super chamada.
Mkdcorek3
Desculpe, eu não entendi. No caso em que a currentFragmentnão é nulo, é uma instância de Backable e é desanexada (pressionamos o backbotão e fechamos o fragmento), a primeira ocorrência de super.onBackPressed();é chamada, depois a segunda.
CoolMind
Excelente Solution
David Toledo
Talvez "closeable" soa um pouco melhor do que "Backable"
pumnao
15

A solução é simples:

1) Se você tem uma classe de fragmento base que todos os fragmentos estendem, adicione esse código à sua classe, caso contrário, crie uma classe de fragmento base

 /* 
 * called when on back pressed to the current fragment that is returned
 */
 public void onBackPressed()
 {
    // add code in super class when override
 }

2) Na sua classe Activity, substitua onBackPressed da seguinte maneira:

private BaseFragment _currentFragment;

@Override
public void onBackPressed()
{
      super.onBackPressed();
     _currentFragment.onBackPressed();
}

3) Na sua classe Fragment, adicione o código desejado:

 @Override
 public void onBackPressed()
 {
    setUpTitle();
 }
IdoT
fonte
9

onBackPressed() faça com que o fragmento seja desconectado da atividade.

De acordo com a resposta de @Sterling Diaz, acho que ele está certo. MAS alguma situação estará errada. (por exemplo, Rodar ecrã)

Então, acho que poderíamos detectar se devemos isRemoving()alcançar objetivos.

Você pode escrever em onDetach()ou onDestroyView(). É trabalho.

@Override
public void onDetach() {
    super.onDetach();
    if(isRemoving()){
        // onBackPressed()
    }
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    if(isRemoving()){
        // onBackPressed()
    }
}
林奕 忠
fonte
8

Bem, eu fiz assim, e funcionou para mim

Interface simples

FragmentOnBackClickInterface.java

public interface FragmentOnBackClickInterface {
    void onClick();
}

Implementação de exemplo

MyFragment.java

public class MyFragment extends Fragment implements FragmentOnBackClickInterface {

// other stuff

public void onClick() {
       // what you want to call onBackPressed?
}

substitua onBackPressed na atividade

    @Override
public void onBackPressed() {
    int count = getSupportFragmentManager().getBackStackEntryCount();
    List<Fragment> frags = getSupportFragmentManager().getFragments();
    Fragment lastFrag = getLastNotNull(frags);
    //nothing else in back stack || nothing in back stack is instance of our interface
    if (count == 0 || !(lastFrag instanceof FragmentOnBackClickInterface)) {
        super.onBackPressed();
    } else {
        ((FragmentOnBackClickInterface) lastFrag).onClick();
    }
}

private Fragment getLastNotNull(List<Fragment> list){
    for (int i= list.size()-1;i>=0;i--){
        Fragment frag = list.get(i);
        if (frag != null){
            return frag;
        }
    }
    return null;
}
Błażej
fonte
Não está funcionando ! super.onbackpressed () é chamado sempre :(
Animesh Mangla
Como passar este evento Fragmento Interno. Eu tenho o ViewPager em Atividade e esse ViewPager tem fragmentos. Agora, todos esses fragmentos têm fragmento filho. Como passar o evento do botão Voltar para esses fragmentos filho.
Kishan Vaghela
@KishanVaghela Bem, eu não toquei no Android desde então, mas tenho uma ideia. Dê-me 24 para atualizar minha resposta.
Błażej
Ok, não há problema. Uma opção é que eu preciso passar o evento listener para cada fragmento filho do fragmento pai. mas não é o caminho ideal, porque preciso fazer isso para todos os fragmentos.
Kishan Vaghela
@KishanVaghela Eu estava pensando em gerente em atividade. Você pode adicionar / remover fragmentos com essa interface. Mas com essa abordagem, você precisa implementar o gerente em cada fragmento que possui subfragmentos. Então, talvez a melhor solução seja criar gerente como singleton. Então você terá que adicionar / remover fragmentos / objetos e em 'onBackPressed' você chamará o método 'onBackPressed' deste gerenciador. Esse método chamará o método 'onClick' em todos os objetos que foram adicionados ao gerenciador. Isso é mais ou menos claro para você?
Błażej
8

Você deve adicionar interface ao seu projeto como abaixo;

public interface OnBackPressed {

     void onBackPressed();
}

E então, você deve implementar essa interface no seu fragmento;

public class SampleFragment extends Fragment implements OnBackPressed {

    @Override
    public void onBackPressed() {
        //on Back Pressed
    }

}

E você pode acionar esse evento onBackPressed em suas atividades, como abaixo;

public class MainActivity extends AppCompatActivity {
       @Override
        public void onBackPressed() {
                Fragment currentFragment = getSupportFragmentManager().getFragments().get(getSupportFragmentManager().getBackStackEntryCount() - 1);
                if (currentFragment instanceof OnBackPressed) {  
                    ((OnBackPressed) currentFragment).onBackPressed();
                }
                super.onBackPressed();
        }
}
oyenigun
fonte
É um bom método, mas tenha cuidado, não chame getActivity().onBackPressed();, pois invocará a exceção: "java.lang.StackOverflowError: tamanho da pilha 8 MB".
CoolMind 01/03
8

Este é apenas um pequeno código que fará o truque:

 getActivity().onBackPressed();

Espero que ajude alguém :)

Shahid Sarwar
fonte
4
Ele pediu para substituir o comportamento, não para simplesmente invocar o método de atividade.
precisa saber é o seguinte
2
Eu li com atenção, obrigado. Sua solução simplesmente retransmite o método onBackPressed de volta para a Activity, quando ele perguntou como substituir.
Mkdcorek # 13/17
7

Se você usa o EventBus, provavelmente é uma solução muito mais simples:

No seu fragmento:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    EventBus.getDefault().register(this);
}

@Override
public void onDetach() {
    super.onDetach();
    EventBus.getDefault().unregister(this);
}


// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    getSupportFragmentManager().popBackStack();
}

e na sua classe Activity você pode definir:

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

// This method will be called when a MessageEvent is posted
public void onEvent(BackPressedMessage type){
    super.onBackPressed();
}

@Override
public void onBackPressed() {
    EventBus.getDefault().post(new BackPressedMessage(true));
}

BackPressedMessage.java é apenas um objeto POJO

Isso é super limpo e não há problemas de interface / implementação.

Akshat
fonte
7

esta é a minha solução:

em MyActivity.java:

public interface OnBackClickListener {
        boolean onBackClick();
    }

    private OnBackClickListener onBackClickListener;

public void setOnBackClickListener(OnBackClickListener onBackClickListener) {
        this.onBackClickListener = onBackClickListener;
    }

@Override
    public void onBackPressed() {
        if (onBackClickListener != null && onBackClickListener.onBackClick()) {
            return;
        }
        super.onBackPressed();
    }

e em fragmento:

((MyActivity) getActivity()).setOnBackClickListener(new MyActivity.OnBackClickListener() {
    @Override
    public boolean onBackClick() {
        if (condition) {
            return false;
        }

        // some codes

        return true;
    }
});
Hamidreza Samadi
fonte
6

Usando o componente Navigation, você pode fazer o seguinte :

Java

public class MyFragment extends Fragment {

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // This callback will only be called when MyFragment is at least Started.
    OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
        @Override
        public void handleOnBackPressed() {
            // Handle the back button event
        }
    });
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);

    // The callback can be enabled or disabled here or in handleOnBackPressed()
}
...
}

Kotlin

class MyFragment : Fragment() {

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // This callback will only be called when MyFragment is at least Started.
    val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
        // Handle the back button event
    }

    // The callback can be enabled or disabled here or in the lambda
}
...
}
B-GangsteR
fonte
4

Que tal usar onDestroyView ()?

@Override
public void onDestroyView() {
    super.onDestroyView();
}
O melhor artista
fonte
5
que tal ir para outro fragmento do fragmento real, o que acontecerá usando seu método? :)
Choletski 26/08/2015
Resposta mais inútil. É o mesmo que escrever i = iou if (1 < 0) {}.
CoolMind 30/10/19
4
@Override
public void onResume() {
    super.onResume();

    getView().setFocusableInTouchMode(true);
    getView().requestFocus();
    getView().setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
                // handle back button
                replaceFragmentToBackStack(getActivity(), WelcomeFragment.newInstance(bundle), tags);

                return true;
            }

            return false;
        }
    });
}
Gururaj Hosamani
fonte
Somente se os fragmentos não tiverem uma visão focalizável (como editar texto), permita que os fragmentos manuseiem o botão Voltar como esta resposta. Caso contrário, permita que a atividade que contém fragmentos faça isso por OnBackPress () quando a primeira resposta for escrita; uma vez que a visão focalizável roubará o foco do fragmento. No entanto, podemos recuperar o foco para o fragmento por foco claro, todas as visualizações focalizáveis ​​pelo método clearFocus () e reorientar para o fragmento.
Nguyen Tan Dat
4
public class MyActivity extends Activity {

    protected OnBackPressedListener onBackPressedListener;

    public interface OnBackPressedListener {
        void doBack();
    }

    public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
        this.onBackPressedListener = onBackPressedListener;
    }

    @Override
    public void onBackPressed() {
        if (onBackPressedListener != null)
            onBackPressedListener.doBack();
        else
            super.onBackPressed();
    } 

    @Override
    protected void onDestroy() {
        onBackPressedListener = null;
        super.onDestroy();
    }
}

no seu fragmento adicione o seguinte, não esqueça de implementar a interface da atividade principal.

public class MyFragment extends Framgent implements MyActivity.OnBackPressedListener {
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
         ((MyActivity) getActivity()).setOnBackPressedListener(this);
    }

@Override
public void doBack() {
    //BackPressed in activity will call this;
}

}
joelvarma
fonte
4

Na minha solução (Kotlin);

Estou usando a função onBackAlternative como um parâmetro em BaseActivity .

BaseActivity

abstract class BaseActivity {

    var onBackPressAlternative: (() -> Unit)? = null

    override fun onBackPressed() {
        if (onBackPressAlternative != null) {
            onBackPressAlternative!!()
        } else {
            super.onBackPressed()
        }
    }
}

Eu tenho uma função para definir onBackPressAlternative em BaseFragment .

BaseFragment

abstract class BaseFragment {

     override fun onStart() {
        super.onStart()
        ...
        setOnBackPressed(null) // Add this
     }

      //Method must be declared as open, for overriding in child class
     open fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
         (activity as BaseActivity<*, *>).onBackPressAlternative = onBackAlternative
     }
}

Então meu onBackPressAlternative está pronto para uso em fragmentos.

Fragmentos secundários

override fun setOnBackPressed(onBackAlternative: (() -> Unit)?) {
    (activity as BaseActivity<*, *>).onBackPressAlternative = {
        // TODO Your own custom onback function 
    }
}
Mete
fonte
3

Não implemente o método ft.addToBackStack () para que, ao pressionar o botão Voltar, sua atividade seja concluída.

proAddAccount = new ProfileAddAccount();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, proAddAccount);
//fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Muhammad Aamir Ali
fonte
me diga stackoverflow.com/questions/32132623/…
Aditya Vyas-Lakhan
3

Basta seguir estes passos:

Sempre ao adicionar um fragmento,

fragmentTransaction.add(R.id.fragment_container, detail_fragment, "Fragment_tag").addToBackStack(null).commit();

Em seguida, na atividade principal, substitua onBackPressed()

if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
    getSupportFragmentManager().popBackStack();
} else {
    finish();
}

Para manipular o botão Voltar no seu aplicativo,

Fragment f = getActivity().getSupportFragmentManager().findFragmentByTag("Fragment_tag");
if (f instanceof FragmentName) {
    if (f != null) 
        getActivity().getSupportFragmentManager().beginTransaction().remove(f).commit()               
}

É isso aí!

Divya
fonte
3

No ciclo de vida da atividade, sempre o botão voltar do Android lida com transações do FragmentManager quando usamos FragmentActivity ou AppCompatActivity.

Para lidar com o backstack, não precisamos manipular sua contagem de backstack ou marcar nada, mas devemos manter o foco ao adicionar ou substituir um fragmento. Encontre os seguintes snippets para lidar com as caixas dos botões voltar,

    public void replaceFragment(Fragment fragment) {

        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
        }
        transaction.replace(R.id.activity_menu_fragment_container, fragment).commit();
    }

Aqui, não adicionarei pilha traseira ao meu fragmento de página inicial porque é a página inicial do meu aplicativo. Se adicionar addToBackStack ao HomeFragment, o aplicativo aguardará a remoção de todo o problema de atividade, e a tela ficará em branco, mantendo a seguinte condição,

if (!(fragment instanceof HomeFragment)) {
            transaction.addToBackStack(null);
}

Agora, você pode ver o fragmento adicionado anteriormente na acitvity e o aplicativo será fechado ao acessar o HomeFragment. você também pode procurar nos seguintes trechos.

@Override
public void onBackPressed() {

    if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) {
        closeDrawer();
    } else {
        super.onBackPressed();
    }
}
Deva
fonte
3

Fragmento: Faça um BaseFragment colocando um método:

 public boolean onBackPressed();

Atividade:

@Override
public void onBackPressed() {
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            if (!fragment.isVisible()) continue;

            if (fragment instanceof BaseFragment && ((BaseFragment) fragment).onBackPressed()) {
                return;
            }
        }
    }

    super.onBackPressed();
}

Sua atividade será executada nos fragmentos anexados e visíveis e chamará BackBackPressed () em cada um deles e abortará se um deles retornar 'verdadeiro' (o que significa que foi tratado, portanto, não há mais ações).

Alécio Carvalho
fonte
Senhor, este é o caminho mais elegante e funciona perfeito!
asozcan
3

De acordo com as notas de versão do AndroidX , androidx.activity 1.0.0-alpha01é lançado e apresenta ComponentActivityuma nova classe base dos arquivos existentes FragmentActivitye AppCompatActivity. E esta versão nos traz um novo recurso:

Agora você pode registrar uma OnBackPressedCallbackvia addOnBackPressedCallbackpara receber onBackPressed()retornos de chamada sem precisar substituir o método em sua atividade.

TonnyL
fonte
Você pode mostrar um exemplo disso? Não estou conseguindo resolver esse método.
Bryan Bryce
1
@BryanBryce Eu não encontrei um exemplo, mas o documento oficial: developer.android.com/reference/androidx/activity/…
TonnyL
O método não resolverá o fato de que o AppCompatActivity está estendendo de androidx.core.app.ComponentActivity em vez de androidx.activity.ComponentActivity.
wooldridgetm
3

Você pode registrar um fragmento em atividade para lidar com a contrapressão:

interface BackPressRegistrar {
    fun registerHandler(handler: BackPressHandler)
    fun unregisterHandler(handler: BackPressHandler)
}

interface BackPressHandler {
    fun onBackPressed(): Boolean
}

uso:

No fragmento:

private val backPressHandler = object : BackPressHandler {
    override fun onBackPressed(): Boolean {
        showClosingWarning()
        return false
    }
}

override fun onResume() {
    super.onResume()
    (activity as? BackPressRegistrar)?.registerHandler(backPressHandler)
}

override fun onStop() {
    (activity as? BackPressRegistrar)?.unregisterHandler(backPressHandler)
    super.onStop()
}

Em Atividade:

class MainActivity : AppCompatActivity(), BackPressRegistrar {


    private var registeredHandler: BackPressHandler? = null
    override fun registerHandler(handler: BackPressHandler) { registeredHandler = handler }
    override fun unregisterHandler(handler: BackPressHandler) { registeredHandler = null }

    override fun onBackPressed() {
        if (registeredHandler?.onBackPressed() != false) super.onBackPressed()
    }
}
Francis
fonte
3

Fornecer navegação de retorno personalizada manipulando onBackPressed agora é mais fácil com retornos de chamada dentro do fragmento.

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                if (true == conditionForCustomAction) {
                    myCustomActionHere()
                } else  NavHostFragment.findNavController(this@MyFragment).navigateUp();    
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(
        this, onBackPressedCallback
    )
    ...
}

Se você deseja a ação de retorno padrão com base em alguma condição, pode usar:

 NavHostFragment.findNavController(this@MyFragment).navigateUp();
krishnakumarcn
fonte
3

Dentro do método onCreate do fragmento, adicione o seguinte:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    OnBackPressedCallback callback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            //Handle the back pressed
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
}
David Elmasllari
fonte
depois de adicionar esse backpress myFragment funciona, mas agora a atividade backpress não funcionou
Sunil Chaudhary
2

Melhor solução,

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {
    @Override
    public void onBackPressed() {

        FragmentManager fm = getSupportFragmentManager();
        for (Fragment frag : fm.getFragments()) {
            if (frag == null) {
                super.onBackPressed();
                finish();
                return;
            }
            if (frag.isVisible()) {
                FragmentManager childFm = frag.getChildFragmentManager();
                if (childFm.getFragments() == null) {
                    super.onBackPressed();
                    finish();
                    return;
                }
                if (childFm.getBackStackEntryCount() > 0) {
                    childFm.popBackStack();
                    return;
                }
                else {

                    fm.popBackStack();
                    if (fm.getFragments().size() <= 1) {
                        finish();
                    }
                    return;
                }

            }
        }
    }
}
Shervin Gharib
fonte