屏幕旋转时单击按钮上的实时数据观察器

Live Data Observer On Button Click when screen rotataed

我想知道按钮何时单击片段并更新 MainActivity。这段代码工作正常,但是当屏幕由于 Activity LifeCycle OnCreate 再次调用而旋转并且 viewModel.nav.observe 使用新值 true 再次调用时,但我希望它被调用 onButton 点击​​。 我需要的是 onClick 值将被设置为 true 然后 false,所以我只知道按钮何时被点击而不是在屏幕上旋转。 或者以其他方式知道按钮在片段中被单击。

我怎样才能做到这一点?

片段

class Fragment1 : Fragment() {

    companion object {
        fun newInstance() = Fragment1()
    }

    private lateinit var viewModel: SharedViewModel

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_1, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
        
        buttonNextF1.setOnClickListener {
            viewModel.nav.value = true
        }
    }
}

主要Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val viewModel = ViewModelProvider(this).get(SharedViewModel::class.java)

        var fragment1 = Fragment1.newInstance()
        navigateToFragment(fragment1)

        viewModel.nav.observe(this, {
            Log.d("DTAG", "Status: $it")
        })
    }

    private fun navigateToFragment(fragment: Fragment) {

        val fragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.addToBackStack(null)
        fragmentTransaction.replace(R.id.mainFrame, fragment)
        fragmentTransaction.commit()

    }
}

视图模型

class SharedViewModel : ViewModel() {
    
    var nav = MutableLiveData(false)
}

您可以使用 SingleLiveEvent 来实现您的目标,因为:

  • 您只想通知事件(点击片段上的按钮)一次
  • LiveData上只有一个观察者(MainActivity)在观察

步骤 1: 创建 SingleLiveEvent.kt 文件

class SingleLiveEvent<T> : MutableLiveData<T?>() {
    private val mPending = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T?>) {
        if (hasActiveObservers()) {
            Log.w(
                TAG,
                "Multiple observers registered but only one will be notified of changes."
            )
        }
        // Observe the internal MutableLiveData
        super.observe(owner, Observer { t ->
            if (mPending.compareAndSet(true, false)) {
                observer.onChanged(t)
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        mPending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        value = null
    }

    companion object {
        private const val TAG = "SingleLiveEvent"
    }
}

第 2 步: 在 SharedViewModel 中

class SharedViewModel : ViewModel() {
    var nav = SingleLiveEvent<Boolean>()
}

步骤 3:在 MainActivity

viewModel.nav.observe(this, Observer {
    Log.d("DTAG", "Status: $it")
})

只需使用saveStateViewMode,文档here

我不喜欢这个解决方案,但是 this code lab 说,你应该 post “导航处理”事件返回到视图模型。

查看模型实现:

interface MyViewModel {
    val navigationEvent: LiveData<Unit?>
    
    fun onNavigationButtonClicked()
    fun onNavigationDone()
}

class MyViewModelImpl : ViewModel(), MyViewModel {
    
    override val navigationEvent = MutableLiveData<Unit?>()

    override fun onNavigationButtonClicked() {
        navigationEvent.value = Unit
    }

    override fun onNavigationDone() {
        navigationEvent.value = null
    }
}

查看模型使用情况

vm.navigationEvent.observe(lifecycleOwner, { event ->
    // check event is not handled yet
    event ?: return@observe
    // make transition
    findNavController().navigate(direction)
    // notify view model navigation event is handled
    vm.onNavigationDone()
})