为Android构建现代应用——设计原则

这篇具有很好参考价值的文章主要介绍了为Android构建现代应用——设计原则。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为Android构建现代应用——设计原则 - 掘金     

state”是声明性观点的核心

在通过Compose或SwiftUI等框架设计声明性视图时,我们必须明确的第一个范式是State(状态)。UI组件结合了它的图形表示(View)和它的State(状态)。UI组件中发生变化的任何属性或数据都可以表示为状态。例如,在TextField类型的UI组件中,用户输入的文本是一个可以更改的变量;因此,value是一个可以表示为状态(name)的变量,如下面的代码片所示。

   TextField(
          label = { Text("User name") },
          value = name,
          onValueChange = onNameChange
       )


声明性View的层次结构:

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

移动应用程序屏幕可以包含View层次结构,如上图所示。每个View依次可以包含多个State变量。例如,图中的所有View都有一个State。
包含或依赖于State的View称为Stateful View(有状态视图),没有State依赖的View称为Stateless View(无状态视图)。Google和Apple都建议尽可能设计无状态视图,因为使用这种类型有以下优点:
       1.你可以重用它们
       2.它们允许你将state管理委托给其他组件
       3.它们是功能性的,避免了副作用

根据这些建议,设计应该面向Stateless views(无状态视图),并将那些Stateful View(有状态视图)转换为Stateless views(无状态视图)。
那么,如何实现这的呢?

将"State hoisting"应用委托于states

状态提升是一种将Stateful View(有状态视图)转换为Stateless View(无状态视图)的技术。这是通过控制反转实现的,如下代码:

//这是一个Stateful View(有状态视图)
@Composable
fun text1(){
    var name by remember{ mutableStateOf("")}
    var phone by remember{mutableStateOf("")}
    ContactInformation1(
        name = name,
        nNameChange = {name=it},
        phone = phone,
        onPhoneChange ={phone=it} )

}

//这是一个Stateless View(无状态视图)
@Composable
fun ContactInformation1(name:String,onNameChange:(String)->Unit,phone:String,onPhoneChange:(String)->Unit){
    Column(
        modifier = Modifier.fillMaxSize().padding(8.dp),
        horizontalAlignment = Alignment.CenterHorizontally){
        TextField(
            label={Text(text = "User name")}, 
            value = name, 
            onValueChange = onNameChange)
        Spacer(Modifier.padding(5.dp))
        TextField(
            label={ Text(text = "Phone number")}, 
            value = phone,
            onValueChange = onPhoneChange)
        Spacer(modifier = Modifier.padding(5.dp))
        Button(onClick = { println("Order generated for $name and phone $phone")},){
            Text(text = "Pay order")
        }
    }
}


在这个代码中,name和phone的状态控件被委托给text1(),因此ContactInformation1()不关心他的数据状态,可以被其他view重用。
text1()变为Stateful(有状态),ContactInformation1变为Stateless(无状态)。

//有状态视图
@Preview
@Composable
fun OrderSoreen(){
    //states name and phone
    var name by remember {mutableStateOf("")}
    var phone by remember { mutableStateOf("") }
    ContaotInformation(name=name, onNameChange = {name=it},phone=phone, onPhoneChange = {phone=it}){}
}
//无状态视图
@Composable
fun ContaotInformation(
    name:String,
    onNameChange:(String)->Unit,
    phone:String,
    onPhoneChange:(String)->Unit,
    payOrder:()->Unit) {
    
}

在上面代码中,控制的反转是通过高阶函数实现的,允许状态和操作的定义作为参数传递给ContaotInformation视图。

定义“真实数据源”,谁负责提供状态呢?

首先,让我们理清“真实数据源”这个词是什么?
真实数据源指得是提供视图需要呈现在屏幕上得数据得可靠来源,并且用户将与这些数据进行交互。
在我们得分析中,数据与状态密切相关。视图使用状态来接收完成其工作所需的信息(数据)。
在上图中,我们看到了如何在各自的视图中找到状态。这意味这上述图中的每个视图都是真实的数据源。甚至我们之前讨论过的UI TextField组件的变量名也可以是一个状态,因此,它也是一个事实数据源。

