介绍
本章是一个运用上一章:设计准则中学到的概念的项目。
项目的方针包括以下完成:
• 创建一个运用程序,该运用程序运用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("")}
那么,“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")
}
}
}
编辑
那么事情呢?
在这个屏幕示例中,咱们辨认出的事情有:
• “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")
},
...
)
留意!
运用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"
}
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")
}
}
}
在这第二种完成选项中,咱们看到状况和事情都被托付给了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
在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")
}
}
}
当一个屏幕上有许多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()
}
留意:
有些或许现已留意到,我将字段验证逻辑包括在了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)
现在所有的逻辑都在ViewModel端了。
总结
在这个练习中,咱们回忆了运用View或ViewModel作为实在源来办理状况和事情的办法。 此外,咱们运用了一些技能来更好地安排状况和事情的结构,以便有一个更好安排和易于盯梢的完成。 在下一章中,咱们将看到“当即下单”运用程序的总结,这是一个电子商务运用,咱们将完成它,以解释现代Android运用开发的概念和技能。