介绍

本章是一个运用上一章:设计准则中学到的概念的项目。

项目的方针包括以下完成:

• 创建一个运用程序,该运用程序运用View作为实在来历。

• 修正运用程序,使其运用ViewModel作为实在来历。

• 将状况和事情进行分组,以简化View和ViewModel之间的消息传递。

例如,在这个项目中,咱们将完成电子商务的一部分屏幕。

该电子商务将在下章:OrderNow,一个实在运用程序中设计和开发的一个运用程序示例。

该屏幕将是OrderScreen,其中包括用户请求的订单信息和用户或买家的其他联系详情。

咱们将在项目中只完成屏幕的一部分以简化。方针是练习办理状况的不同方式。

后面会有一个完好的事例:

github.com/yaircarreno…

咱们的方针是完成一个包括两个字段的表单:

• User name

• Phone number

另外,”PayOrder”按钮的启用或禁用将取决于“User name”和“Phone number”字段的正确验证。以下将展示不同的完成选项。

“Views”作为实在源

首要,咱们需求辨认哪些UI元素或许会发生变化,并在屏幕上代表不同的状况。在这个比如中,它们是:

• 为用户名输入的文本值。

• 为电话号码输入的文本值。

• 付出订单按钮的启用/禁用特点。

因而,在View(Composables)中,咱们能够这样表明这些特点:

var name  by remember { mutableStateOf("")}
var phone by remember { mutableStateOf("")}

为Android构建现代应用—— 练习状态管理