在一个视图层次中有这么多的真实源是否合理?

答案是不合理的。

建议将真实的数据源限制在单个组件(或者尽可能少),这样你就可以对流有更大哦的控制,并避免状态不一致。
拥有一个单一的,明确定义的真实数据源也有助于正确实现单向数据流设计模式,这是由声明性视图(如Compose或SwiftUI)推广的模式。

如何在我们的设计中减少真实数据源的数量呢?

这可以通过上面解释的状态提升技术减少有状态视图的数量,并将状态集中在一个视图中。一般来说,委托是层次基本最高的视图,即父视图。
如下图:只有一个真实数据源,那就是父视图。
一方面,子视图只负责传播与用户交互接收到的事件。另一方面,他们接收到渲染视图的状态(重组),以反映UI的变化。

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

除了将所有状态处理责任委托给一个视图,还有其他选择吗?

答案是肯定的。 

更好的选择是将这个责任委托给一个状态持有者或者承担这个角色的ViewModel。我们在下一节中看到更多的细节。

ViewModel作为真实数据源 为了防止视图被责任压得喘不过气来,另一个组件被召唤来处理状态管理。这个适当的元素就是我们熟知的ViewModel。 如下图所示,将状态从View移动到ViewModel可以创建责任分离,使得展示逻辑和其对状态的影响可以集中化。

将状态处理委托给一个ViewModel图:

 

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

尽管在实现中这个组件(ViewModel)是可选的,但我强烈建议使用它,因为它提供了许多优点,如有效管理数据和视图之间的生命周期。关于这个架构组件的更多信息,我建议查阅关于ViewModels的官方Google文档。视图和ViewModel之间的通信只包括两种类型的消息,事件和状态:

1.事件是由任何视图或子视图通知给ViewModel的动作,作为用户与UI组件交互的结果。

2.状态代表ViewModel交付给视图进行各自图形解释的信息(数据)。

3.ViewModel的主要功能是接收来自视图发送的事件,解释它们,应用业务逻辑,并将它们转化为状态,以便回传给视图。

4.视图的任务是接收由ViewModel发送的状态,并通过重组将它们转化为图形UI表示。

5.现在,对于每个组件的责任以及它们之间的消息有了更清晰的认识,让我们现在分析一下信息流的情况。


理解数据流,“单向数据流模式”

如果我们简化图上中的图,结果会使得下面的图:

单向数据流:

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

这是视图和ViewModel之间的循环消息。信息流只遵循一个方向,因此得名单向数据流模式。

可以将事件注入循环的外部因素是用户交互,如列表中的滚动,按钮上的点击,以及与其他应用层的交互,如来自仓库的响应或用户的响应,后台计时器,或者可能是推送通知的到达。

这个循环不能被中断,因为任何诱发的中断或延迟都会导致用户体验差。用户会感觉到应用程序慢,被阻塞,质量差。 因此,设计时应尽可能考虑以下规则:
-   定义视图的可组合项必须是幂等的和功能性的。
-   在视图端,不能有任何拖慢循环的任务。任何需要大量处理的任务都必须委托给ViewModel,它将通过反应式编程和Flow Coroutines异步执行这些任务。

现在你对数据流和View和ViewModel之间交换的消息有了更好的理解,那么一个合乎逻辑的问题是:

View和ViewModel之间的通信渠道是如何实现的?

我们接下来看看。

让我们连接View和ViewModel组件 如图所示,需要实现的两种类型的通信渠道已经清晰地标识出来。 

第一个通道是事件通道,方向是View –> ViewModel。 

对于这个实现,只需要ViewModel公开可以被View调用的公共操作,如下面的代码片段所示。

 //UI's Events

       fun onNameChange(): (String) -> Unit = {

        name = it

        }

        fun onPhoneChange(): (String) -> Unit = {

        phone = it

        }


