Обработка кнопки возврата в компоненте навигации Android

Я хотел бы знать, как правильно обрабатывать действие кнопки возврата системы с помощью контроллера навигации. В моем приложении у меня есть два фрагмента (например, фрагмент1 и фрагмент2), и у меня есть действие во фрагменте1 с назначением на фрагмент2. Все работает хорошо, кроме одного - когда пользователь нажимает кнопку возврата системы во фрагменте 2, я хочу показать диалоговое окно (например, используя DialogFragment) для подтверждения выхода. Как лучше всего реализовать такое поведение? Если я использую app:defaultNavHost = "true" во фрагменте хоста, он автоматически возвращается, игнорируя мои правила. И, кроме того, для чего нужен этот компонент?

Обработка кнопки возврата в компоненте навигации Android

Может быть, я должен использовать "pop to"?

С помощью «Pop to» вы можете определить, куда идти (пункт назначения) при нажатии на кнопку «Назад / вверх».

Alex 26.06.2018 15:26

@Alex Итак, если для него установлено значение none, как он должен реагировать на кнопку "Назад"?

Kiryl Tkach 26.06.2018 15:33

Когда для него установлено значение «нет», поведение по умолчанию, пользователь будет перемещен к предыдущему месту назначения (фрагмент 1)

Alex 26.06.2018 15:36

@Alex, хорошо, есть ли способ обработать кнопку возврата по второму фрагменту?

Kiryl Tkach 26.06.2018 15:47
stackoverflow.com/a/51044879/1268507
Alex 26.06.2018 16:06
104
5
121 003
27
Перейти к ответу Данный вопрос помечен как решенный

Ответы 27

Вот решение, которое должно делать то, что вы хотите, но я думаю, что это плохое решение, потому что оно противоречит идее компонента Android Navigation (позволяя Android обрабатывать навигацию).

Заменить onBackPressed в своей деятельности

override fun onBackPressed() {
    when(NavHostFragment.findNavController(nav_host_fragment).currentDestination.id) {
        R.id.fragment2-> {
            val dialog=AlertDialog.Builder(this).setMessage("Hello").setPositiveButton("Ok", DialogInterface.OnClickListener { dialogInterface, i ->
                finish()
            }).show()
        }
        else -> {
            super.onBackPressed()
        }
    }
} 