那么,“Pay order” 按钮的((enable/disable)特点的状况呢?

在这种情况下,这个状况是由其他两个状况:名字和电话号码衍生出来的。因而,这个状况不需求进一步界说。

View代码或许像这样:

@Preview
@Composable
fun OrderScreen1(){
    var name by remember{ mutableStateOf("")}
    var phone by remember { mutableStateOf("")}
    ContactInformation3(name=name, onNameChange = {name=it},phone=phone, onPhoneChange = {phone=it})
}
@Composable
fun ContactInformation3(name:String,onNameChange:(String)->Unit,phone:String,onPhoneChange:(String) ->Unit) {
    Column(modifier= Modifier
        .fillMaxSize()
        .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally,){
        TextField(
            label={ Text( "User name") },
            value = name,
            onValueChange = onNameChange
        )
        Spacer(modifier = Modifier.padding(5.dp))
        TextField(
            label = { Text(text = "Phone number")},
            value = phone,
            onValueChange=onPhoneChange
        )
        Spacer(Modifier.padding(5.dp))
        Button(onClick = { println("Order generated for $name and phone $phone") }, enabled = name.length>1 && phone.length==11){
            Text(text = "Pay Order")
        }
    }
}

为Android构建现代应用—— 练习状态管理

为Android构建现代应用—— 练习状态管理
为Android构建现代应用—— 练习状态管理​编辑

那么事情呢?

在这个屏幕示例中,咱们辨认出的事情有:

• “User name”被修正的事情。

• “Phone number”被修正的事情。

• 选中(点击)“Pay Order”按钮的事情。 这些事情的处理方式如下:

	//User name changed
	onNameChange = { name = it }
	...
	//Phone number changed
	onPhoneChange = { phone = it }
	//Pay order clicked
	Button(
	    onClick = {
	        println("Order generated for $name and phone $phone")
	    },
	...
	)

为Android构建现代应用—— 练习状态管理

留意!

运用ViewModel,项目中必须有Google在ViewModel中所提供的依靠项,声明依靠项:

dependencies {
	def lifecycle_version = "2.5.0-rc01"
	// ViewModel
	implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
	// ViewModel utilities for Compose
	implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
}

为Android构建现代应用—— 练习状态管理

View的完好完成如下:

class OrderViewModel:ViewModel() {
    var name by  mutableStateOf("")
    var onNameChange :(String)->Unit ={ name=it}
    var phone by mutableStateOf("")
    var onPhoneChange:(String)->Unit ={phone=it}
    var payOrder:()->Unit={
        println("name=$name  phone=$phone")
    }
}
@Preview
@Composable
fun OrderScreen(viewModel2: OrderViewModel = viewModel()) {
    ContactInformation4(viewModel2.name,viewModel2.onNameChange,viewModel2.phone,viewModel2.onPhoneChange,viewModel2.payOrder)
}
@Composable
fun ContactInformation4(name: String, onNameChange: (String) -> Unit, phone:String, onPhoneChange: (String) -> Unit, payOrder:()->Unit) {
    Column(modifier = Modifier
        .fillMaxSize()
        .padding(8.dp),horizontalAlignment=Alignment.CenterHorizontally){
        TextField(label = { Text(text = "User name")}, value = name, onValueChange = onNameChange )
        Spacer(modifier = Modifier.padding(5.dp))
        TextField(label = { Text(text = "phone number")},value=phone, onValueChange = onPhoneChange)
        Spacer(modifier = Modifier.padding(5.dp))
        Button(onClick = payOrder, enabled = name.length>1&& phone.length==11) {
            Text(text = "Pay order")
        }
    }
}

为Android构建现代应用—— 练习状态管理

在这第二种完成选项中,咱们看到状况和事情都被托付给了ViewModel;因而,ViewModel成为了实在源。

随着实在源的改变,设计获得了在ViewModel中运用集中式业务或表明逻辑的灵活性。 到目前为止,咱们有了一个适宜且工作正常的完成。可是,通过界说组件UI的状况,咱们能够看到它能够得到进一步的改善。

分组“States”

在上面的比如中,你能够看到字段是表单的一部分。按钮的状况乃至依靠于表单的字段。因而,将这些UI元素组合成一个包括它们的单一UI元素是有意义的。 由于比如中只有三个UI元素,分组它们的好处或许不那么明显;然而,让咱们想象一个屏幕,它有许多其他区域,包括许多其他的UI元素。 首要,将状况组合在一个名为FormUiState的结构中,如下所示:

data class FormUiStates(val name:String="",var phone:String="")
val FormUiStates.successValidated:Boolean get() = name.length>1 && phone.length==11

为Android构建现代应用—— 练习状态管理

在ViewModel中,咱们用一个单一的状况替换状况,如下所示:

data class FormUiStates(val name:String="",var phone:String="")
val FormUiStates.successValidated:Boolean get() = name.length>1 && phone.length==11
class OrderViewModel5:ViewModel() {
    //UI's states
    var formUiState by mutableStateOf(FormUiStates())
    private set
    //UI's Events
    fun onNameChange():(String)->Unit={
        formUiState=formUiState.copy(name=it)
    }
    fun onPhoneChange():(String)->Unit={
        formUiState= formUiState.copy(phone = it)
    }
    fun payOrder():() ->Unit={
        println("Order generated for ${formUiState.name} and phone ${formUiState.phone}")
    }
}
//在View中,咱们依照如下方式更新状况的运用:
@Preview
@Composable
fun OrderScreen(viewModel5:OrderViewModel5= viewModel()){
    ContactInformation5(
        name = viewModel5.formUiState.name,
        onNameChange = viewModel5.onNameChange(),
        phone = viewModel5.formUiState.phone,
        onPhoneChange = viewModel5.onPhoneChange(),
        payOrder = viewModel5.payOrder(),
        isValidPayOrder = viewModel5.formUiState.successValidated)
}
@Composable
fun ContactInformation5(name: String,onNameChange: (String) -> Unit,phone: String,onPhoneChange: (String) -> Unit,payOrder: () -> Unit,isValidPayOrder:Boolean) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally
    ){
        TextField(label = { Text(text = "user name")}, value =name , onValueChange =onNameChange )
        Spacer(modifier = Modifier.padding(5.dp))
        TextField(label = { Text(text = "phone number")}, value =phone   , onValueChange =onPhoneChange )
        Spacer(modifier = Modifier.padding(5.dp))
        Button(onClick = payOrder, enabled = isValidPayOrder) {
            Text(text = "Pay Order")
        }
    }
}

为Android构建现代应用—— 练习状态管理

当一个屏幕上有许多UI元素时,将相关的UI元素分组变得愈加重要。将UI元素分组到组件UI的状况中能够简化、安排和生成在完成中更清晰的代码。 同样的技巧能够运用于事情。如下所示,主要的区别在于表明类型。

分组“Eents”