第二个通道是状态通道,方向是 ViewModel –> View。

UI如何知道状态已经改变呢?

观察状态。要追踪状态,首先,ViewModel必须通过mutableStateOf组件将它们暴露给UI,如下所示:
 

   // UI's states

        var name by mutableStateOf("")

        private set

        var phone by mutableStateOf("")

        private set

mutableStateOf不仅允许将状态暴露给视图,而且还允许视图订阅以接收该状态的任何更改的通知。

让我们看看ViewModel和View(Composable)的完整实现

viewModel:

class OrderViewModel1: ViewModel() {
    //UI's states
    var name by mutableStateOf("")
    private set
    var phone by mutableStateOf("")
    private set

    //UI's Events
    fun onNameChange():(String) -> Unit ={
        name =it
    }

    fun onPhoneChange():(String)->Unit ={
        phone =it
    }

    fun payOrder():()->Unit ={
        println("Order generated for $name and phone $phone")
    }
}

View(Composables):

@Preview
@Composable
fun OrderScreen(viewModel:OrderViewModel1 = viewModel()){
    ContactInformation2(
        name = viewModel.name,
        onNameChange = viewModel.onNameChange(),
        phone = viewModel.phone,
        onPhoneChange = viewModel.onPhoneChange(),
        payOrder = viewModel.payOrder()
    )
}

@Composable
fun ContactInformation2(
    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){
            Text(text = "Pay order")
        }
    }
}

到目前为止,我们已经看到,像名字和电话这样的状态是一个字符串变量的表示;也就是说,状态代表一个原始变量。然而,我们可以将状态表示扩展到组件和屏幕。

在下一节中,我们将查看表示状态的其他选项。

被表示为状态的结构

在Compose和一般的声明式视图中,状态可以表示不同类型的UI结构:

由状态表示的结构:

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 


-   属性UI的状态:它们是以状态表示的原始变量。在图中,如名字、电话或地址等文本输入字段就是这种类型。
-   组件UI的状态:代表与组合相关联的UI元素的状态。例如,在OrderScreen上,一个叫做ContactInformationForm的组件可以组合所需的数据,如联系信息。这个组件可能有NameValueChanged、PhoneValueChanged和SuccessValidated的状态。
-   屏幕UI的状态:它代表与一个可以被视为绝对和独立状态的屏幕相关联的状态;例如,一个叫做OrderScreen的屏幕可能有以下状态:加载中、成功加载或加载失败。

现在,让我们看看在Android和Kotlin中存在哪些实现选项来定义这些状态。

属性UI的状态

它们是从原始类型变量(如String、Boolean、List或Int等)声明的状态。

如果它在ViewModel中声明(ViewModel作为真实数据源),其定义可能是这样的:

var name by mutableStateOf("")
private set

var phone by mutableStateOf("")
private set

var address by mutableStateOf("")
private set

var payEnable by mutableStateOf(false)
private set

如果它在View中声明(View作为真实数据源),它在Composable中的定义可能是这样的:

var name by remember { mutableStateOf("") }

  var phone by remember { mutableStateOf("") }

  var address by remember { mutableStateOf("") }

 var payEnable by remember { mutableStateOf(false) }

remember是一个Composable,它允许你在重新组合时临时保持变量的状态。因为它是一个Composable,所以这个属性只能在声明式视图中定义,也就是在Composable函数中。

请始终记住,要通过"by"关键字使用委托,你需要导入:

import androidx.compose.runtime.getValue 
import androidx.compose.runtime.setValue

在前面的例子中,我们只讨论了通过使用mutableStateOf组件来表示属性或变量的状态。

然而,也可能数据流可以被表示为状态并被Composables观察。这些额外的选项与Flow、LiveData或RxJava有关。在“实现‘特性’”中,我们将看到使用StateFlow的几个例子。

组件UI的状态

当你有一组相互关联的UI元素时,他们的状态可以被组织到一个单一的结构或者UI组件和一个单一的状态中。

