首页 > 解决方案 > 在片段的 OnViewCreated 中启动时,ActionMode 回调永远不会调用 onCreateActionMode

问题描述

@ExperimentalCoroutinesApi
class WeekdayTimesFragment @Inject constructor(viewModelFactory: SavedStateViewModelFactory.Factory)
    :Fragment(R.layout.fragment_weekday_times), WeekdayAlarmTimesAdapter.OnWeekdayAlarmTimePressed, ActionModeListener {

    private lateinit var weekdayAlarmTimesAdapter: WeekdayAlarmTimesAdapter
    private val weekdayTimesViewModel
            by viewModels<WeekdayTimesViewModel> { viewModelFactory.create(this) }
    override val actionCallback = PrimaryActionCallback(this)
    private var weekday: Int = 0
    private val isActionModeActive: Boolean
        get() = weekdayTimesViewModel.isActionModeActive
    private val numSelected: Int
        get() = weekdayTimesViewModel.selectedTimePositions.size

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        weekday = requireArguments().get("WEEKDAY") as Int
        if(isActionModeActive) { //action mode may be active prior to rotation
            startActionMode() //for some reason, starting action mode here does not work....
        }
        initRecyclerView() //will determine what recyclerview items are selected via the viewmodel
        weekdayTimesViewModel.getTimesAndAlarmsForSelectedWeekday(Weekday[weekday])
            .observe(viewLifecycleOwner) { times->
                weekdayAlarmTimesAdapter.submitWeekdayTimes(times)
            }
    }

    override fun alarmTimePressed(wasSelected: Boolean, position: Int, time: WeekdayTime) {
        if(wasSelected) { //this item was selected prior to being selected, effectively unselecting it
            weekdayTimesViewModel.selectedTimePositions.remove(position)
        } else {
            weekdayTimesViewModel.selectedTimePositions[position] =
                Time(time.hour, time.minute, Weekday[weekday], time.alarm.alarmId, time.isDisabled, time.id)
        }

        if(numSelected == 0 && isActionModeActive) { //no more times are selected, turn off action mode
            destroyActionMode() //need to de-select recyclerview items
        } else {
            if (!isActionModeActive) {
                startActionMode()
            } else {
                actionCallback.changeTitle("$numSelected selected")
            }
        }
    }

    private fun startActionMode() {
        actionCallback
            .startActionMode(
                requireView(),
                R.menu.toolbar_contextual_menu,
                "$numSelected selected")
        //fragment hosting the viewpager
        (requireParentFragment() as ActionModeListenerController).currentActionModeListener = this
        weekdayTimesViewModel.isActionModeActive = true
    }

    override fun onActionItemClick(item: MenuItem?) {
        item?.let {
            when(it.itemId){
                R.id.toolbar_disable_alarm-> {
                    requireContext().createGenericAlertDialog(
                        message = if(numSelected == 1) "Are you sure you want to disable this alarm?"
                        else "Are you sure you want to disable these alarms?",
                        positiveListener = { _, _ ->
                            weekdayTimesViewModel.disableSelectedTimes()
                            showUndoSnackbar(true)
                        }
                    ).show()
                }
                R.id.toolbar_delete_alarms-> {
                    requireContext().createGenericAlertDialog(
                        message = if(numSelected == 1) "Are you sure you want to delete this alarm?"
                        else "Are you sure you want to delete these alarms?",
                        positiveListener = { _, _ ->
                            weekdayTimesViewModel.deleteSelectedTimes()
                            showUndoSnackbar(false)
                        }
                    ).show()
                }
            }
        }
    }

    //when we aren't destroying the view but need to end action mode (changing tab or after we confirm an action)
    //we may want to do specific things specific to certain listeners, e.g. de-select certain recyclerview items.
    override fun destroyActionMode() {
        actionCallback.finishActionMode()
        weekdayTimesViewModel.isActionModeActive = false
        //also un-select all of the elements that have been selected
        weekdayTimesViewModel.selectedTimePositions.clear()
        //redraw recyclerview as well
        rv_weekday_times.resetAdapterState()
    }

    private fun showUndoSnackbar(disablingTimes: Boolean){
        val message =
            if(disablingTimes) "Your alarm times have been disabled." else "Your alarm times have been deleted."
        Snackbar.make(requireView(), message, Snackbar.LENGTH_LONG).setAction("UNDO"){
            if(disablingTimes)
                weekdayTimesViewModel.restoreDisabledTimes()
            else
                weekdayTimesViewModel.restoreDeletedTimes()
        }.setActionTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary))
        .setAnchorView(requireActivity().bottom_nav_view).show()
    }

    //when rotating screen or navigating we can just end action mode. The state of whether we are
    //in action mode either doesn't matter (navigation) or is already persisted, along with the selected
    //items in the viewmodel(rotation)
    override fun onDestroyView() {
        actionCallback.finishActionMode()
        super.onDestroyView()
    }

    private fun initRecyclerView() {
        weekdayAlarmTimesAdapter = WeekdayAlarmTimesAdapter(this, weekdayTimesViewModel.selectedTimePositions.keys)
        rv_weekday_times.apply {
            adapter = weekdayAlarmTimesAdapter
            layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
        }
        weekdayAlarmTimesAdapter.selectedItems.clear()
        //clears selected items as these items no longer remain consistently for the lifecycle of the view
    }
}

因此,当我通过 recyclerview 的 OnBindViewHolder 中设置的单击侦听器获得回调 (alarmTimePressed) 时,我的 ActionMode 工作得很好。我启动了我的 ActionMode 并且效果很好,我保存了 ActionMode 在我的视图模型中是否处于活动状态,这在旋转时效果很好,就好像它在旋转之前处于活动状态一样,它仍然保持活动状态。正如您在我的 OnViewCreated 中看到的那样,当 ActionMode 应该处于活动状态时,我会调用 startActionMode()。问题是当我从那里启动它时,永远不会调用 onCreateActionMode 回调。这很奇怪,因为即使在旋转之后,如果我在 RecyclerView 中选择一个项目,它也会正确启动 ActionMode。我传递给 startActionMode 的视图在这两种情况下都是相同的。如果您需要 PrimaryActionCallback 的代码,请访问:https ://hastebin.com/vepujuhefe.m. 不认为这是非常必要的,但如果你觉得有必要看看:)。有人知道这里发生了什么吗?

标签: androidandroid-fragmentsandroid-actionmode

解决方案


推荐阅读