Спасибо за ваше решение, но я использовал его, прежде чем решил переключиться на компонент навигации. Вроде еще рано :(

Kiryl Tkach 26.06.2018 16:42

это слишком рано. Я реализовал аналогичное хакерское решение для управления навигацией по панели приложений

filthy_wizard 25.02.2019 11:28

Итак, я создал интерфейс

public interface OnBackPressedListener {
    void onBackPressed();
}

И реализовал это всеми фрагментами, которые нужно обработать кнопкой возврата. В основном я переопределил метод onBackPressed():

@Override
public void onBackPressed() {
    final Fragment currentFragment = mNavHostFragment.getChildFragmentManager().getFragments().get(0);
    final NavController controller = Navigation.findNavController(this, R.id.nav_host_fragment);
    if (currentFragment instanceof OnBackPressedListener)
        ((OnBackPressedListener) currentFragment).onBackPressed();
    else if (!controller.popBackStack())
        finish();

}

Итак, если верхний фрагмент моего узла навигации реализует интерфейс OnBackPressedListener, я вызываю его метод onBackPressed(), в другом месте я просто возвращаю стек и закрываю приложение, если задний стек пуст.

Приличное решение. Хотя мне всегда интересно, в чем смысл Navigation Component, если у него нет такой функциональности из коробки.

First_Strike 24.12.2018 14:14

Проверьте ответ @Jurij Pitulja - это рекомендуемый способ решения проблемы

Mitch 30.04.2019 00:30

Зачем нужен отдельный интерфейс вместо того, что есть в Jetpack?

IgorGanapolsky 19.03.2020 15:38

@IgorGanapolsky на момент ответа в Jetpack не было интерфейса.

Kiryl Tkach 19.03.2020 16:15

@KirylTkach, что такое объявление mNavHostFragment?

E.Akio 03.05.2020 20:56

@ E.Akio это ваш navHostFragment в вашем макете developer.android.com/reference/androidx/navigation/fragment‌ /…

Kiryl Tkach 03.05.2020 22:38

@KirylTkach Конечно, я это знаю, но каков код для его реализации?

E.Akio 04.05.2020 16:15

Попробуй это. Думаю, это тебе поможет.

override fun onBackPressed() {
    when (mNavController.getCurrentDestination()!!.getId()) {

        R.id.loginFragment -> {
            onWarningAlertDialog(this, "Alert", "Do you want to close this application ?")
        }
        R.id.registerFragment -> {
            super.onBackPressed()
        }
    }
}



private fun onWarningAlertDialog(mainActivity: MainActivity, s: String, s1: String) {

        val dialogBuilder = AlertDialog.Builder(this)
        dialogBuilder.setMessage(/*""*/s1)
                .setCancelable(false)
                .setPositiveButton("Proceed", DialogInterface.OnClickListener { dialog, id ->
                    finish()
                })
                .setNegativeButton("Cancel", DialogInterface.OnClickListener { dialog, id ->
                    dialog.cancel()
                })

        // create dialog box
        val alert = dialogBuilder.create()
        // set title for alert dialog box
        alert.setTitle("AlertDialogExample")
        // show alert dialog
        alert.show()
    }

Обновлять22 апреля '21

Я обновляю свой ответ, чтобы продемонстрировать образец рекомендуемого подхода, который также указан выше принятый ответ.

class MyFragment : Fragment() {

    ...
    
    private val backPressedDispatcher = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // Redirect to our own function
            [email protected]()
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...

        setHasOptionsMenu(true) //Set this to true in order to trigger callbacks to Fragment#onOptionsItemSelected

        (requireActivity() as AppCompatActivity).apply {
            // Redirect system "Back" press to our dispatcher
            onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressedDispatcher)

            // Set toolbar if it is in Fragment's layout. If you have a global toolbar that lives in Activity layout, then you don't need this line.
            setSupportActionBar(view.findViewById(R.id.toolbar))

            // Setup action bar to work with NavController
            setupActionBarWithNavController(findNavController())
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return if (item.itemId == android.R.id.home) {
            // Redirect "Up/Home" button clicks to our own function
            [email protected]()
            true
        } else {
            super.onOptionsItemSelected(item)
        }
    }

    private fun onBackPressed() {
        // Work your magic! Show dialog etc.
    }

    override fun onDestroyView() {
        // It is optional to remove since our dispatcher is lifecycle-aware. But it wouldn't hurt to just remove it to be on the safe side.
        backPressedDispatcher.remove() 
        super.onDestroyView()
    }

 }

Оригинальный ответ03 янв.

Немного поздно для вечеринки, но с последней версией Navigation Component 1.0.0-alpha09 теперь у нас есть AppBarConfiguration.OnNavigateUpListener.

Обратитесь к этим ссылкам для получения дополнительной информации: https://developer.android.com/reference/androidx/navigation/ui/AppBarConfiguration.OnNavigateUpListenerhttps://developer.android.com/jetpack/docs/release-notes

Спасибо, что показали мне примечания к выпуску! Выяснилось, что android: menuCategory = "вторичный" избегает выталкивания заднего стека!

Valentin Gavran 06.01.2019 18:00

для меня это работает только с панелью инструментов, но не с кнопкой "Назад"

Jurij Pitulja 07.02.2019 20:26

То же самое ^ работает для панели инструментов, как обрабатывать кнопку назад?

Gauri Gadkari 21.04.2021 21:17

@GauriGadkari Спасибо за отзыв! К сожалению, вы правы, мой первоначальный ответ не относится к системному нажатию на заднюю часть. Поэтому я обновил свой ответ примером использования рекомендованного подхода. См. Также принятый ответ.

Onur D. 22.04.2021 23:09
Ответ принят как подходящий

Новейшее обновление - 25 апреля 2019 г.

В новом выпуске androidx.activity вер. 1.0.0-alpha07 внесены некоторые изменения

Дополнительные объяснения в официальном руководстве по Android: Обеспечьте настраиваемую обратную навигацию

Пример:

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

Старые обновления

UPD: 3 апреля 2019 г.

Теперь это упрощено. Подробнее здесь

Пример:

requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), this);

@Override
public boolean handleOnBackPressed() {
    //Do your job here
    //use next line if you just need navigate up
    //NavHostFragment.findNavController(this).navigateUp(); 
    //Log.e(getClass().getSimpleName(), "handleOnBackPressed");
    return true;
    }

