Как обрабатывать кнопку "Назад", когда она находится в начальном пункте назначения компонента навигации

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

Это код, который я использую сейчас:

findNavController(this, R.id.my_nav_host_fragment)
                .navigateUp()

Когда я где-нибудь на своем графике, он отлично работает, он отправляет меня обратно, но когда я в самом начале - приложение вылетает, поскольку бэкстек пуст.

Это все имеет смысл для меня, я просто не уверен, как справиться с этим.

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

Идеи?

5 ответов

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

fun NavController.navigateUpOrFinish(activity: AppCompatActivity): Boolean {
return if (navigateUp()) {
    true
} else {
    activity.finish()
    true
}

}

И затем назовите это как:

override fun onSupportNavigateUp() = 
        findNavController(R.id.nav_fragment).navigateUpOrFinish(this)

Однако я не смог использовать NavigationUI, так как это скрывало стрелку назад, когда я был в пункте назначения. Так что вместо:

NavigationUI.setupActionBarWithNavController(this, controller)

Я вручную управлял значком дома:

setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_navigation_back)

Override onBackPressed в своей деятельности и проверьте, является ли текущий пункт назначения начальным или нет.

Практически это выглядит так:

@Override
public void onBackPressed() {
    if (Navigation.findNavController(this,R.id.nav_host_fragment)
            .getCurrentDestination().getId() == R.id.your_start_destination) {
        // handle back button the way you want here
        return;
    }
    super.onBackPressed();
}

Вы не должны переопределять "onBackPressed", вы должны переопределить "onSupportNavigateUp" и поместить туда

findNavController(this, R.id.my_nav_host_fragment)
            .navigateUp()

Из официальной документации:вы также перезапишите AppCompatActivity.onSupportNavigateUp() и вызовете NavController.navigateUp

https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing

В Jetpack Navigation Componenet, если вы хотите выполнить какую-либо операцию, когда фрагмент выталкивается, вам необходимо переопределить следующие функции.

  1. Добавьте OnBackPressedCallback во фрагмент, чтобы запускать специальную операцию при нажатии кнопки "Назад" в панели навигации системы внизу.

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
    
         onBackPressedCallback = object : OnBackPressedCallback(true) {
             override fun handleOnBackPressed() {
                 //perform your operation and call navigateUp
                findNavController().navigateUp()
             }
         }
     requireActivity().onBackPressedDispatcher.addCallback(onBackPressedCallback)
     }
    
  2. Добавьте onOptionsItemMenu во фрагмент, чтобы обработать нажатие стрелки назад в верхнем левом углу приложения.

     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
    
         setHasOptionsMenu(true)
     }
    
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
       if (item.itemId == android.R.id.home) {
           //perform your operation and call navigateUp
           findNavController().navigateUp()
           return true
       }
       return super.onOptionsItemSelected(item)
    }
    
  3. Если нет специального кода для запуска при нажатии back на фрагменте хоста, используйте onSupportNavigateUp в Activity.

     override fun onSupportNavigateUp(): Boolean {
       if (navController.navigateUp() == false){
         //navigateUp() returns false if there are no more fragments to pop
         onBackPressed()
       }
       return navController.navigateUp()
     }
    

Обратите внимание, что onSupportNavigateUp() не вызывается, если фрагмент содержит onOptionsItemSelected()

Так как моя кнопка "Назад" работает правильно, и при использовании NavController.navigateUp() произошел сбой при запуске кнопки "Назад". Я изменил этот код на что-то вроде этого. Другой возможностью будет просто проверить, является ли currentDestination == startDestination.id, но я хочу закрыть Activity и вернуться к другой Activity.

override fun onSupportNavigateUp() : Boolean {
        //return findNavController(R.id.wizard_nav_host_fragment).navigateUp()
        onBackPressed()
        return true
    }
/** in your activity **/
private boolean doubleBackToExitPressedOnce = false;
        @RequiresApi(api = Build.VERSION_CODES.M)
        @Override
        public void onBackPressed() {
            int start = Navigation.findNavController(this, R.id.nav_host_fragment).getCurrentDestination().getId();
            if (start == R.id.nav_home) {
                if (doubleBackToExitPressedOnce) {
                    super.onBackPressed();
                    return;
                }
                this.doubleBackToExitPressedOnce = true;
                Toast.makeText(MainActivity.this, "Press back again to exits", Toast.LENGTH_SHORT).show();

                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        doubleBackToExitPressedOnce = false;
                    }
                }, 2000);
            } else {
                super.onBackPressed();
            }
        }

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

Just call this in your back button Onclick

 requireActivity().finish()