Navigation Controllerを使用してシステムの戻るボタン操作を適切に処理する方法を知りたい。私のアプリには2つのフラグメント(fragment1とfragment2など)があり、fragment1にアクションがあり、fragment2への宛先があります。 1つを除いてすべてがうまくいきます-ユーザーがfragment2でシステムの戻るボタンを押すと、ダイアログを表示して(たとえばDialogFragmentを使用して)終了を確認します。この動作を実装する最良の方法は何ですか?ホストフラグメントでapp:defaultNavHost="true"
を使用すると、ルールを無視して自動的に戻ります。さらに、このコンポーネントは何のためにありますか?
「ポップする」を使用する必要がありますか?
2019年4月25日最新アップデート
新しいリリース 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:2019年4月3日
今では簡素化されています。詳細 こちら
例:
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 2019年4月3日以降):
this であるため、 JetPack implementation 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}'
そこで、インターフェイスを作成しました
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()
メソッドを呼び出します。他の場所では、単にスタックをポップバックし、バックスタックが空の場合はアプリケーションを閉じます。
ここにあなたが望むものを行うべき解決策がありますが、Androidナビゲーションコンポーネントのアイデア(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()
}
}
}
推奨 アプローチは、アクティビティのOnBackPressedCallback
にOnBackPressedDispatcher
を追加することです。
requireActivity().onBackPressedDispatcher.addCallback {
// handle back event
}
パーティーに少し遅れましたが、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
2.1.0-alpha06
現在のフラグメントでのみバックプレスを処理する場合
requireActivity().onBackPressedDispatcher.addCallback(this@LoginFragment) {
// handle back event
}
アクティビティ全体
requireActivity().onBackPressedDispatcher.addCallback() {
// handle back event
}
ロジックに応じて、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
}
})
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 で見ることができます
これを試して。これはあなたの役に立つと思います。
オーバーライドfun BackBackeded(){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() }
アプリに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() {
}
}
Basefragmentを拡張して、フラグメントのonBackPressed()をオーバーライドします
//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.
}
}
ここに私の解決策があります
NavHostFragment
フラグメントを含むアクティビティにandroidx.appcompat.app.AppCompatActivity
を使用します。
次のインターフェイスを定義し、すべてのナビゲーション宛先フラグメントに実装します
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ソリューションを試しましたが、Kiryl Tkachのソリューションを使用しても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()
}
}
私はこのように主な活動を書きました
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.my_nav_Host_fragment).navigateUp(appBarConfiguration)
}