Поскольку этому вопросу и некоторым ответам уже более пяти лет, позвольте мне поделиться своим решением. Это продолжение и модернизация в ответ от @oyenigun
ОБНОВИТЬ:
В нижней части этой статьи я добавил альтернативную реализацию, использующую абстрактное расширение Fragment, которое вообще не будет включать Activity, что было бы полезно для любого с более сложной иерархией фрагментов, включающей вложенные фрагменты, которые требуют различного обратного поведения.
Мне нужно было реализовать это, потому что некоторые из фрагментов, которые я использую, имеют меньшие представления, которые я хотел бы отклонить с помощью кнопки «Назад», такие как небольшие информационные представления, которые появляются, и т. Д., Но это хорошо для всех, кому нужно переопределить поведение кнопка возврата внутри фрагментов.
Сначала определите интерфейс
public interface Backable {
boolean onBackPressed();
}
Этот интерфейс, который я называю Backable
(я сторонник соглашений об именах), имеет единственный метод, onBackPressed()
который должен возвращать boolean
значение. Нам нужно ввести логическое значение, потому что нам нужно будет знать, «нажата» ли кнопка «назад», «поглотила» ли она событие назад. Возврат true
означает, что он есть, и никаких дальнейших действий не требуется, в противном случае он false
говорит, что обратное действие по умолчанию все еще должно иметь место. Этот интерфейс должен быть его собственным файлом (желательно в отдельном названном пакете interfaces
). Помните, что разделение ваших классов на пакеты - это хорошая практика.
Во-вторых, найдите верхний фрагмент
Я создал метод, который возвращает последний Fragment
объект в заднем стеке. Я использую теги ... если вы используете ID, внесите необходимые изменения. У меня есть этот статический метод в служебном классе, который имеет дело с состояниями навигации и т. Д., Но, конечно, поместите его там, где он вам больше подходит. Для назидания я поместил свой класс в класс 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;
}
Удостоверьтесь, что число обратных стеков больше 0, иначе ArrayOutOfBoundsException
может быть выброшено во время выполнения. Если оно не больше 0, вернуть ноль. Мы проверим нулевое значение позже ...
В-третьих, реализовать в фрагменте
Реализуйте Backable
интерфейс в любом фрагменте, где вам нужно переопределить поведение кнопки «Назад». Добавьте метод реализации.
public class SomeFragment extends Fragment implements
FragmentManager.OnBackStackChangedListener, Backable {
...
@Override
public boolean onBackPressed() {
// Logic here...
if (backButtonShouldNotGoBack) {
whateverMethodYouNeed();
return true;
}
return false;
}
}
В onBackPressed()
переопределении поместите любую логику, которая вам нужна. Если вы хотите, чтобы кнопка «Назад» не вставляла задний стек (поведение по умолчанию), верните true , чтобы событие «Назад» было поглощено. В противном случае верните false.
Наконец, в вашей деятельности ...
Переопределите onBackPressed()
метод и добавьте к нему следующую логику:
@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();
}
Мы получаем текущий фрагмент в заднем стеке, затем делаем нулевую проверку и определяем, реализует ли он наш Backable
интерфейс. Если это так, определите, было ли событие поглощено. Если это так, мы закончили onBackPressed()
и можем вернуться. В противном случае рассматривайте это как обычное нажатие назад и вызывайте метод super.
Второй вариант - не привлекать активность
Иногда вы не хотите, чтобы Activity обрабатывал это вообще, и вам нужно обрабатывать это непосредственно внутри фрагмента. Но кто сказал, что у вас не может быть фрагментов с API для обратной печати? Просто расширьте свой фрагмент до нового класса.
Создайте абстрактный класс, который расширяет Fragment и реализует View.OnKeyListner
интерфейс ...
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();
}
Как вы можете видеть, любой фрагмент, который расширяется, BackableFragment
будет автоматически захватывать обратные клики, используя View.OnKeyListener
интерфейс. Просто вызовите абстрактный onBackButtonPressed()
метод из реализованного onKey()
метода, используя стандартную логику, чтобы распознать нажатие кнопки назад. Если вам нужно зарегистрировать нажатия клавиш, отличные от кнопки «Назад», просто вызовите super
метод при переопределении onKey()
вашего фрагмента, иначе вы переопределите поведение в абстракции.
Прост в использовании, просто расширьте и внедрите:
public class FragmentChannels extends BackableFragment {
...
@Override
public void onBackButtonPressed() {
if (doTheThingRequiringBackButtonOverride) {
// do the thing
} else {
getActivity().onBackPressed();
}
}
...
}
Поскольку onBackButtonPressed()
метод в суперклассе является абстрактным, после расширения вы должны реализовать его onBackButtonPressed()
. Он возвращается, void
потому что ему просто нужно выполнить действие внутри класса фрагмента, и ему не нужно ретранслировать поглощение прессы обратно в Activity. Убедитесь, что вы вызываете onBackPressed()
метод Activity, если все, что вы делаете с кнопкой «назад», не требует обработки, в противном случае кнопка «Назад» будет отключена ... и вам это не нужно!
Предостережения
Как вы можете видеть, это устанавливает ключевой слушатель на корневой вид фрагмента, и нам нужно сфокусировать его. Если в вашем фрагменте, который расширяет этот класс (или в других внутренних фрагментах или представлениях, имеющих одинаковые значения), есть тексты редактирования (или любые другие представления для кражи фокуса), вам придется обрабатывать это отдельно. Есть хорошая статья о расширении EditText, чтобы потерять фокус на спине.
Я надеюсь, что кто-то найдет это полезным. Удачного кодирования.