Устарело (начиная с версии 1.0.0-alpha06 3 апреля 2019 г.):

Начиная с это, это можно реализовать, просто используя реализацию JetPackOnBackPressedCallback в вашем фрагменте и добавьте его в действие: getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);

Ваш фрагмент должен выглядеть так:

public MyFragment extends Fragment implements OnBackPressedCallback {

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        
        getActivity().addOnBackPressedCallback(getViewLifecycleOwner(),this);
}
    
    @Override
    public boolean handleOnBackPressed() {
        //Do your job here
        //use next line if you just need navigate up
        //NavHostFragment.findNavController(this).navigateUp(); 
        //Log.e(getClass().getSimpleName(), "handleOnBackPressed");
        return true;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        getActivity().removeOnBackPressedCallback(this);
    }
}

UPD: Ваша активность должна расширять AppCompatActivity или FragmentActivity и в файле Gradle:

 implementation 'androidx.appcompat:appcompat:{lastVersion}'

Это все еще существует в Jetpack?

Bryan Bryce 05.02.2019 00:37

Должен ли он быть, если я использую AppCompatActivity? К какому классу он принадлежит?

Bryan Bryce 05.02.2019 01:03

Если ваша активность AppCompat не из библиотеки поддержки, а из библиотеки androidX, тогда да, вам следует

Jurij Pitulja 05.02.2019 01:06

Использование AndroidX AppCompatActivity и Androidx Fragment, и я тоже не считаю это доступным вариантом

Omega142 07.02.2019 17:09

Должно. Проверьте версию или попробуйте очистить и пересобрать проект.

Jurij Pitulja 07.02.2019 20:24

Omega142 Я считаю, что вам следует перейти от ComponentActivity, чтобы иметь возможность переопределить этот метод

Onur D. 07.02.2019 20:31

ComponentActivity, новый базовый класс существующих FragmentActivity и AppCompatActivity

filthy_wizard 25.02.2019 17:42

Не забудьте добавить "androidx.activity: activity-ktx:" в список зависимостей. Если это не поможет, то Android Studio, скорее всего, ссылается на androidx.core.app.ComponentActivity вместо androidx.activity.ComponentActivity. developer.android.com/reference/androidx/appcompat/app/… ясно показывает, что AppCompatActivity является подклассом androidx.activity.ComponentActivity. Проблема в том, что AppCompatActivity ссылается на androidx.core.app.ComponentActivity.

wooldridgetm 27.02.2019 22:41

Я знаю, что это старый поток, но я надеюсь, что вы его видите, у меня есть этот handleBackClick, он отлично работает, проходит через все фрагменты, которые он настроил, но он все еще активирует MainActivity onBackPressed (). Я думал, если один из фрагментов поглотил его, он на этом закончился?

Dave 09.03.2019 13:32

Считайте, что ваше возвращаемое значение от handleOnBackPressed - «false», это единственная причина, по которой он вызывает действие «onBackPressed». Измените его на "true".

Jurij Pitulja 09.03.2019 15:49

addOnBackPressedCallback - это метод androidx.activity.ComponentActivity, который расширяет androidx.core.app.ComponentActivity. Это означает, что если вы используете, например, androidx.fragment.app.FragmentActivity, который также расширяет androidx.core.app.ComponentActivity, у вас addOnBackPressedCallback недоступен.

Nantoka 25.03.2019 18:27

Да. Вы должны использовать AppCompatActivity. Также в будущих выпусках androidX он может быть отделен от активности.

Jurij Pitulja 25.03.2019 18:49

@JurijPitulja androidx.appcompat.app.AppCompatActivity расширяет androidx.fragment.app.FragmentActivity, поэтому у вас также нет метода addOnBackPressedCallback в AppCompatActivity, по крайней мере, с текущей стабильной версией 1.0.2 библиотеки совместимости androidx.

Nantoka 25.03.2019 20:43

Да, ты прав. В стабильной версии такого метода нет. Я использую последнюю альфу. Первая ссылка в посте показывает, когда и в какой версии она доступна.

Jurij Pitulja 25.03.2019 23:59

Я думаю, что с 16 апреля вам понадобится еще одно обновление, потому что кажется, что OnBackPressedCallback стал абстрактным классом, а множественного наследования нет, поэтому сценарий с BaseFragment, передающим "this" в качестве параметра для addCalback (), падает в воду.

