Jetpack之Compose修炼(一)

881 阅读7分钟

1. 简介

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

image.png

任何的新技术的出现都有其需要解决的问题,Compose当然也不例外

Compose主要解决的问题就是原生Android的UI开发,随着近年大前端趋势的愈演愈烈,React的声明式UI也深入人心,推动了新一代UI的变革,Compose在设计方面对此也多有借鉴

image.png

Jetpack Compose官方主页

1.1. 特点

从官方给出的文档来看,主要有以下几个特点:

  • 更少的代码
  • 直观
  • 加速开发
  • 功能强大

在后续的开发中将会逐渐体会到它所带来的好处

更少的代码

主要是现在提倡低代码开发,Compose在代码方面进行了改进,相对于传统的XML布局、View的继承和对应的命令式操作,Compose只需要开发者关注UI的描述,并且使用了更加简洁的语法用以描述UI

直观

相对于XML的布局形式,Compose增加了代码的可读性,直接用函数描述界面的结构

加速开发

Compose提倡使用组合,这一点也与React当中的组件思想有异曲同工之妙,可以增强代码的复用性,同时加速开发的效率

功能强大

Compose的代码能够与原来传统的布局兼容,并且兼容到API 21,对各常用三方库都兼容,稳定兼容正是强大的一方面

2. 命令式UI与声明式UI

如果之前有接触过React,可能对这二者的区别已经有所了解了
Android在显示界面方面的流程其实也和JS一样,显示找到对应的UI元素,然后进行相应的操作,比如设置属性,获取内容之类的,这便是命令式UI

在Android中,我们通过XML或者new出View类型的对象生成UI布局之后,会调用findViewById()等方法去找到布局里对应的元素,然后通过返回的对象执行操作 这种场景就像是找人办事,分为找、做两个步骤,看上去其实还好,但是当你需要找的人多了,问题就来了 —— 我要找100个人,每个人有不同的任务(写代码的毕竟是人,情况复杂了犯错的概率也就上升了),此时不仅代码量大大增加,出现问题的风险也增加了,同时还会有可读性的问题

而声明式UI相对更简单,只需要搭建好UI的描述,其他的数据处理交给专门的逻辑,如果打个比方的话,这就像是大型公司的架构,将公司的结构组织好,等到业务来的时候,从上层传递下去,由对应部门负责处理,相当于早已摆好了架势,一方面也是一种大局观的体现

3. 设计原则

3.1. 一切皆为函数

在Compose当中,组件是Composable函数,通过@Composable注解,将函数作为组件,相互直接可以进行组合,并且Composable函数只能在Composable函数中被调用

并且,在Compose当中,不包含类的概念,因而所有组件都是Kotlin中所谓的顶层函数

3.2. 组合优于继承

继承带来方便的同时,也引起了一系列问题,比如说代码功能的冗余,可读性变差等
在Android中有各种各样的View,它们都直接或间接继承了View,这也就导致有些东西也许用不到,但还是硬生生地继承过来了

Compose是希望通过使用组件进行组合,像搭积木一样,把需要的组合到一起,拼出想要的,一方面能够复用,另一方面也是减少了无用的部分

3.3. 单一数据源

单一数据源的原则声明式UI的重要原则
简单来讲就是组件UI的变化需要归纳为一个原因,这样才能确保UI状态的可控,否则会有更多的同步开销以及产生数据问题的风险

在UI交互方面,数据总是保持自上而下的流动,而事件则是自下而上的传递

4. 环境搭建

Android官网的Android Studio下载

主要就是需要版本比较新的Android Studio,现在Android官网上能够下载到的已经是可以支持的了,官网上还附带有Compose开发的样例图片

image.png

点击下载Android Studio,安装步骤对应的说明在当前页面的下方也可以找到,“Install Android Studio”就是对应的安装说明,点击跳转便可以查看,里面也有对应的指导视频,过程这里就不赘述了

image.png

Android Studio的安装说明

image.png

5. 第一个Compose应用

5.1. 创建

直接打开Android Studio,从头开始创建一个Compose项目
新建项目的时候,从提供的项目模板中选择“Empty Compose Activity”,这便是Compose项目的模版,然后选择“Next”下一步

image.png

接着简单填写一下项目的信息,“Finish”完成项目的创建

image.png

等待项目创建完成后,可以看到,Android Studio为我们创建了一些模板代码,默认生成一个MainActivity,继承自ComponentActivity,右侧是预览页面

image.png

其他先不做,现在先直接运行一下

image.png

这便是Compose应用的一个HelloWorld Demo,正如各个技术入门的传统一般,接下来对其中的代码进行分析

5.2. 分析

学习要知其然,知其所以然,在学习运用后需要进一步深入分析原理,才能力求精进
先前只是对已有的程序进行简单的执行,下面对于代码的逻辑进行简要的分析,了解为何能够在屏幕上呈现“Hello Android!”的结果

正如刚刚所看到的,代码主要集中在Android Studio为我们创建的MainActivity.kt当中,除去顶部的包名以及包导入,其他内容如下所示:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeDemoTheme {
                // A surface container using the 'background' color from the theme
                Surface(color = MaterialTheme.colors.background) {
                    Greeting("Android")
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeDemoTheme {
        Greeting("Android")
    }
}

Composable组件

onCreate()是Activity的生命周期回调,除此以外,下面有两个方法,它们都带有@Composable注解,这两个方法就是Composable组件,代表两个UI控件

我们的项目中并没有加载XML布局文件,setContent{}正是通过这种名为Composable组件的方法加载的布局

Greeting()这个方法能够传入参数,name便是传入的参数,然后内部调用了Text()方法,将字符串拼接后的结果再传给Text(),其实Text()也是一个Composable组件,如同原本的TextView一样,用于显示文本字符串,因此Greeting简单封装了一层,或者说是组合了文本控件的功能,使其根据传进来的name参数显示文本,Greeting("Android")自然就输出了“Hello, Android!”

另外,Composable组件都是顶层函数,也就是单独声明在Kotlin文件的最外层的函数,这样可以任意进行调用,并且Composable函数只能被Composable函数调用,这就形成了树形的调用结构,在这里扮演树根的就是setContent{}块,setContent{}接受Composable组件作为内容

image.png

组件预览

涉及UI界面的开发埋着头盲写可不行,需要随时预览开发的效果才能更加高效精准

在DefaultPreview()这个Compose组件上还有一个@Preview注解,这便是用来预览组件的
在Composable组件有参数同时又没有给默认值的情况下是没有办法进行预览的

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeDemoTheme {
        Greeting("Android")
    }
}

给Composable加上@Preview注解后,打开右侧的预览面板便可以预览对应的组件了

image.png

对于页面的修改后需要编译,重新刷新预览,确保显示正确

image.png

如果有多个@Preview标注的Composable组件,可以同时预览,更换@Preview的参数在UI适配的时候会更加有效率

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    ComposeDemoTheme {
        Greeting("Android")
    }
}

@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun DefaultPreviewNight() {
    ComposeDemoTheme {
        Greeting("Android")
    }
}
image.png