Обработка кнопки возврата в компоненте навигации Android - PullRequest
0 голосов
/ 26 июня 2018

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

enter image description here

Должен ли я использовать "поп к" может быть?

Ответы [ 10 ]

0 голосов
/ 27 июля 2019

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

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

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

0 голосов
/ 29 мая 2019

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

действия.
requireActivity().onBackPressedDispatcher.addCallback { 
    // handle back event
}
0 голосов
/ 16 июля 2019

В 2.1.0-alpha06

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

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

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

requireActivity().onBackPressedDispatcher.addCallback() {
    // handle back event
}
0 голосов
/ 25 марта 2019

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

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

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

0 голосов
/ 15 мая 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()
    }
}
0 голосов
/ 04 февраля 2019

Самое новое ОБНОВЛЕНИЕ 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 boolean 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 г.):

Поскольку это , его можно реализовать, используя 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}'
0 голосов
/ 03 января 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

0 голосов
/ 22 декабря 2018

Попробуй это. Я думаю, что это поможет вам.

переопределить удовольствие onBackPressed () { когда (mNavController.getCurrentDestination () !!. getId ()) {

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

}

личное развлечение onWarningAlertDialog (mainActivity: MainActivity, s: Строка, s1: Строка) {

    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()
}
0 голосов
/ 25 октября 2018

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

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();

}

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

0 голосов
/ 26 июня 2018

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

Переопределить «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()
        }
    }
} 
...