在 Fragment 之間共享資料

Activity 中的兩個或更多 Fragment 需要相互通訊是一種很常見的現象。想象一下拆分檢視 (master-detail) Fragment 的常見情況,假設您有一個 Fragment,在該 Fragment 中,使用者從列表中選擇一項,還有另一個 Fragment,用於顯示選定項的內容。這種情況不太容易處理,因為這兩個 Fragment 都需要定義某種介面描述,並且所有者 Activity 必須將兩者繫結在一起。此外,這兩個 Fragment 都必須處理另一個 Fragment 尚未建立或不可見的情況。

可以使用 ViewModel 物件解決這一常見的難點。這兩個 Fragment 可以使用其 Activity 範圍共享 ViewModel 來處理此類通訊,如以下示例程式碼所示:

class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>() fun select(item: Item) {
selected.value = item
}
} class MasterFragment : Fragment() { private lateinit var itemSelector: Selector // Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
} class DetailFragment : Fragment() { // Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
// Update the UI
})
}
}

請注意,這兩個 Fragment 都會檢索包含它們的 Activity。這樣,當這兩個 Fragment 各自獲取 ViewModelProvider 時,它們會收到相同的 SharedViewModel 例項(其範圍限定為該 Activity)。

此方法具有以下優勢:

  • Activity 不需要執行任何操作,也不需要對此通訊有任何瞭解。
  • 除了 SharedViewModel 約定之外,Fragment 不需要相互瞭解。如果其中一個 Fragment 消失,另一個 Fragment 將繼續照常工作。
  • 每個 Fragment 都有自己的生命週期,而不受另一個 Fragment 的生命週期的影響。如果一個 Fragment 替換另一個 Fragment,介面將繼續工作而沒有任何問題。

當在其中一個fragment中修改了資料,希望另外一個fragment的資料也可以被修改。使用“共享 view model”的方式避免了一大堆炒雞繁瑣的回撥介面。

class DatePickerFragment:DialogFragment() {
private val crimeDetailViewModel:CrimeDetailViewModel by activityViewModels()//1設定為共享viewmodel override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreateDialog(savedInstanceState)
val dateListener = DatePickerDialog.OnDateSetListener{
_, year, month, dayOfMonth ->
val chooseDate: Date = GregorianCalendar(year, month, dayOfMonth).time//
val crime = crimeDetailViewModel.crimeLiveData.value
crime?.date = chooseDate
crimeDetailViewModel.saveCrime(crime!!)//2儲存
}
val calendar = Calendar.getInstance()
calendar.time = crimeDetailViewModel.crimeLiveData.value?.date ?: Date()//
val initYear = calendar.get(Calendar.YEAR)
val initMonth = calendar.get(Calendar.MONTH)
val initDay = calendar.get(Calendar.DAY_OF_MONTH) return DatePickerDialog(
requireContext(),
dateListener,
initYear,
initMonth,
initDay
)
}
}

上面的程式碼例項中就使用了共享viewmodel.當然在另一個activity中也是用 by activityViewModels()這種方式初始化。

配合Navigation和livedata使用簡直不要太絲滑!!!