, поскольку этому вопросу и некоторым из ответов уже более пяти лет, позвольте мне поделиться своим решением. Это продолжение и модернизация к ответу от @ oyenigun
UPDATE:
В нижней части этой статьи я добавил альтернативную реализацию, использующую абстрактное расширение Fragment, которое вообще не будет включать Activity, что было бы полезно для любого с более сложной иерархией фрагментов, включающей вложенные фрагменты, которые требуют различного обратного поведения.
Мне нужно было реализовать это, потому что некоторые из фрагментов, которые я использую, имеют меньшие представления, которые я хотел бы отклонить с помощью кнопки «Назад», такие как небольшие информационные представления, которые появляются, и т. Д., Но это хорошо для тех, кому нужно переопределить поведение кнопки возврата внутри фрагментов.
Сначала определите интерфейс
public interface Backable {
boolean onBackPressed();
}
Этот интерфейс, который я называю Backable
(я сторонник соглашений об именах), имеет единственный метод onBackPressed()
, который должен возвращать значение boolean
. Нам нужно ввести логическое значение, потому что нам нужно будет знать, «нажата» ли кнопка «назад», «поглотила» ли она событие назад. Возвращение true
означает, что оно есть, и никаких дальнейших действий не требуется, в противном случае false
говорит, что обратное действие по умолчанию все еще должно иметь место. Этот интерфейс должен быть его собственным файлом (желательно в отдельном пакете с именем interfaces
). Помните, что разделение ваших классов на пакеты - это хорошая практика.
Во-вторых, найдите верхний фрагмент
Я создал метод, который возвращает последний Fragment
объект в заднем стеке. Я использую теги ... если вы используете идентификаторы, внесите необходимые изменения. У меня есть этот статический метод в служебном классе, который имеет дело с состояниями навигации и т. Д., Но, конечно, поместите его там, где он вам больше подходит. Для назидания я поместил свой класс в класс 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()
и можем вернуться. В противном случае обработайте это как обычное нажатие назад и вызовите супер метод.
Второй вариант, чтобы не задействовать Задание
Временами вы не хотите, чтобы 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. Убедитесь, что вы вызываете метод Activity onBackPressed()
, если все, что вы делаете с кнопкой возврата, не требует обработки, в противном случае кнопка возврата будет отключена ... и вам не нужно что!
Предостережения
Как вы можете видеть, это устанавливает ключевой слушатель на корневой вид фрагмента, и нам нужно сфокусировать его. Если в вашем фрагменте, который расширяет этот класс (или в других внутренних фрагментах или представлениях, имеющих одинаковые значения), есть тексты редактирования (или любые другие представления для кражи фокуса), вам придется обрабатывать это отдельно. Есть хорошая статья о , расширяющая EditText , чтобы потерять фокус при обратном нажатии.
Я надеюсь, что кто-то найдет это полезным. Удачного кодирования.