最近在学习Compose的过程中,参考MVVM对整体框架进行了封装,分为BaseActivity、BaseViewModel,通过继承和反射的方式,完成对ViewModel的创建和绑定。以下给出示例:
BaseViewModel
采用实现接口的方式,完成对BaseViewModel的定义。优势:提高代码的安全性、灵活性和适用性。
// ViewModel的定义
interface IViewModel<out ViewModel : BaseViewModel> {
val mViewModel: ViewModel
}
封装BaseViewModel,可以实现公用的方法,比如网络请求时的转圈效果:
abstract class BaseViewModel : ViewModel() {
private val _isLoading = MutableStateFlow(false)
val isLoading: StateFlow<Boolean> = _isLoading
protected fun setLoading(isLoading: Boolean) {
_isLoading.value = isLoading
}
protected fun handleError(exception: Exception) {
Log.e(TAG, "$exception")
}
companion object {
private const val TAG = "BaseViewModel"
}
}
BaseActivity
-
使用反射的方式传入ViewModel
-
使用遍历继承链的方式实例化mViewModel变量
-
这样在子类中就可以通过mViewModel调用相关的ViewModel获取数据
-
通过抽象方法的方式传入Composable方法
PS:不将Composable方法作为类的构造参数传入的原因是,可能我们在Composable方法调用的一些系统API需要使用Context,而Composable方法作为构造参数传入由于Activity生命周期未到onCreate,无法获取到Context,所以将Composable方法定义为抽象方法,由子类实现。
abstract class BaseActivity<out ViewModel : BaseViewModel> : ComponentActivity(),
IViewModel<ViewModel> {
final override val mViewModel: ViewModel
init {
mViewModel = findViewModelClass().newInstance()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
ScreenContent()
}
}
}
}
@Suppress("UNCHECKED_CAST")
private fun findViewModelClass(): Class<ViewModel> {
var thisClass: Class<*> = this.javaClass
while (true) {
(thisClass.genericSuperclass as? ParameterizedType)?.actualTypeArguments?.firstOrNull()
?.let {
return it as Class<ViewModel>
}
?: run {
thisClass = thisClass.superclass ?: throw IllegalArgumentException()
}
}
}
@Composable
abstract fun ScreenContent()
}
使用示例
以下是一个使用上述MVVM进行实现的示例代码,相当于跳转目录。
// MainActivity
class MainActivity : BaseActivity<MainViewModel>() {
@Composable
override fun ScreenContent() = ScrollableButtonColum(mViewModel)
}
@Composable
private fun ScrollableButtonColum(viewModel: MainViewModel) {
val context = LocalContext.current
val scrollState = rememberScrollState()
Column(modifier = Modifier.verticalScroll(scrollState)) {
viewModel.buttons.forEach { buttonText ->
Button(
onClick = {
with(context) {
when (buttonText) {
MainViewModel.FLASH_LIGHT -> jumpTo<FlashlightActivity>()
MainViewModel.ANIMATION_OFFSET -> jumpTo<AnimationOffsetActivity>()
MainViewModel.TAB_PAGER -> jumpTo<TabActivity>()
MainViewModel.SCRAPE_CARD -> jumpTo<ScratchCardActivity>()
}
}
},
modifier = Modifier.padding(8.dp)
) {
Text(text = buttonText)
}
}
}
}
// MainViewModel
class MainViewModel : BaseViewModel() {
val buttons: List<String> by lazy {
listOf(
FLASH_LIGHT, ANIMATION_OFFSET, TAB_PAGER, SCRAPE_CARD
)
}
companion object {
const val FLASH_LIGHT = "手电筒"
const val ANIMATION_OFFSET = "动画偏移"
const val TAB_PAGER = "首页模拟"
const val SCRAPE_CARD = "刮刮乐"
}
}