Brcinho 26.04.2019 15:48

Это ХОРОШО РАБОТАЕТ. Я рекомендую вам использовать первое - обновленное решение.

Mitch 30.04.2019 00:26

Где я могу сообщить об ошибке? Если активность прекращается системой. Тогда последним обратным вызовом будет не тот, который вы добавили в метод «onCreate ()», а тот, который был получен из NavHostFragment.

Kęstas Venslauskas 20.05.2019 13:32

OnBackPressedCallback теперь является абстрактным классом! Так что это решение не работает для меня.

Junia Montana 13.09.2019 03:26

@JuniaMontana, чтобы вы могли создать частное поле mOnBackPressedCallback и передать его вместо передачи «this».

Kiryl Tkach 13.09.2019 16:28

Мне было интересно, почему это решение не работает для меня. Позже я обнаружил, что переопределил onBackPressed (), но не вызвал super.onBackPressed () из переопределенного метода. Все обратные вызовы, зарегистрированные через addCallback, оцениваются, когда вы вызываете super.onBackPressed (). onBackPressed вызывается всегда, независимо от зарегистрированных экземпляров OnBackPressedCallback.

Anik Dey 21.10.2019 10:37

Как выполнить нормальную операцию возврата после выполнения некоторой логики на - handleIOnBackPressed ()?

krishnakumarcn 01.11.2019 12:59

Для меня это не вызывает, если нажата кнопка «Назад» на панели инструментов.

nullmn 18.02.2020 19:06

Это зависит от того, какую панель инструментов вы используете. Есть разные случаи

Jurij Pitulja 18.02.2020 19:21

