android---Jetpack Compose 学习001

Compose 是什么

Jetpack Compose 是用于构建原生 Android 界面的新工具包。它可简化并加快 Android 上的界面开发,帮助您使用更少的代码、强大的工具和直观的 Kotlin API,快速打造生动而精彩的应用。

Compose 的优势

1. 更少的代码。使用更少的代码实现更多的功能,并且可以避免各自 bug,从而使代码简洁且易于维护。

2. 直观。你只需要描述界面,Compose 会负责处理剩余的工作。应用状态变化时,界面会自动更新。

3. 加快应用开发。兼容现有的所有代码,方便你随时随地采用。借助实时预览 和全面的 Android Studio 支持,实现快速迭代。

4. 功能强大。凭借对 Android 平台 API 的直接访问和对于 Material Design、深色主题、动画等的内置 支持,创建精美的应用。

组合函数(Composable)和预览(Preview)

Jetpack Compose 是围绕可组合函数构建的。这些函数可让你以程序化方式定义应用的界面,只需描述应用界面的外观并提供数据依赖项,而不必关注界面的构建过程(初始化元素,将其附加到父项等)。如需创建可组合函数,只需将@Composable 注解添加到函数名称中即可。

示例:创建一个 Jetpack Compose 项目。new --> new project --> Empty Compose Activity

布局

在 Compose 中,你可以通过从可组合函数中调用其他可组合函数来构建界面层次结构。

示例:我们有两个文本,希望它能够上下排列

针对上面的问题,我们就可以使用标准布局 Column。使用 Column 垂直排列元素。

此外,使用 Row 水平排列元素,使用 Box 堆叠元素。

布局配置

为了装饰或配置可组合项,Compose 使用了修饰符。通过修饰符,你可以更改可组合项的大小、布局、外观,还可以添加高级互动,例如使元素可点击。你可以将这些修饰符连接起来,以创建更丰富的可组合项。

Material Design

Compose 旨在支持 Material Design 原则。它的许多界面元素都原生支持 Material Design。Material Design 是围绕三大要素构建的:颜色排版形状

启用深色主题。你可以启动深色主题(或 夜间模式),以避免显示屏过亮(尤其是在夜间),或者是节省设备电量。由于支持 Material Design,Jetpack Compose 默认能够处理深色主题。使用 Material 颜色、文本和背景时,系统会自动适应深色背景

我们在创建项目时,编译器自动帮我们生成了深色和亮色的主题。可以直接使用

列表与状态

Compose 可以轻松创建列表并添加有趣的动画效果。

\bullet 创建消息列表

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetpackCompose001Theme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    //MessageCard(Message("Android", "Jetpack Compose"))
                    // 展示消息
                    Conversation(messages = MessageData.conversationSample)
                }
            }

        }
    }

    /**
     * TODO MessageCard 表示一个消息条目,即 item
     */
    @Composable
    fun MessageCard(msg : Message){
        Row(modifier = Modifier
            .padding(all = 8.dp)
            .background(MaterialTheme.colors.background)) {
            Image(
                painter = painterResource(id = R.drawable.img),
                contentDescription = null,
                modifier = Modifier
                    .size(40.dp) // 设置图片的尺寸
                    .clip(CircleShape) // 裁剪图片

            )
            // TODO 用于设置两个控件,它们之间的间距;width:左右间距;height:上下间距
            Spacer(modifier = Modifier.width(8.dp))
            //TODO remember: 给 Composable 函数开辟一个私有的空间,专门存储Composable 里的变量,即这里的 isExpanded
            // isExpanded 用来控制 item 的展开和关闭。点击一下展开,再点击一下则关闭
            var isExpanded by remember { mutableStateOf(false)}
            // TODO 当我们点击这个 Column 时,改变 isExpanded 的值
            Column(
                modifier = Modifier.clickable { isExpanded = !isExpanded }
            ){
                Text(
                    text = msg.author,
                    color = MaterialTheme.colors.secondaryVariant // 设置字体颜色
                )
                Spacer(modifier = Modifier.height(4.dp))
                Text(
                    text = msg.body,
                    modifier = Modifier.padding(all = 4.dp), // 填充间距
                    style = MaterialTheme.typography.body2, // 设置字体
                    //maxLines = 1 // 默认显示一行
                maxLines = if(isExpanded) Int.MAX_VALUE else 1 //如果是展开,则全部显示;如果是关闭,显示1行
                )
            }
        }
    }

    /**
     * 创建一个消息列表
     *TODO 里面包含很多的 MessageCard
     */
    @Composable
    fun Conversation(messages : List<Message>){
        // 消息列表:LazyColumn 是一个带缓存的列表
        LazyColumn{
            // items 指定消息集合;
            // TODO 它是一个函数。第一个参数是 messages,表示消息集合。第二个参数是 lambda 表达式,表示每一个item
            items(messages){ message ->
                // 每一个 item 就是 MessageCard
                MessageCard(msg = message)
            }
        }

    }

}