为了进一步收拾代码,咱们现在将分组表单的相关事情。首要,咱们要创建一个结构来按以下方式分组事情:

咱们留意到,事情的分组与上一章中的屏幕UI状况部分解释的技能类似。记住,这是适用的,因为咱们界说的不同类型的事情是相关的,但它们能够是互斥的和独立的。

在ViewModel中,消息被简化为如下所示的一个:


@Preview
@Composable
fun OrderScreen7(viewModel7:OrderViewModel7= viewModel()){
    ContactInformation7(
        name = viewModel7.formUiState.name,
        onNameChange = {viewModel7.onFormEvent(FormUiEvent.onNameChange(it))},
        phone = viewModel7.formUiState.phone,
        onPhoneChange = {viewModel7.onFormEvent(FormUiEvent.onPhoneChange(it))},
        payOrder = {viewModel7.onFormEvent(FormUiEvent.payOrderClicked)},
        isValidPayOrder = viewModel7.formUiState.successValidated
    )
}
@Composable
fun ContactInformation7(
    name: String,
    onNameChange: (String) -> Unit,
    phone:String,
    onPhoneChange:(String)->Unit,
    payOrder:()->Unit,
    isValidPayOrder:Boolean) {
    Column(modifier = Modifier
        .fillMaxSize()
        .padding(8.dp), horizontalAlignment = Alignment.CenterHorizontally){
        TextField(label = { Text(text = "User name")}, value =name , onValueChange =onNameChange )
        Spacer(modifier = Modifier.padding(5.dp))
        TextField(value = phone, label = { Text(text = "phone number")}, onValueChange =onPhoneChange )
        Spacer(modifier = Modifier.padding(5.dp))
        Button(onClick = payOrder,
            enabled = isValidPayOrder) {
            Text(text = "Play Order")
        }
    }
}
class OrderViewModel7 :ViewModel() {
    //UI's states
    var formUiState by mutableStateOf(FormUiSatate6())
    private set
    //UI's Events
    fun onFormEvent(formEvent:FormUiEvent){
        when(formEvent){
            is FormUiEvent.onNameChange->{
                formUiState=formUiState.copy(name = formEvent.name)
            }
            is FormUiEvent.onPhoneChange->{
                formUiState=formUiState.copy(phone = formEvent.phone)
            }
            is FormUiEvent.payOrderClicked ->{
                println("Sending form with parameters:${formUiState.name} and phone ${formUiState.phone}")
            }
        }
    }
    //Business's logic or maybe some UI's logic for update the state
    companion object{
        fun applyLogicToValidateInputs(name:String,phone:String):Boolean{
            return name.length>1 && phone.length ==11
        }
    }
}
data class FormUiSatate6(val name:String="",val phone:String="")
val FormUiSatate6.successValidated:Boolean get()=name.length>1 && phone.length==11
sealed class FormUiEvent{
    data class onNameChange (val name:String):FormUiEvent()
    data class onPhoneChange(val phone:String):FormUiEvent()
    object payOrderClicked: FormUiEvent()
}

为Android构建现代应用—— 练习状态管理

留意:

有些或许现已留意到,我将字段验证逻辑包括在了FormUiState状况结构中。 由于逻辑通常比验证字符长度更复杂,最好将验证和验证使命托付给ViewModel。 因而,咱们在ViewModel和FormUiState中添加了以下改变:

	// Business's logic or maybe some UI's logic for update the state
	companion object {
	fun applyLogicToValidateInputs(name: String, phone: String): Boolean {
	return name.length > 1 && phone.length > 3
	}
	}
data class FormUiState(
	val name: String = "",
	val phone: String = ""
	)
val FormUiState.successValidated: Boolean get() =OrderViewModel.applyLogicToValidateInputs(name, phone)

为Android构建现代应用—— 练习状态管理

现在所有的逻辑都在ViewModel端了。

总结

在这个练习中,咱们回忆了运用View或ViewModel作为实在源来办理状况和事情的办法。 此外,咱们运用了一些技能来更好地安排状况和事情的结构,以便有一个更好安排和易于盯梢的完成。 在下一章中,咱们将看到“当即下单”运用程序的总结,这是一个电子商务运用,咱们将完成它,以解释现代Android运用开发的概念和技能。