в вашем новейшем решении, если я хочу иметь условие в моем обратном вызове, и если условие имеет значение true, сделайте что-нибудь, иначе верните управление действием, как мне это сделать? Пример: OnBackPressedCallback callback = new OnBackPressedCallback (true) {@Override public void handleOnBackPressed () {if (условие) {// сделать что-то} else {// activity.onSupportNavigationUp ()}}};

roghayeh hosseini 01.04.2020 23:12

Я бы хотел, чтобы они еще больше упростили это Написание прослушивателя обратного нажатия для глобальной работы с компонентом навигации в основном не идеально - может иметь смысл иметь свойство API в типе <activity> <fragment> навигационного графа, чтобы можно было указать, нужен ли вам пользовательский обработчик для навигации внутри этого класса или null для отключения обратного нажатия.

Saifur Rahman Mohsin 11.08.2020 09:57

В новейшей версии android 11 это вызывает ошибку, если мы вызываем ее в onCreate `java.lang.IllegalStateException: не удается получить доступ к LifecycleOwner представления фрагментов, когда getView () имеет значение null, то есть до onCreateView () или после onDestroyView ()`

androidtitan 15.12.2020 23:57

Необязательно вызывать removeOnBackPressedCallback из onDestroyView, потому что действие автоматически удаляет обратные вызовы в зависимости от продолжительности жизни viewLifeCycleOwner, переданного в вызов addOnBackPressedCallback.

Monte Creasor 11.01.2021 23:12

Вот мое решение

Используйте androidx.appcompat.app.AppCompatActivity для активности, содержащей фрагмент NavHostFragment.

Определите следующий интерфейс и реализуйте его во всех фрагментах пункта назначения навигации.

interface InterceptionInterface {

    fun onNavigationUp(): Boolean
    fun onBackPressed(): Boolean
}

В вашей деятельности переопределите onSupportNavigateUp и onBackPressed:

override fun onSupportNavigateUp(): Boolean {
        return getCurrentNavDest().onNavigationUp() || navigation_host_fragment.findNavController().navigateUp()
}

override fun onBackPressed() {
        if (!getCurrentNavDest().onBackPressed()){
            super.onBackPressed()
        }
}

private fun getCurrentNavDest(): InterceptionInterface {
        val currentFragment = navigation_host_fragment.childFragmentManager.primaryNavigationFragment as InterceptionInterface
        return currentFragment
}

Это решение имеет то преимущество, что фрагментам назначения навигации не нужно беспокоиться об отмене регистрации своих слушателей, как только они будут отсоединены.

Я попробовал решение Jurij Pitulja, но мне просто не удалось найти getOnBackPressedDispatcher или addOnBackPressedCallback также с помощью решения Кирилла Ткача не удалось найти текущий фрагмент, поэтому вот мой:

interface OnBackPressedListener {
    fun onBackPressed(): Boolean
}

override fun onBackPressed() {
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
    val currentFragment = navHostFragment?.childFragmentManager!!.fragments[0]
    if (currentFragment !is OnBackPressedListener || !(currentFragment as OnBackPressedListener).onBackPressed()) super.onBackPressed()

таким образом вы можете решить во фрагменте, должно ли действие управлять нажатием кнопки или нет.

В качестве альтернативы у вас есть BaseActivity для всех ваших действий, вы можете реализовать это так

override fun onBackPressed() {
    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)
    if (navHostFragment != null){
        val currentFragment = navHostFragment.childFragmentManager.fragments[0]
        if (currentFragment !is AuthContract.OnBackPressedListener ||
                !(currentFragment as AuthContract.OnBackPressedListener).onBackPressed()) super.onBackPressed()
    } else {
        super.onBackPressed()
    }
}

Подход рекомендуемые заключается в добавлении OnBackPressedCallback к OnBackPressedDispatcher активности.

requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { 
    // handle back event
}

Лучше, если владелец обратного вызова также будет передан как этот .addCallback(viewLifecycleOwner) {}, иначе вы продолжите получать обратные вызовы даже после уничтожения фрагмента.

Marat 06.11.2019 15:16

В 2.1.0-alpha06

Если вы хотите обрабатывать обратное нажатие только в текущем фрагменте

requireActivity().onBackPressedDispatcher.addCallback(this@LoginFragment) {
    // handle back event
}

Для всей деятельности

requireActivity().onBackPressedDispatcher.addCallback() {
    // handle back event
}

Для тех, кто ищет реализацию Kotlin, см. Ниже.

Обратите внимание, что OnBackPressedCallback, похоже, работает только для обеспечения настраиваемого поведения назад для встроенной программной / аппаратной кнопки возврата, а не для кнопки со стрелкой назад / кнопки возврата домой в качестве кнопки вверх на панели действий / панели инструментов. Чтобы также переопределить поведение кнопки возврата на панели действий / панели инструментов, я предлагаю решение, которое работает для меня. Если это ошибка или вам известно о лучшем решении для этого случая, прокомментируйте.

build.gradle

...
implementation "androidx.appcompat:appcompat:1.1.0-rc01"
implementation "androidx.navigation:navigation-fragment-ktx:2.0.0"
implementation "androidx.navigation:navigation-ui-ktx:2.0.0"
...

MainActivity.kt

...
import androidx.appcompat.app.AppCompatActivity
...

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        setContentView(R.layout.activity_main)

        ...

        val navController = findNavController(R.id.nav_host_fragment)
        val appBarConfiguration = AppBarConfiguration(navController.graph)

        // This line is only necessary if using the default action bar.
        setupActionBarWithNavController(navController, appBarConfiguration)

        // This remaining block is only necessary if using a Toolbar from your layout.
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        toolbar.setupWithNavController(navController, appBarConfiguration)
        // This will handle back actions initiated by the the back arrow 
        // at the start of the toolbar.
        toolbar.setNavigationOnClickListener {
            // Handle the back button event and return to override 
            // the default behavior the same way as the OnBackPressedCallback.
            // TODO(reason: handle custom back behavior here if desired.)

            // If no custom behavior was handled perform the default action.
            navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
        }
    }

    /**
     * If using the default action bar this must be overridden.
     * This will handle back actions initiated by the the back arrow 
     * at the start of the action bar.
     */
    override fun onSupportNavigateUp(): Boolean {
        // Handle the back button event and return true to override 
        // the default behavior the same way as the OnBackPressedCallback.
        // TODO(reason: handle custom back behavior here if desired.)

        // If no custom behavior was handled perform the default action.
        val navController = findNavController(R.id.nav_host_fragment)
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
}

MyFragment.kt

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val onBackPressedCallback = object : OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // Handle the back button event
            }
        }
        requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback)
    }
}

Официальную документацию можно посмотреть на https://developer.android.com/guide/navigation/navigation-custom-back

Это лучший ответ. Вы должны уметь обрабатывать обратное нажатие с панели инструментов и обычное обратное нажатие. Недурно