例如,在前面的图中,元素User name、Phone number、Address,甚至Pay Order按钮可以被组织到一个单一的UI组件中,并且其状态在一个叫做FormUiState的单一状态中表示。

// 定义 FormUiState 数据类
data class FormUiState(
    val nameValueChanged: String = "",
    val phoneValueChanged: String = "",
    val addressValueChanged: String = ""
)

// 定义 FormUiState 的扩展属性
val FormUiState.successValidated: Boolean
    get() = nameValueChanged.length > 1 && phoneValueChanged.length > 3


在这种情况下,将多个状态建模到一个合并的状态类中效果非常好,因为这些变量是相关的,甚至定义了其他变量的值。例如,这就发生在 `successValidated` 变量上,它依赖于 `nameValueChanged` 和 `phoneValueChanged` 变量。

合并状态对实现带来了好处,集中了控制,整理了代码。这将是我们在实现中最常用的技术。

**屏幕UI的状态**

如果需要建模的状态可以是独立的,并且是同一家族的一部分,你可以使用以下定义:

sealed class OrderScreenUiState {
    data class Success(val order:Order):OrderScreenUiState()
    data class Failed(val message:String):OrderScreenUiState()
    object Loading:OrderScreenUiState()
}


这种实现方式适合处理绝对和排他的状态;你有一个状态或另一个状态,但不会同时有两种状态。

通常,像OnboardignScreen或ResultScreen这样的简单屏幕可以用这些状态进行建模。

当屏幕更复杂并且包含许多独立操作且有多种关系的UI元素时,建议优先选择使用属性UI状态和组件UI状态技术来定义状态

建模和分组事件

回到OrderScreen的例子,我们现在将看一下如何建模Events,并如何类似于States地将它们分组。

考虑一个下图所示的屏幕:

多次事件:

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

ViewModel向视图暴露四个操作(事件),每个操作被一个视图UI元素使用。

分析这四个事件,它们与输入用户联系信息的表单相关,所以将它们分组到一个事件类型中是有意义的,如下图所示:

 组合事件:

为Android构建现代应用——设计原则,Android Jetpack Compose 别裁,android jetpack

 

表示不同类型事件的实现可能是这样的:

sealed class ContactFormEvent {
    data class onNameChange(val name:String):FormUiEvent()
    data class onPhoneChange(val phone:String):FormUiEvent()
    data class onAddressChange(val address:String):FormUiEvent()
    object PayOrder:FormUiEvent()
}


最后,你不必在简化状态或事件时过于严格。需要分析每种用法的优点和缺点,并做出相应的决策。

对于那些相关的UI组件,将它们分组是很有意义的;一些其他的横切元素将更健康地保持它们的独立性。

总结

在这第一章中,我们回顾了在现代Android应用开发中使用的主要概念。

像状态和事件,状态提升,真实数据源,和单向数据流这样的概念在实现Jetpack Compose,ViewModels,和其他可用于Android的架构组件之前是必须理解的。这就是为什么我们在这第一章就开始讲这些概念的原因。

在接下来的章节中,我们进入移动应用中的架构和设计的定义,为此我们将使用本章介绍的概念作为参考。

稍后,我们将使用电子商务作为概念来实现一个名为“Order Now”的移动应用。这个应用将具有电子商务的主要部分,如购物车,产品列表,和结账过程。

这项工作将引导读者接触到接近真实和生产应用的设计和开发经验。

但首先,我们将应用这一章学到的概念来实现一个简单的表单。

这将是下一章所描述的主题。文章来源地址https://www.toymoban.com/news/detail-600439.html

