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

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

введите описание изображения здесь

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


person Kiryl Tkach    schedule 26.06.2018    source источник
comment
С помощью Pop to вы можете определить, куда идти (пункт назначения) при нажатии кнопки «Назад / вверх».   -  person Alex    schedule 26.06.2018
comment
@Alex Итак, если для него установлено значение none, как он должен реагировать на кнопку "Назад"?   -  person Kiryl Tkach    schedule 26.06.2018
comment
Если для него установлено значение none, поведение по умолчанию, пользователь будет перемещен к предыдущему месту назначения (фрагмент 1).   -  person Alex    schedule 26.06.2018
comment
@Alex, хорошо, есть ли способ обработать кнопку возврата по второму фрагменту?   -  person Kiryl Tkach    schedule 26.06.2018
comment
stackoverflow.com/a/51044879/1268507   -  person Alex    schedule 26.06.2018


Ответы (28)


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

Новая версия androidx.activity ver. 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 г.):

Поскольку this, его можно реализовать, просто используя JetPack OnBackPressedCallback в вашем фрагменте и добавьте его в действие: 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}'
person Jurij Pitulja    schedule 04.02.2019
comment
Это все еще существует в Jetpack? - person Bryan Bryce; 05.02.2019
comment
Должен ли он быть, если я использую AppCompatActivity? К какому классу он принадлежит? - person Bryan Bryce; 05.02.2019
comment
Если ваша активность AppCompat не из библиотеки поддержки, а из библиотеки androidX, тогда да, вам следует - person Jurij Pitulja; 05.02.2019
comment
Использование AndroidX AppCompatActivity и Androidx Fragment, и я тоже не считаю это доступным вариантом - person Omega142; 07.02.2019
comment
Должно. Проверьте версию или попробуйте очистить и пересобрать проект. - person Jurij Pitulja; 07.02.2019
comment
Omega142 Я считаю, что вам следует перейти от ComponentActivity, чтобы иметь возможность переопределить этот метод - person Onur D.; 07.02.2019
comment
ComponentActivity, новый базовый класс существующих FragmentActivity и AppCompatActivity - person filthy_wizard; 25.02.2019
comment
Обязательно добавьте 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. - person wooldridgetm; 28.02.2019
comment
Я знаю, что это старый поток, но я надеюсь, что вы его видите, у меня есть этот handleBackClick, он отлично работает, проходит через все фрагменты, которые он настроил, но он все еще активирует MainActivity onBackPressed (). Я думал, если один из фрагментов поглотил его, он на этом закончился? - person Dave; 09.03.2019
comment
Думайте, что ваше возвращаемое значение от handleOnBackPressed ложно, и это единственная причина, по которой он вызывает активность onBackPressed. Измените его на true. - person Jurij Pitulja; 09.03.2019
comment
addOnBackPressedCallback - это метод androidx.activity.ComponentActivity, который расширяет androidx.core.app.ComponentActivity. Это означает, что если вы используете, например, androidx.fragment.app.FragmentActivity, который также расширяет androidx.core.app.ComponentActivity, у вас addOnBackPressedCallback недоступен. - person Nantoka; 25.03.2019
comment
да. Вам следует использовать AppCompatActivity. Также в будущих выпусках androidX он может быть отделен от активности. - person Jurij Pitulja; 25.03.2019
comment
@JurijPitulja androidx.appcompat.app.AppCompatActivity расширяет androidx.fragment.app.FragmentActivity, поэтому у вас также нет метода addOnBackPressedCallback в AppCompatActivity, по крайней мере, с текущей стабильной версией 1.0.2 библиотеки совместимости androidx. - person Nantoka; 25.03.2019
comment
Да ты прав. В стабильной версии такого метода нет. Я использую последнюю альфу. Первая ссылка в посте показывает, когда и в какой версии она доступна. - person Jurij Pitulja; 26.03.2019
comment
Я думаю, что с 16 апреля вам понадобится еще одно обновление, потому что кажется, что OnBackPressedCallback стал абстрактным классом, и нет множественного наследования, поэтому сценарий с BaseFragment, передающий это как параметр для addCalback (), падает в воду. - person Brcinho; 26.04.2019
comment
Это ХОРОШО РАБОТАЕТ. Я рекомендую вам использовать первое - обновленное решение. - person Mitch; 30.04.2019
comment
Где я могу сообщить об ошибке? Если активность прекращается системой. Тогда последним обратным вызовом будет не тот, который вы добавили в метод onCreate (), а тот, который был получен из NavHostFragment. - person Kęstas Venslauskas; 20.05.2019
comment
OnBackPressedCallback теперь является абстрактным классом! Так что это решение не работает для меня. - person Junia Montana; 13.09.2019
comment
@JuniaMontana, чтобы вы могли создать частное поле mOnBackPressedCallback и передать его вместо этого. - person Kiryl Tkach; 13.09.2019
comment
Мне было интересно, почему это решение не работает для меня. Позже я обнаружил, что переопределил onBackPressed (), но не вызвал super.onBackPressed () из переопределенного метода. Все обратные вызовы, зарегистрированные через addCallback, оцениваются, когда вы вызываете super.onBackPressed (). onBackPressed вызывается всегда, независимо от зарегистрированных экземпляров OnBackPressedCallback. - person Anik Dey; 21.10.2019
comment
Как выполнить нормальную операцию возврата после выполнения некоторой логики на - handleIOnBackPressed ()? - person krishnakumarcn; 01.11.2019
comment
Для меня это не вызывает, если нажата кнопка «Назад» на панели инструментов. - person nullmn; 18.02.2020
comment
Это зависит от того, какую панель инструментов вы используете. Есть разные случаи - person Jurij Pitulja; 18.02.2020
comment
в вашем новейшем решении, если я хочу иметь условие в моем обратном вызове, и если условие имеет значение true, сделайте что-нибудь, иначе верните управление действием, как мне это сделать? Пример: OnBackPressedCallback callback = new OnBackPressedCallback (true) {@Override public void handleOnBackPressed () {if (условие) {// сделать что-то} else {// activity.onSupportNavigationUp ()}}}; - person roghayeh hosseini; 02.04.2020
comment
Я бы хотел, чтобы они еще больше упростили это. Написание прослушивателя обратного нажатия для глобальной работы с компонентом навигации в основном не идеален - может иметь смысл иметь свойство API в типе ‹activity› ‹fragment› навигационного графа, чтобы можно было указать, нужен ли вам пользовательский обработчик для навигации внутри этого класса или null для отключения обратного нажатия. - person Saifur Rahman Mohsin; 11.08.2020
comment
В последней версии android 11 это вызывает ошибку, если мы вызываем ее в onCreate `java.lang.IllegalStateException: не удается получить доступ к LifecycleOwner представления фрагментов, когда getView () имеет значение null, то есть до onCreateView () или после onDestroyView ()` - person androidtitan; 16.12.2020
comment
Необязательно вызывать removeOnBackPressedCallback из onDestroyView, поскольку действие автоматически удаляет обратные вызовы в зависимости от продолжительности жизни viewLifeCycleOwner, переданного в вызов addOnBackPressedCallback. - person Monte Creasor; 12.01.2021

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

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(), в другом месте я просто возвращаю стек и закрываю приложение, если задний стек пуст.

person Kiryl Tkach    schedule 25.10.2018
comment
Приличное решение. Хотя мне всегда интересно, в чем смысл Navigation Component, если у него нет такой функциональности из коробки. - person First_Strike; 24.12.2018
comment
Проверьте ответ @Jurij Pitulja - рекомендуемый способ решения проблемы - person Mitch; 30.04.2019
comment
Зачем нужен отдельный интерфейс вместо того, что есть в Jetpack? - person IgorGanapolsky; 19.03.2020
comment
@IgorGanapolsky на момент ответа в Jetpack не было интерфейса. - person Kiryl Tkach; 19.03.2020
comment
@KirylTkach, что такое объявление mNavHostFragment? - person E.Akio; 03.05.2020
comment
@ E.Akio это ваш navHostFragment в вашем макете developer.android.com/reference / androidx / navigation / fragment / - person Kiryl Tkach; 03.05.2020
comment
@KirylTkach Конечно, я это знаю, но каков код для его реализации? - person E.Akio; 04.05.2020

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

requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { 
    // handle back event
}
person Community    schedule 29.05.2019
comment
лучше, если владелец обратного вызова также передается так .addCallback(viewLifecycleOwner) {}, иначе вы продолжите получать обратные вызовы даже после уничтожения фрагмента. - person Marat; 06.11.2019

Для тех, кто ищет реализацию 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

person Abtin Gramian    schedule 27.07.2019
comment
Это лучший ответ. Вы должны уметь обрабатывать обратное нажатие с панели инструментов и обычное обратное нажатие. Пальцы вверх - person odifek; 14.01.2020
comment
Это мне очень помогло! Моя кнопка возврата на панели инструментов не срабатывала OnBackPressedCallback, но после добавления toolbar.setNavigationOnClickListener { onBackPressed() } она работала, так что теперь аппаратное обеспечение и панель инструментов работают одинаково. Спасибо за четкий развернутый ответ! - person poby; 03.05.2020
comment
Это правильное и наиболее логичное решение. Обязательно выдержит испытание временем. - person Otieno Rowland; 21.06.2020
comment
Я использовал только код для обратного переопределения фрагмента, он отлично работает! - person sud007; 29.11.2020

Вот решение, которое должно делать то, что вы хотите, но я думаю, что это плохое решение, потому что оно противоречит идее компонента 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()
        }
    }
} 
person Alex    schedule 26.06.2018
comment
Спасибо за ваше решение, но я использовал его, прежде чем решил переключиться на компонент навигации. Вроде еще рано :( - person Kiryl Tkach; 26.06.2018
comment
это слишком рано. Я реализовал аналогичное хакерское решение для управления навигацией по панели приложений - person filthy_wizard; 25.02.2019

Я написал в основном упражнении вот так,

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

In 2.1.0-alpha06

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

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

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

requireActivity().onBackPressedDispatcher.addCallback() {
    // handle back event
}
person solaza    schedule 16.07.2019

Это 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();
     }