odifek 14.01.2020 11:11

Это мне очень помогло! Моя кнопка возврата на панели инструментов не запускала OnBackPressedCallback, но после добавления toolbar.setNavigationOnClickListener { onBackPressed() } она работала, так что теперь аппаратное обеспечение и панель инструментов работают одинаково. Спасибо за четкий развернутый ответ!

poby 03.05.2020 09:18

Это правильное и наиболее логичное решение. Обязательно выдержит испытание временем.

Otieno Rowland 21.06.2020 21:09

Я использовал только код для обратного переопределения фрагмента, он отлично работает!

sud007 29.11.2020 17:48

Если вы используете BaseFragment для своего приложения, вы можете добавить onBackPressedDispatcher к своему базовому фрагменту.

//Make a BaseFragment for all your fragments
abstract class BaseFragment : Fragment() {

private lateinit var callback: OnBackPressedCallback

/**
 * SetBackButtonDispatcher in OnCreate
 */

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

/**
 * Adding BackButtonDispatcher callback to activity
 */
private fun setBackButtonDispatcher() {
    callback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            onBackPressed()
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(this, callback)
}

/**
 * Override this method into your fragment to handleBackButton
 */
  open fun onBackPressed() {
  }

}

Переопределите onBackPressed () в вашем фрагменте, расширив basefragment

//How to use this into your fragment
class MyFragment() : BaseFragment(){

private lateinit var mView: View

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    mView = inflater.inflate(R.layout.fragment_my, container, false)
    return mView.rootView
}

override fun onBackPressed() {
    //Write your code here on back pressed.
}

}

В основном действии я писал вот так:

override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
    }   

В зависимости от вашей логики, если вы хотите закрыть только текущий фрагмент, вам нужно передать viewLifecycleOwner, код показан ниже:

   requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            requireActivity().finish()
        }
    })

Однако, если вы хотите закрыть приложение на backPressed независимо от того, из какого фрагмента (возможно, вы этого не захотите!), Не передавайте viewLifecycleOwner. Также, если вы хотите отключить кнопку «Назад», не делайте ничего внутри handleOnBackPressed (), см. Ниже:

 requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // do nothing it will disable the back button
        }
    })

Это 2 строки кода, которые могут прослушивать обратное нажатие, из фрагментов, [ПРОВЕРЕНО и РАБОТАЕТ]

  requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {

            //setEnabled(false); // call this to disable listener
            //remove(); // call to remove listener
            //Toast.makeText(getContext(), "Listing for back press from this fragment", Toast.LENGTH_SHORT).show();
     }

Рекомендуемый метод работал у меня, но после обновления моей библиотеки implementation 'androidx.appcompat:appcompat:1.1.0'

Реализуйте, как показано ниже

 val onBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // Handle the back button event
        }
    }
    requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)

используя Kotlin

Если вы используете компонент навигации, следуйте приведенным ниже кодам в методе onCreateView () (в этом примере я хочу просто закрыть свое приложение этим фрагментом)

 OnBackPressedCallback backPressedCallback = new OnBackPressedCallback(true) {
        @Override
        public void handleOnBackPressed() {
            new AlertDialog.Builder(Objects.requireNonNull(getActivity()))
                    .setIcon(R.drawable.icon_01)
                    .setTitle(getResources().getString(R.string.close_app_title))
                    .setMessage(getResources().getString(R.string.close_app_message))
                    .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            getActivity().finish();
                        }
                    })
                    .setNegativeButton(R.string.no, null)
                    .show();
        }
    };
    requireActivity().getOnBackPressedDispatcher().addCallback(this, backPressedCallback);

вы можете предоставить свою настраиваемую обратную навигацию с помощью OnBackPressedDispatcher

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
// and if you want to need navigate up
//NavHostFragment.findNavController(this).navigateUp()
    }

    // The callback can be enabled or disabled here or in the lambda
}
}

Дополнительные объяснения в официальном руководстве по Android: https://developer.android.com/guide/navigation/navigation-custom-back

И если вы хотите, чтобы такое же поведение было и для кнопки возврата на панели инструментов, просто добавьте это в свою активность:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == android.R.id.home) {
        getOnBackPressedDispatcher().onBackPressed();
        return true;
    }
    return super.onOptionsItemSelected(item);
}

Мое мнение requireActivity (). onBackPressed ()

