fragment之間相互傳資料、共享資料

靈劍山真人發表於2021-05-31

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

相關文章