person Biplob Das    schedule 06.10.2019

Обновление 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()
    }

 }

Исходный ответ 3 января 2019 г.

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

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

person Onur D.    schedule 03.01.2019
comment
Спасибо, что показали мне примечания к выпуску! Выяснилось, что android: menuCategory = secondary избегает выталкивания заднего стека! - person Valentin Gavran; 06.01.2019
comment
для меня это работает только с панелью инструментов, но не с кнопкой "Назад" - person Jurij Pitulja; 07.02.2019
comment
То же самое ^ работает для панели инструментов, как обрабатывать кнопку назад? - person Gauri Gadkari; 21.04.2021
comment
@GauriGadkari Спасибо за отзыв! К сожалению, вы правы, мой первоначальный ответ не относится к системному нажатию на заднюю часть. Поэтому я обновил свой ответ примером использования рекомендованного подхода. См. Также принятый ответ. - person Onur D.; 23.04.2021

Рекомендуемый метод работал у меня, но после обновления моей библиотеки 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

person Gstuntz    schedule 12.10.2019

вы можете предоставить свою настраиваемую обратную навигацию с помощью 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

person MohamedHarmoush    schedule 22.04.2020

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

requireActivity().onBackPressed()

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

person Cyd    schedule 11.08.2020

Если вы используете компонент навигации, следуйте приведенным ниже кодам в методе 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);
person Ahmet    schedule 25.03.2020

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

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

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

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

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