data class Message(val author : String, val body : String)

\bullet 在展开消息时显示动画效果

主要修改 MessageCard() 方法

效果展示

 Compose 所解决的问题

在编写可维护的软件时,我们的目标是最大程度地减少耦合并增加内聚。要增加内聚,就要尽可能的将相关的代码组织在一起,以便我们可以轻松地维护它们,并方便我们随着应用规模的增长而扩展我们的代码,这个称之为关注点分离

我们在当前 Android 开发的上下文中进行更为实际的操作,并以视图模型(viewmodel)和 xml 布局为例:viewmodel 与 layout 之间经常有一些耦合

事实证明,这里隐藏了很多依赖关系:viewmodel 与 xml 存在许多耦合。例如,findViewById 拿到控件然后去操作它,使用这些 API 需要对 XML 布局的形式和内容有一定了解,由于应用规模会随着时间增长,我们还必须保证这些依赖不会过时。这种情况下,耦合度就比较高

通常,viewmodel 会使用像 kotlin 这一的编程语言进行定义,而布局则使用 XML。由于这两种语言的差异,使得它们之间存在一条强制的分割线。然而即使存在这种情况,视图模型与布局 XML 还是可以关联得十分紧密

如果我们开始用相同的语言定义布局和 UI 结构会怎么样?如果我们选用 Kotlin 来做这件事会怎么样?

由于我们可以使用相同的语言,一些以往隐式的依赖关系可能会变得更加明显。我们也可以重构代码并将其移动至那些可以使它们减少耦合和增加内聚的位置。

 你肯恶搞会以为这是建议你将逻辑与 UI 混合起来,不过现实的情况是,无论你如何组织架构,你的应用中都将出现与 UI 相关联的逻辑。架构本身并不会改变这一点。

例如下面这段代码,

    /**
     *TODO 里面包含很多的 MessageCard
     */
    @Composable
    fun Conversation(messages : List<Message>){
        // 消息列表:LazyColumn 是一个带缓存的列表
        LazyColumn{
            // items 指定消息集合;
            // TODO 它是一个函数。第一个参数是 messages,表示消息集合。第二个参数是 lambda 表达式,表示每一个item
            items(messages){ message ->
                // 每一个 item 就是 MessageCard
                MessageCard(msg = message)
            }
        }
    }

如果是按照以前 xml 那种写法,我们首先需要去 xml 里搞一个 RecyclerView,然后又跑到 java 代码里搞一个适配器,适配器里又要放一个 item 的布局,这又是在操作 xml。所以,逻辑就在业务代码和布局文件里穿插。如果是相同的语言,比如这里。我们就可以将两者写到一起,实现轻度耦合。

