2023年12月29日 星期五

【Android】 在 Views 中使用 Compose (2) : 在 Compose 中使用 ViewModel 與 LiveData


傳入 ViewModel

1. 建立 ViewModel、在 Fragment 中使用


import androidx.lifecycle.ViewModel

class PViewModel: ViewModel() {
}

2. 在Fragment 中,將 ViewModel 傳入 composable function  

注意:在正式版應用程式中,只能由畫面層級可組合項參照 ViewModel。如果子項可組合項需要來自 ViewModel 的資料,最佳做法是只傳遞子項可組合項需要的資料,不要傳遞整個 ViewModel 的資料。詳情請參閱「螢幕 UI 狀態」。

可組合項沒有自己的 ViewModel 例項,相同的例項會在可組合項以及代管該 Compose 程式碼的生命週期擁有者 (Activity 或 Fragment) 之間共用。

class BlankFragment2 : Fragment() {

    private var _binding: FragmentBlank2Binding? = null
    private val viewModel: PViewModel by viewModels()

    // This property is only valid between onCreateView and onDestroyView.
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentBlank2Binding.inflate(inflater, container, false)
        val view = binding.root
        binding.apply {
            composeView.setContent {
                // You're in Compose world!
                MaterialTheme {
                    PlantDetailDescription(viewModel)
                }
            }
        }
        return view
    }
}

@Composable
fun PlantDetailDescription(pViewModel : PViewModel) {
    Surface {
        Text("Hello Compose")
    }
}

從可組合項觀察 LiveData

1. 在 ViewModel 中建立 LiveData 的變數

class PViewModel: ViewModel() {
    private val _score = MutableLiveData(0)
    val score: LiveData<Int>
        get() = _score
}

2. 在可組合項觀察 LiveData

注意:LiveData.observeAsState() 會開始觀察 LiveData,並以 State 物件表示其值。每當有新的值發布至 LiveData 時,傳回的 State 都會更新,造成所有 State.value 的用法重新組合。
要使用 observeAsState()  需要加上 : 
implementation ("androidx.compose.runtime:runtime-livedata:1.5.4")

@Composable
fun PlantDetailDescription(pViewModel : PViewModel) {

    val pScore by pViewModel.score.observeAsState()
    pScore?.let {
        PlantScore(pScore!!)
    }

}

/*由於 LiveData 發出的值可能是 null,因此您必須將其用法納入 null 檢查中。
  因此,也為了方便重複使用,建議您將 LiveData 的消費與監聽分割至不同的可組合項。
  讓我們新建一個名為 PlantScore 的可組合項,用來顯示 Score 資訊。*/
@Composable
fun PlantScore(score: Int){
    Text("分數是$score")
}

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentBlank2Binding.inflate(inflater, container, false)
        val view = binding.root
        binding.apply {
            composeView.setContent {
                // You're in Compose world!
                MaterialTheme {
                    PlantDetailDescription(viewModel)
                }
            }
        }
        return view
    }

使用屬性委託 (by) 可以不用 .value

  • 下列三種方式皆在可組合項中宣告 MutableState 物件( 這三種宣告作用相等,僅做為語法糖,運用在不同狀態用途中。) :
    •  val mutableState = remember { mutableStateOf(default) } 
    •  var value by remember { mutableStateOf(default) } 
      •  使用這種方式,因為是使用屬性委託而非=,所以不用每次都輸入 .value 
    •  val (value, setValue) = remember { mutableStateOf(default) } 




0 comments:

張貼留言