0


Android Compose UI (三) (Compose UI + MVI)结合使用

文章目录

1.前言

在这里插入图片描述

在上一篇文章中已经介绍了常规的没有结合Compose UI来使用的MVI模式了,本篇文章就是把之前的内容结合起来,在之前的基础上修改为完整的Compose UI + MVI的案例,如果对于文章中有不理解的可以回过头去看之前的内容.

2.ViewModel的完整代码

class LoginViewModel :ViewModel(){val loginChannel = Channel<LoginIntent>(Channel.UNLIMITED)privateval loginState =MutableStateFlow(LoginState())val uiLoginState: StateFlow<LoginState>= loginState

    privateval toNewPage = MutableSharedFlow<Boolean>()val uiToNewPage: SharedFlow<Boolean>= toNewPage

    init{
        viewModelScope.launch{
            loginChannel.consumeAsFlow().collect{when(it){is LoginIntent.RunLoginIntent ->{login()}}}}}privatefunlogin(){
        loginState.value = loginState.value.copy(isLogin =true)
        viewModelScope.launch{val name = loginState.value.nameCache.value
            val password = loginState.value.passwordCache.value
            val enPassword =getEncryptPassword(password)val loginToken = APIManager.requestLoginToken(name,getEncryptPassword(password),getVerify(name, enPassword))//以上delay是模拟了一个请求耗时
            loginState.value = loginState.value.copy(isLogin =false)//如果token不是空的话,就代表登录成功了,emi登录成功的消息出去,让UI执行操作if(loginToken.isNotEmpty()){
                toNewPage.emit(true)}}}privatefungetEncryptPassword(password: String): String {//这里随便做一下密码加密,实际应该是做MD5处理或者其他算法处理,密码不以明文形式提交return password.plus("abc")}privatefungetVerify(userName: String, password: String): String {//这里生成校验秘钥,用于接口请求校验,也可以在请求头里面做,一般是用于进一步防止别人模拟请求return"This is where the data is encrypted"}openclass LoginIntent {object RunLoginIntent :LoginIntent()}}
ViewModel

中定义了

Channel

和对应的登录状态数据还有需要

View

层执行动作的

Flow

,一样对外暴露的还是抽象接口,然后在

init

函数中订阅

channel

,当接收到

RunLoginIntent

意图的时候就执行

login

函数.在函数中分别执行了以下步骤

  • 设置是否正在登录中为true,可以看到这里我们通过copy函数,很方便的就修改了其中某一个值,在页面中接收到这个值的改变后,就会显示登录动画.
  • 对密码明文进行加密,这里只是简单演示一下,实际会复杂一些.
  • 生成校验内容,这个一般是用来服务器防止模拟请求,客户端配合处理就行了
  • 调用APIManager请求登录数据,这里模拟的是返回一个token,后续使用这个token去做其他事情,实际可能是返回一个用户完整数据+令牌或者是其他之类的.
  • 登录接口返回之后,就把登录中的状态取消掉,因为耗时操作已经完成了.
  • 最后判定如果是登录成功,就通过toNewPage字段emit跳转页面的信息出去,实际的业务这里应该还会有登录失败了的话,需要提示用户登录失败之类的提示.

3.View的完整代码

LoginActivity
class LoginActivity :ComponentActivity(){companionobject{constval TAG ="LoginActivity"}overridefunonCreate(savedInstanceState: Bundle?){super.onCreate(savedInstanceState)
        setContent {LoginView().initView()CollectState()}}@ComposableprivatefunCollectState(){
        hiltViewModel<LoginViewModel>().apply{
            uiToNewPage.collectWithEffect{if(it){//执行登录跳转页面iLog("$TAG 跳转到主页页面")}}
            uiLoginState.collectWithEffect{ value ->if(value.isLogin){//显示登录加载框iLog("$TAG 显示了登录进度条")return@collectWithEffect}iLog("$TAG 隐藏登录进度条")}}}}constval UNIT_EXT =0xff0011

需要注意一下,这里的

hiltViewModel

需要引入一个

compose

的库

androidx.hilt:hilt-navigation-compose:1.0.0
  • onCreate中还是调用的setContent函数去设置LoginView完了去订阅相关的业务数据
  • 订阅数据中,通过接收到状态,去决定跳转以及显示网络请求动画(这里只是标识了一下,实际是操作对应的弹窗以及调用页面跳转代码)
  • 一般情况下是一个组数据就可以了,但是这里因为有跳转页面这种一次性的逻辑操作,所以加了uiToNewPage的变量
  • 可以看到所有的数据都是通过uiLoginState来管理的,不管是记录的数据状态,还是通知到UI的状态,维护数据的话也是通过这一个State就行了
LoginView
classLoginView(){@OptIn(ExperimentalUnitApi::class)@Preview@ComposablefuninitView(){Column(
            modifier = Modifier
                .background(color =colorResource(id = R.color.bg_white)).fillMaxWidth().fillMaxHeight(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ){LogIcon()VSpacer(30)Text(text ="Welcome to example", fontWeight = FontWeight.Bold, fontSize =TextUnit(23f, TextUnitType.Sp))VSpacer(30)InputEdit(hint ="please input your userName")VSpacer(5)InputEdit(hint ="please input your password",true)VSpacer(30)val loginViewModel: LoginViewModel =hiltViewModel()Button(modifier = Modifier.semantics{ testTag ="test"}, onClick ={
                loginViewModel.loginChannel.trySend(LoginViewModel.LoginIntent.RunLoginIntent).getOrThrow()}){Text(text ="Login")}}}@ComposablefunLogIcon(){Image(
            painter =painterResource(id = R.drawable.ic_launcher_background),
            contentDescription ="图标",
            modifier = Modifier
                .width(50.dp).height(50.dp).clip(CircleShape).border(2.dp,colorResource(id = android.R.color.black), CircleShape))}@ComposablefunVSpacer(height: Int){Spacer(
            modifier = Modifier
                .fillMaxWidth().height(height.dp))}@OptIn(ExperimentalMaterial3Api::class)@ComposablefunInputEdit(hint: String, isPassword: Boolean =false){val loginViewModel: LoginViewModel =hiltViewModel()val name = remember { loginViewModel.uiLoginState.value.nameCache }val password = remember { loginViewModel.uiLoginState.value.passwordCache }val state =if(isPassword) password else name
        val transformation =if(isPassword)PasswordVisualTransformation()else VisualTransformation.None
        val hintStr =if(isPassword)"Password"else"UserName"TextField(visualTransformation = transformation, value = state.value, onValueChange ={
            state.value = it
        }, modifier = Modifier
            .width(260.dp).height(56.dp), label ={Text(text = hintStr)}, keyboardOptions = KeyboardOptions.Default.copy(
            imeAction = ImeAction.Done, keyboardType = KeyboardType.Password
        ), placeholder ={Text(text = hintStr, Modifier.alpha(0.5f))})}}

前面的还是和之前文章一样,只是在数据设置这一块做出了修改,

state

变量直接放在了数据集中,在实际使用的时候通过

remeber

直接引用,可以看到和

Compose UI

结合之后,有许多

Compose

现成的函数调用,可以直接和

View

结合起来,这里就不用再费劲的订阅了,直接通过函数引用就可以了.

4.扩展函数相关

@Composablefun<V> Flow<V>.collectWithEffect(collector1: FlowCollector<V>): Int {LaunchedEffect(key1 = UUID.randomUUID()){collect(collector1)}return UNIT_EXT
}

这里订阅的时候通过

LaunchedEffect

来订阅数据变化,进行了一下基础的封装.

5.总结

到此

Compose UI + MVI

整体结构内容基本完成,可以看到

MVI

模式在

android

中的使用

google

是想要结合

Compose UI

来使用的,所以对此在

Compose

中一些现成的函数支撑,但是

MVI

本身是一种模式,不一定要绑定

Compose UI

使用,甚至都不一定要使用

Kotlin

,像

Channel

,

Flow

这些,哪怕是换了语言或者换了平台,只要是面向对象的语音,应该都是可以定制出来,所以不用整个项目转换为

Compose UI + MVI

也可以使用这种模式 ,但是最好的毕竟是有

google

支持,使用这种模式还是绑定使用

Compose UI

来.后续还会横向对比优缺点.

标签: android ui 命令模式

本文转载自: https://blog.csdn.net/BXHUHU10910/article/details/136058807
版权归原作者 CreeLu 所有, 如有侵权,请联系我们删除。

“Android Compose UI (三) (Compose UI + MVI)结合使用”的评论:

还没有评论