屏幕旋转时单击按钮上的实时数据观察器
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()
})
我想知道按钮何时单击片段并更新 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()
})