我有一個視圖模型,它與一個片段進行了資料系結。該視圖模型與主活動共享。
我的按鈕被系結到視圖上,如下所示:
我的按鈕被系結到視圖上。
<Button
android:id="@ id/startStopBtn"/span>
android:text="@{dashboardViewModel.startStopText == null ? @string/startBtn : dashboardViewModel.startStopText}"。
android:onClick = "@{() -> dashboardViewModel.onStartStopButton(text)}"
android:layout_width="83dp"/span>
android:layout_height="84dp"/span>
android:layout_gravity="center_horizontal|center_vertical"
android:backgroundTint="@{dashboardViewModel.isRecStarted == false ? @color/startYellow : @color/stopRed}"
tools:backgroundTint="@color/startYellow"
android:diplicateParentState="false"。
tools:text="START"/span>
android:textColor="#FFFFFF" />
我期望發生的是,每次我按下按鈕時,函式onStartStopButton(context)就會運行。只要我不旋轉設備,這就能正常作業。當我旋轉設備時,函式會運行兩次,如果我再次旋轉,函式會運行三次,以此類推。如果我轉到另一個片段,然后再回到儀表盤片段,這就沒有問題了。看起來在我每次旋轉螢屏時,實時資料觀察者都會被注冊,但在我每次分離和重新連接該片段時卻不會。
這對于該片段中的所有元素都是如此,無論它們是被資料系結還是我手動觀察它們。
片段代碼:
class DashboardFragment : Fragment(){
private var _binding: FragmentDashboardBinding? = null
private val binding get() = _binding!
private val dashboardViewModel: DashboardViewModel by activityViewModels()
override fun onCreateView(
膨脹器。LayoutInflater,
容器。ViewGroup?
savedInstanceState。Bundle?
)。視圖? {
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
val root: View = binding.root
binding.dashboardViewModel = dashboardViewModel
binding.lifecycleOwner = viewLifecycleOwner
dashboardViewModel.bleSwitchState.observe(viewLifecycleOwner, Observer { switchState -> handleBleSwitch(switchState) })
dashboardViewModel.yLims.observe(viewLifecycleOwner, Observer { yLims ->
updatePlotWithNewData(yLims.first, yLims.second)
})
Timber.i("Dahsboard on create: DashboardViewModel in fragment: $dashboardViewModel")
return root
}
}
視圖模型:
class DashboardViewModel : ViewModel(){
//區域實時資料。
private var _isRecStarted = MutableLiveData<Boolean>()
val isRecStarted: LiveData<Boolean> get() = _isRecStarted
//private var _bleSwitchState = MutableLiveData< Boolean>()
val bleSwitchState = MutableLiveData<Boolean> ()
private var _startStopText = MutableLiveData< String> ()
val startStopText: LiveData<String> get() = _startStopText
private var _yLims = MutableLiveData<Pair<kotlin.Float,kotlin.Float> >()
val yLims: LiveData<Pair<kotlin.Float,kotlin.Float>> get() = _yLims
//endregion
init {
Timber.d("DashboardViewModel created!")
bleSwitchState.value = true ?
}
//區域啟動停止按鈕。
fun onStartStopButton(context: Context){
Timber.i("開始停止按鈕被按下,記錄資料大小。${recordingRawData.size},正在啟動。${isRecStarted.value}")
isRecStarted.value?.let{ isRecStarted ->
if (!isRecStarted){ // starting recording.
_isRecStarted.postValue(true)
_startStopText.postValue(context.getString(R.string.stopBtn))
startDurationTimer()
}else{ //停止錄音
_isRecStarted.postValue(false)
_startStopText.postValue(context.getString(R.string.startBtn))
stopDurationTimer()
}
} ?: run{
Timber.e("錯誤! 啟動的程式因某種原因不存在")
}
}
}
視圖模型第一次從MainActivity中創建,如下所示:
class MainActivity : AppCompatActivity(){
private val dashboardViewModel: DashboardViewModel by viewModels()
override fun onCreate(sedInstanceState: Bundle? ){
Timber.i("DashboardViewModel in main activity: $dashboardViewModel")
}
}
編輯解釋為什么MainActivity會被引導到ViewModel:
ViewModel鏈接到主活動的原因是,主活動為資料流處理一些藍牙的東西,當一個新的樣本到達時,處理它的邏輯和更新儀表盤片段的UI是在DashboardViewModel上。即使儀表盤片段不在那里,資料仍然需要被處理。 所以我需要將新的樣本從主活動中傳遞給DashboardViewModel,因為那是我接收它的地方。有什么建議可以使其發揮作用嗎?
uj5u.com熱心網友回復:
正如你所知,當你用activityViewModels實體化一個片段的ViewModel時,這意味著ViewModel將遵循包含該片段的活動的生命周期。具體到這里就是MainActivity。
那么在你的案例中,ViewModel 與 Activity 生命周期系結意味著什么?
當你回傳片段時,通常LiveData(與片段生命周期相連的ViewModel)將再次觸發。 但是當該ViewModel被附加到Activity的生命周期時,當回傳到Fragment時,LiveData將不會被觸發。
這就導致了當你回傳片段時,你的LiveData不會再次觸發。
而且,LiveData僅根據活動的生命周期來觸發。也就是說,當你旋轉螢屏時,活動重新初始化,現在你的LiveData被觸發了。
編輯: 在這里,我將給你一個方法。也許我下面的代碼并不完全適用于你的情況,但我認為這將有助于你在將ViewModel系結到Activity時如何控制LiveData和ViewModel。
首先,我建議每個Fragment應該有自己的ViewModel,它不應該依賴于任何其他Fragment或Activity。在這里,你應該把由 接下來,當有資料被ShareViewModel的LiveData觸發時,你將在與你的Fragment相關的ViewModel中為LiveData設定值。如下: DashboardModel
DashboardViewModel.kt DashboardFragment.kt 編輯2: 在你旋轉設備的情況下,活動和片段將被重新初始化。那時,LiveData將被觀察到。為了防止這種情況,使用 首先,讓我們創建一個 接下來,修改你要觸發一次的LiveData的回傳型別。 ShareViewModel.kt 添加這個擴展來輕松獲得LiveData的觀察結果。
LiveDataExt.kt 最后在視圖中,你可以得到由LiveDatat觀察到的資料。
注意:當使用
標籤:activityViewModels()初始化的DashboardViewModel重命名為ShareViewModel或任何你認為與此相關的Activity和Fragment之間的ShareViewModel。
class DashboardFragment : Fragment() {
//將這個`DashboardViewModel`改成另一個類名。可以是`ShareViewModel`。
private val shareViewModel: ShareViewModel by activityViewModels()
///這是連接到DashboardFragment生命周期的ViewModel。
private val viewModel: DashboardViewModel by viewModels()
private lateinit var _binding: FragmentDashboardBinding? = null
private val binding get() = _binding!
override fun onCreateView(
膨脹器。LayoutInflater,
容器。ViewGroup?
savedInstanceState。Bundle?
)。視圖? {
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
binding.dashboardViewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
override fun onDestroyView() {
_binding = null ()
super.onDestroyView()
}
}
class DashboardViewModel。ViewModel() {
private val _blueToothSwitchState = MutableLiveData< YourType> ()
val blueToothSwitchState。LiveData<YourType> = _blueToothSwitchState
private val _yLims = MutableLiveData<Pair<YourType> >()
val yLims: LiveData<Pair<YourType>> = _blueToothSwitchState
fun setBlueToothSwitchState(data: YourType) {
_blueToothSwitchState.value = data
}
fun setYLims(data: Pair<YourType, YourType>) {
_yLims.value = data class DashboardFragment : Fragment() {
...
override fun onViewCreated(view。View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
shareViewModel.run {
bleSwitchState.observe(viewLifeCycleOwner) {
viewModel.setBlueToothSwitchState(it)
}
yLims.observe(viewLifeCycleOwner) {
viewModel.setYLims(it)
}
}
viewModel.run {
//這里,LiveData根據`DashboardFragment'的生命周期發射觀察。
//所以當你回到`DashboardFragment`時,LiveData被重新觸發,你仍然可以得到該LiveData的觀察結果。
blueToothSwitchState.observe(viewLifeCycleOwner, ::handleBleSwitch)
yLims.observe(viewLifeCycleOwner) {
updatePlotWithNewData(it.first, it.second)
}
}
}
...
}
Event。它將使你的LiveData無法觀察到該值,直到你再次為LiveData設定該值為止。
Event類。open class Event< out T>(private val content: T) {
var hasBeenHandled = false
private set
fun getContentIfNotHandled(): T? = if (hasBeenHandled) {
null: T?
} else {
hasBeenHandled = true {
內容
}
fun peekContent(): T = content
}
class ShareViewModel。ViewModel() {
private val _test = MutableLiveData<Event<YourType>>()
val test。LiveData<Event<YourType>> = _test
fun setTest(value: YourType){
_test.value = Event(value)
}
}
fun <T>LiveData<Event<T> > 。 eventObserve(所有者。LifecycleOwner, observer: (t: T) -> Unit){
this.observe(owner) { it?.getContentIfNotHandled()? .let(observer) }
}
class DashboardFragment : Fragment() {
...
override fun onViewCreated(view。View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
shareViewModel.test.eventObserve(viewLifeCycleOwner) {
Timber.d("this is test")
}
}
...
}
LiveData與Event時,確保LiveData在旋轉設備時沒有被重置。如果LiveData再次被設定為值,即使你使用Event,LiveData仍然會被觸發。
