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 boolean
valor. Precisamos impor um valor booleano, pois precisamos saber se o pressionamento do botão voltar "absorveu" o evento back. Retornar true
significa que sim, e nenhuma ação adicional é necessária; caso contrário, false
diz 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 Fragment
objeto 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, ArrayOutOfBoundsException
poderá 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 Backable
interface 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 Backable
interface. 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.OnKeyListner
interface ...
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 BackableFragment
captura automaticamente os cliques anteriores usando a View.OnKeyListener
interface. 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 super
mé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 void
porque 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.