到了这里,关于为Android构建现代应用——设计原则的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 如何是Jetpack Compose构建漂亮的应用程序

    Jetpack compose 是在 Android 上构建 UI 的未来。 如果您完全不熟悉 android 并且不知道 Jetpack Compose 是什么——它基本上是一种构建本机用户界面的新方法。 Jetpack compose官方站点 https://developer.android.com/jetpack/compose 在本文中,您将了解如何使用 Jetpack Compose 遵循最佳实践进行 UI 开发

    2024年02月03日
    浏览(66)
  • 为Android构建现代应用——应用架构

    选择风格( Choosing a style ) 我们将依照Google在《应用架构指南》中推荐的最佳实践和架构指南来构建OrderNow的架构。 这些定义包括通过各层定义组件的一些Clean Architecture原则。 层次的定义( Definition of the layers ) 在应用程序中,我们将定义以下主要层次: • 用户界面(UI)层 •

    2024年02月15日
    浏览(33)
  • 为Android构建现代应用——主体结构

    创建Screents和ViewModels 在前面的章节中,我们已经分析了OrderNow项目的理论概念和我们将赋予的组织。 在本章中,我们将开始实现初始结构和模板,这将联接每一个应用程序的部分。 首先将添加以下带有各自视图模型的主屏幕: • 首页 • 产品列表 • 产品详情 • 购物车 添

    2024年02月15日
    浏览(28)
  • Jetpack Compose: Hello Android

    Jetpack Compose 是一个现代化的工具包,用于使用声明式方法构建原生 Android UI。在本博文中,我们将深入了解一个基本的 “Hello Android” 示例,以帮助您开始使用 Jetpack Compose。我们将探讨所提供代码片段中使用的函数和注解。 在深入代码之前,请确保您已经准备好使用 Jetpac

    2024年03月10日
    浏览(43)
  • Android Jetpack Compose — Slider滑动条

            在Android Jetpack Compose中,Slider(滑动条)是一个常用的用户界面控件,它允许通过滑动条来选择一个范围或数值。Slider控件非常适用于调整音量、亮度、进度等需要连续调整的场景。 一、Slider的属性         Slider是Android Jetpack Compose中的一个控件,用于实现滑动条

    2024年02月11日
    浏览(31)
  • 探索Android Jetpack Compose的Surface组件

    随着声明性 UI 框架 Jetpack Compose 的出现,Android 开发变得更加简洁和直观。在这篇博客中,我们将深入探讨其中的一项基本构建块 —— Surface 组件,了解它如何影响 UI 的显示和设计。 一、Jetpack Compose和Surface组件 二、Surface组件的基本使用 三、影响Surface的属性 一、Jetpack Co

    2024年02月11日
    浏览(39)
  • Android Jetpack Compose之RadioGroup的使用

    Android Jetpack Compose是一个现代化的UI工具包,帮助开发者以声明式的方式构建出美观且功能强大的Android应用。在本文中,我们将详细介绍其中的一个重要组件—— RadioGroup 。 一. RadioGroup简介 Jetpack Compose中并没有像传统View系统中那样直接提供 RadioGroup ,但我们可以很方便地通

    2024年02月06日
    浏览(40)
  • Android Jetpack Compose中使用字段验证的方法

    数据验证是创建健壮且用户友好的Android应用程序的关键部分。随着现代UI工具包Jetpack Compose的引入,处理字段验证变得更加高效和直观。在这篇文章中,我们将探讨如何在Android应用中使用Jetpack Compose进行字段验证。 字段验证是确保用户在各种输入字段中输入的数据符合特定

    2024年02月11日
    浏览(32)
  • Android Jetpack Compose实现轮播图效果

    在最近思索如何使用Compose方式改进我的开源TMDB电影列表应用程序的主屏幕时,一个激动人心的概念浮现在我的脑海中——为什么不整合一个吸引人的轮播图来展示即将上映的电影呢?在本文中,我将分享我的开发和实现自定义轮播图的经历,提供涉及不同步骤的见解。 首先

    2024年02月08日
    浏览(82)
  • 对于Android开发,我们为何要学Jetpack Compose?

    Jetpack Compose 是用于构建原生 Android 界面的新工具包。它可简化并加快 Android 上的界面开发,使用更少的代码、强大的工具和直观的 Kotlin API,快速让应用生动而精彩。Compose 使用全新的组件——可组合项 (Composable) 来布局界面,使用修饰符 (Modifier) 来配置可组合项。 为何Jetp

    2024年02月10日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包