【问题标题】:How to add logging to navigation back or up using navigation architecture component?如何使用导航架构组件将日志记录添加到导航返回或向上?
【发布时间】:2020-08-30 13:19:47
【问题描述】:

我在我的 Android 应用程序中使用导航组件,它会自动为我提供后退和向上导航。现在我不想更改任何这些行为,但我想添加一些特定于用户按下工具栏中的向上按钮或后退按钮的片段的日志记录。

我试过this,它只对后退按钮有效,我不知道如何保持默认导航行为不变。另外,这似乎在活动级别添加了一个回调,因此它并不特定于我添加回调的片段。

似乎 onOptionsItemSelected 是为普通菜单项调用的,但不是为向上按钮调用的。

如何在不改变整个应用程序行为的情况下始终如一地处理这个问题?

【问题讨论】:

  • 请检查我的回答是否符合您的预期。
  • 您只想检查后退或向上按钮的点击,还是可以更通用地检测后退堆栈上的弹出?
  • 您是要记录一个特定片段或图表中所有目的地的向上和返回按钮吗?您到底在记录什么,为什么只记录您想要记录的内容(相对于所有导航事件)?您需要区分 back 和 up 吗?
  • 我想记录用户尝试打开新全屏片段的操作,然后通过弹出堆栈取消,他可以通过点击后退按钮或向上箭头来完成由导航组件管理。我不在乎它是恢复还是启动,这是我感兴趣的流行音乐,但我确实关心这个流行音乐在导航图中发生的位置,因为我想记录它发生的位置。

标签: android kotlin navigation


【解决方案1】:

您非常接近答案。
试试这个。

注意:此代码是从我的应用程序中复制的。根据您的要求进行更改。

活动中:

onCreate():

// Observe action state live data
activityViewModel.actionStateMutableLiveData.observe(this, Observer { actionState ->
    actionState?.let {
        if (actionState != "NO_ACTION") {
            when (actionState) {
                "NAVIGATE_UP" -> {
                    if (!navController.navigateUp()) {
                        finish()
                    }
                }
            }
            // Reset action state
            activityViewModel.setActionState("NO_ACTION")
        }
    }
})

还有,

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return when (item.itemId) {
        android.R.id.home -> {
            navController.currentDestination?.id?.let { currentDestinationId ->
                return when (currentDestinationId) {
                    R.id.fragmentToLog -> {
                        false
                    }
                    else -> {
                        activityViewModel.setActionState("NAVIGATE_UP")
                        true
                    }
                }
            }
            activityViewModel.setActionState("NAVIGATE_UP")
            return true
        }
        else -> {
                item.onNavDestinationSelected(findNavController(R.id.fragment_activitymain))
                        || super.onOptionsItemSelected(item)
        }
    }
}

片段:

onViewCreated():

requireActivity().onBackPressedDispatcher.addCallback(this) {
    handleNavigateBack()
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.itemId) {
        android.R.id.home -> {
            handleNavigateBack()
            return true
        }
    }
    return super.onOptionsItemSelected(item)
}

private fun handleNavigateBack() {
    // TODO: Add your fragment logs here
    activityViewModel.setActionState("NAVIGATE_UP")
}

活动视图模型:

// Action state
var actionStateMutableLiveData = MutableLiveData(NO_ACTION)
    private set

fun setActionState(actionStateValue: String?) =
    actionStateMutableLiveData.postValue(actionStateValue)

使用的概念:

  • MVVM 架构
  • 实时数据和查看模型
  • Android 架构导航组件

如果有什么不清楚的地方请评论。

【讨论】:

  • 所有这些,只是为了检测返回导航?!我不在我的活动中使用视图模型,我不想为此设置所有这些。一定有更简单的方法!
  • @Sebastien 正如我所提到的,这是我在我的应用程序中拥有的东西,因为我需要更多的控制权。您可以了解它的工作原理并根据您的要求进行更改。
【解决方案2】:

在您的活动中,您应该首先通过以下代码设置 NavigationUI

val navController = this.findNavController(R.id.nav_app_id) 
NavigationUI.setupActionBarWithNavController(this,navController)

在 Fragment 中,您可以检测到用户何时单击onOptionsItemSelected 中的工具栏,然后调用findNavController().navigateUp()

【讨论】:

  • 我尝试添加这些行,即使我已经在活动中调用了toolbar?.setupWithNavController(navController, appBarConfig),但是当我点击向上按钮时,我从未在片段中的 onOptionsItemSelected 中点击断点,只有当我点击其他菜单时项目。
【解决方案3】:

一个可能的解决方案涉及三个步骤:定义一个接口,让所需的片段实现该接口并覆盖您的托管活动中的 onSupportNavigateUp / onNavigateUp / onBackPressed 方法。

首先,定义一个接口,例如:

interface CustomNavAction {
   fun logSomeStuff()
}

其次,将接口添加到所需的Fragment中,例如:

class AFragment : Fragment(), CustomNavAction {
  ...
  override fun logSomeStuff() {
    Log.d("TAG", "Up or back")
  }
  ...
}

最后,重写承载导航组件的Activity中的onSupportNavigateUp/onNavigateUp/onBackPressed方法,找到当前fragment,检查是否实现了接口。如果是这样,您可以调用该操作。例如:

override fun onSupportNavigateUp(): Boolean {
    val navController = findNavController(R.id.nav_host_fragment)

    // find current fragment, and invoke custom action if up button is pressed
    val navHostFragment = supportFragmentManager.fragments[0]
    val currentFragment = navHostFragment.childFragmentManager.fragments[0]
    if (currentFragment is CustomNavAction) {
        (currentFragment as CustomNavAction).logSomeStuff()
    }

    return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

override fun onBackPressed() {
    super.onBackPressed()
    ... 
    // similar logic as onSupportNavigateUp
}

这样,你就不会弄乱整体导航,只有实现接口的片段才会记录一些东西。

【讨论】:

  • 我在宿主活动中添加了 onBackPressed、onNavigateUp 和 onSupportNavigateUp 的实现,并在那里设置了断点。当我按下后退按钮时会调用 onBackPressed,但是当我按下工具栏中的箭头时不会调用 onNavigateUp 和 onSupportNavigateUp。
  • @Sebastien 我已经快速破解了解决方案并将其放在 github - github.com/stefanthaler/stackoverflow_questions_63517369 我的两个猜测为什么在您的情况下没有调用 onNavigateUp/onSupportNavigateUp 是:未设置 defaultNavHost 属性/导航主机未正确注册/jetpack(支持)库的过时版本
【解决方案4】:

此解决方案并非特定于片段,但通过这种方式,您可以用更少的代码检测到后按和向上导航

在Fragment中,onCreate添加这个

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // enable the callback, when we receive a back press event disable the callback and dispatch the event to the activity
        requireActivity().onBackPressedDispatcher.addCallback(
            this,
            object : OnBackPressedCallback(true) {
                override fun handleOnBackPressed() {
                    Log.d(
                        "SomeTag",
                        "User pressed up back button to navigate back from fragment."
                    )
                    isEnabled = false
                    requireActivity().onBackPressed()
                }
            })
    }

现在,在您的活动中,您需要覆盖默认的ToolBar#onNavigationOnClickListener()

toolbar.setNavigationOnClickListener {
            val currentFragment = navHostFragment.childFragmentManager.primaryNavigationFragment
            Log.d(
                "SomeTag",
                "User pressed up arrow to navigate back from fragment: $currentFragment"
            )
            navController.navigateUp(appBarConfiguration)
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-12-22
    • 2020-08-07
    • 2019-09-23
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    相关资源
    最近更新 更多