当屏幕旋转时,按钮点击的实时数据观察器

4
我希望在片段上点击按钮并更新MainActivity。这段代码运行良好,但由于Activity生命周期OnCreate被再次调用,并且viewModel.nav.observe又被调用了一个新值true,而我只希望它在仅有时调用。
我需要的是,在单击事件触发后值将设置为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
        }
    }
}

主活动

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)
}
3个回答

3

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

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

步骤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"
    }
}

第二步:在SharedViewModel中

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

第三步:在MainActivity中

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

1
我不喜欢这个解决方案,但this code lab说,你应该将“导航已处理”事件发送回视图模型。

视图模型实现:

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()
})

0

只需使用saveStateViewMode,文档这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接