person Atul Bhardwaj    schedule 23.07.2020

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

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

Navigation.findNavController(view).popBackStack()

из SecondFragment. Таким образом вы вытолкните SecondFragmetn заднего стека и не вернетесь к SecondFragment при нажатии кнопки возврата из FirstFragment.

person QuartZ    schedule 14.04.2021

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

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

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

person Ylan    schedule 28.06.2021
comment
Это не позволяет настраивать реализацию. - person Taslim Oseni; 11.07.2021

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

Используйте 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
}

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

person Nantoka    schedule 25.03.2019

Я попробовал решение 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()
    }
}
person Amin Keshavarzian    schedule 15.05.2019

Если вы используете 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.
}

}

person Happy Singh    schedule 22.08.2019

В зависимости от вашей логики, если вы хотите закрыть только текущий фрагмент, вам нужно передать 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
        }
    })
person Junia Montana    schedule 13.09.2019

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

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()
    }
person Anandharaj R    schedule 22.12.2018

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

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();
}
person Duc Trung Mai    schedule 17.09.2020

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

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 }
person Fabrice P.    schedule 03.11.2020

Мне нужно поддерживать как настоящую кнопку «Назад», так и кнопку «Назад» на панели инструментов с возможностью переопределения «Назад» в обоих случаях (для отображения диалога или чего-то еще). Я сделал дополнительный метод в 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
    }
}
person Roman    schedule 25.12.2020

Просто в 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
}
person Mohsin H    schedule 10.04.2021
comment
Поскольку жизненный цикл Fragment немного отличается от жизненного цикла View, я думаю, что лучше вместо этого передать viewLifecycleOwner. Как это: requreActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) - person Onur D.; 23.04.2021
comment
Спасибо, @OnurD. Я отредактировал свой фрагмент. - person Mohsin H; 24.04.2021

ktx его версия:

fun Fragment.handleBackButtonEvent(
    onBackPressed: OnBackPressedCallback.() -> Unit
) {
    requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
        onBackPressed()
    }
}

вы можете просто использовать его в Fragmnet.

person Mohsents    schedule 10.07.2021

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

requireActivity().onBackPressed()
person nirazverma    schedule 17.06.2020