在 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使用簡直不要太絲滑!!!