requireActivity().onBackPressed()

Просто добавьте эти строки

     override fun onBackPressed() {
            if (navController.popBackStack().not()) {
            //Last fragment: Do your operation here 
            finish()
   } 

navController.popBackStack () просто вытолкнет ваш фрагмент, если это не последний фрагмент

Используйте это, если вы используете фрагмент или добавляете его в прослушиватель нажатия кнопки. У меня это работает.

requireActivity().onBackPressed()

Вызывается, когда активность обнаруживает нажатие пользователем клавиши возврата. GetOnBackPressedDispatcher () OnBackPressedDispatcher} получит возможность обработать кнопку «Назад» до того, как будет вызвано поведение по умолчанию android.app.Activity # onBackPressed ()}.

Я просмотрел много тем, но ни один из них не работал. Наконец я нашел один:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar mToolbar = findViewById(R.id.topAppBar);
    setSupportActionBar(mToolbar);
}

@Override
public boolean onSupportNavigateUp() {
    navController.navigateUp();
    return super.onSupportNavigateUp();
}

MyFragment.java

@Override
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
    Toolbar mToolbar = (MainActivity) getActivity().findViewById(R.id.topAppBar);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do something when uses presses back button (showing modals, messages,...)
            // Note that this will override behaviour of back button
        }
    });
}

@Override
public void onStop() {
    // Reset back button to default behaviour when we leave this fragment
    Toolbar mToolbar = (MainActivity) getActivity().findViewById(R.id.topAppBar);
    mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mainActivity.onBackPressed();
        }
    });

    super.onStop();
}

просто создайте функцию расширения для фрагмента

fun Fragment.onBackPressedAction(action: () -> Boolean) {
    requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object :
        OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            this.isEnabled = action()
            if (!this.isEnabled) {
                requireActivity().onBackPressed()
            }
        }
    })
}

и после того, как во фрагменте поместите код в onCreateView (действие должно возвращать false, чтобы вызвать действие onBackPressed)

onBackPressedAction { //do something }

Мне нужно поддерживать как настоящую кнопку «Назад», так и кнопку «Назад» на панели инструментов с возможностью переопределения щелчка «Назад» в обоих случаях (для отображения диалогового окна или чего-то еще). Я сделал дополнительный метод в Activity и соответствующие логические проверки (в моем случае 'onBackPressed') по фрагментам:

// Process hardware Back button
override fun onBackPressed() {
    if (canCloseActivity()) {
        super.onBackPressed()
    }
}

// Process toobar Back and Menu button
override fun onSupportNavigateUp(): Boolean {
    if (canCloseActivity()) {
        return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
    }
    return false
}

// Do real check if has unfinished tasks, return false to override activity closing
private fun canCloseActivity(): Boolean {
    val currentFragment = navHostFragment.childFragmentManager.primaryNavigationFragment

    return when {
        currentFragment is MyFragment && currentFragment.onBackPressed() -> false

        drawerLayout.isOpen -> {
            drawerLayout.close()
            false
        }
        fullScreenPreviewLayout.visibility == View.VISIBLE -> {
            closeFullscreenPreview()
            false
        }
        else -> true
    }
}

Просто в методе onCreate() вашего Fragment используйте этот код после super.onCreate(savedInstanceState):

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

Поскольку жизненный цикл Fragment немного отличается от жизненного цикла View, я думаю, что лучше вместо этого передать viewLifecycleOwner. Как это: requreActivity().onBackPressedDispatcher.addCallback(viewLif‌​ecycleOwner)

Onur D. 22.04.2021 23:14

Спасибо, @OnurD. Я отредактировал свой фрагмент.

Mohsin H 24.04.2021 22:25

если вы действительно пытаетесь обработать кнопку «Назад», вы можете использовать ответ @Jurij Pitulja.

Но если вы хотите вывести ВторойФрагмент (начальный фрагмент Первый фрагмент) и не возвращаться к Первый фрагмент, вы можете использовать:

Navigation.findNavController(view).popBackStack()

из каталога ВторойФрагмент. Таким образом вы вытолкните Второй заднего стека и не вернетесь к ВторойФрагмент, когда вы нажмете кнопку возврата из Первый фрагмент.

Использование компонентов навигации Для меня это было хорошо:

Navigation.findNavController(requireView()).popBackStack()

документация по android

Другие вопросы по теме