不过框架可以为你提供一些工具,从而帮你更加简单地实现关注点分离:这一工具便是 Composable 函数,长久以来你在代码的其它地方实现关注点分离所使用的方法,你在进行这里重构以及编写简洁、可靠、可维护的代码时所获得的技巧,都可以应用在 Composable 函数上。

Composable 函数剖析

在示例中,App 函数从 AppData 类接收数据作为参数。我们便可以使用任何 Kotlin 代码来获取这一数据,并利用它来描述我们的层级结构,例如 Header() 与 Body() 调用。

@Composable
fun App(appData : AppData){
    val derivedData= compute(appData)
    Header()
    if(appData.isOwner){
        EditButton()
    }
    Body{
        for(item in derivedData.items){
            Item(item)
        }
      }
   }

声明式UI

“声明式”是一个流行词,但也是一个很重要的字眼。当我们谈论声明式编程时,我们谈论的是与命令式相反的编程方式。让我们来看个示例:

假设有一个带有未读消息图标的电子邮件应用。如果没有消息,应用会绘制一个空信封;如果有一些消息,应用会在信封中绘制一些纸张;而如果有 100 条消息,我们就把图标绘制成好像在着火的样子....

在这段代码中,我们接收新的数量并且必须搞清楚如何更新当前的 UI 来反映对应的状态。

使用声明式完成上述案例。这里我们定义:1. 当数量大于 99 时,显示火焰;2. 当数量大于 0 时,显示纸张;当数量大于 0 时,绘制数量气泡。

声明式的含义

我们编写代码来按我们的想法描述 UI,而不是如何转换到对应的状态。这里的关键是,编写像这样的声明式代码时,你不需要关注你的 UI 在先前是什么状态,而只需要指定当前应当处于的状态。框架控制着如何从一个状态转到其他状态,所以我们不再需要考虑它。

组合VS继承

在软件开发领域,Composition(组合)指的是多个简单的代码单元如何结合到一起,从而构成更为复杂的代码单元。在面向对象编程模型中,最常见的组合形式之一便是基于类的继承。在 Jetpack Compose 的世界中,由于我们使用函数代替了类型,因此实现组合的方法颇为不同,但相比于继承也拥有许多优点。

a. 继承

假设我们有一个视图,并且我们想要添加一个输入。在继承模型中,我们的代码可能会像下面这样:

View 是基类,ValidatedInput 使用了  Input 的子类。为了验证日期,DateInput 使用了 ValidatedInput 的子类。但是 接下来挑战来了:我们要创建一个日期范围的输入,这意味着需要验证两个日期--开始和结束日期。你可以继承 DateInput,但是你无法执行两次,这便是继承的限制:我们只能继承自一个父类

b. 组合

在 Compose 中,这个问题就变得很简单。假设我们从一个基础的 Input Composable 函数开始:

然后我们创建一个 ValidatedInput,只需要在方法体中调用 Input 即可。我们随后可以对其进行装饰以实现验证逻辑:

接下来,对于 DateInput,我们可以直接调用 ValidatedInput:

现在,当我们实现日期范围输入时,这里不再有任何挑战:只需要调用两次即可。如下实例:

在 Compose 的组合模型中,我们不再有单个父类的限制,这样一来便解决了我们在继承模型中所遇到的问题。

重组

“重组”指的是任何 Composable 函数在任何时候都可以被重新调用。如果你有一个庞大的 Composable 层级结构,当你的层级中的某一部分发生改变时,你不希望重新计算整个层级结构。所以 Composable 函数是可重启动的,你可以利用这一特性来实现一些强大的功能。

总结

Compose 提供了一种现代的方法来定义你的 UI,这使你可以有效地实现关注点分离。由于 Composable 函数与普通 Kotlin 函数很相似,因此你使用 Compose 编写和重构 UI 所使用的工具与你进行 Android 开发的知识储备和所使用的工具将会无缝衔接。

猜你喜欢

转载自blog.csdn.net/qq_44950283/article/details/135188416