最近我们广大程序猿喜提官媒认证:新一代农民工!想不到前几年戏称的“码农”,如今竟一语成谶,哈哈哈!我觉得这是好事,维权啥的更方便了不是么?毕竟农民工工资不能拖欠啊是不是?而且还给我们定性了,咱是地地道道的工人阶级啊!以后可以把上班改称为上工了,各位工友,你们说是不是?
在前一篇笔记中,我们知道了 Compose 布局的一些基本知识,这篇笔记就来详细看看 Compose 布局吧!还有些 Compose 其他的知识,根据官方的实例,我们边看边说。
Android 目前的布局 Layout 有许多:LinearLayout 线性布局、RelativeLayout 相对布局、ConstraintLayout 约束布局、FrameLayout 帧布局、TableLayout 表格布局、AbsoluteLayout 绝对布局、GridLayout 网格布局 7 种。后面的几种基本上用的很少了,而 Compose 的布局方式总共有三种:Column 纵向排列布局、Row 横向排列布局、Box 堆叠排列布局。先来个简单的例子:
图 1
注意到在展示第二行文本的时候,外面包了一层 CompositionLocalProvider 方法,这个是干嘛用的?要想知道这个,就必须先知道 CompositionLocal 是什么了。
但是这两种方式都不太优雅,尤其是嵌套过深,或者数据比较敏感,不想暴露给中间层的函数时,这种情况下,就可以使用 CompositionLocal 来隐式的将数据传递给所需的 composition 树节点。
CompositionLocal 在本质上就是分层的,它可以将数据限定在以某个 Composable 作为根结点的子树中,而且数据默认会向下传递,当然,当前子树中的某个 Composable 函数可以对该 CompositionLocal 的数据进行覆盖,从而使得新值会在这个 Composable 层级中继续向下传递。举个栗子:
图 2 CompositionLocal 改值
再说回布局,上面只用到 Column,可以将元素纵向排列;Row 则可以将元素横向进行排列。在官方栗子中还用到了 Surface。
图 3
图 4
在这里实现了一个带边框圆角和阴影的按钮。Surface 的功能主要有:
Modifier 属性用法太多了,设置 padding、click 等等,布局排版的许多工作都是由它来完成的。
图 5
细心的同学发现了,modifier 只能设置 padding,没有 margin 属性。在 clickable 前后各有一个 padding,前者就是设置的外边距,后者就是内边距。所以,在 Modifier 中设置 padding 的次序很重要。
Compose 自带 Material 组件用于快速开发一个符合 Material Design 标准的 APP,最顶端的组件是 Scaffold,咦?是不是又看到了 Flutter 的影子?
不得不说,Google 的工程师真的很了解建筑学,连起名都借用了建筑学的概念,这个 Scaffold 组件的功能就跟它的翻译一样,用于构建一个基本的 Material Design 布局框架。它提供了诸如 TopAppBar、BottomAppBar、FloatingActionButton 和 Drawer 等常见的组件。
图 6
可以看出,Scaffold 真的为我们提供了好多组件,这里仅仅举了 TopAppBar 和 BottomNavigation 两个。但在实际中,我们用到的并不多,除非是需要快速上线,没有 UI 设计等等。所以我个人感觉,Scaffold 并不是我们应该掌握的重点,了解即可。
图 7
这种实现方法最简单,但是会在页面开始展示时,将列表中所有的 item 加载到内存中,虽然很多 item 都没有显示在屏幕上,这种方法当列表内容很多时,会出现内存占用大的问题。
图 8
列表项比较简单,就一张图一个文案,这里图片加载库使用的是 Coil,使用 Kotlin 协程写的一个图片加载库,感兴趣的可以看看。需要引入 Coil 的依赖:
引入之后就可以使用 code 8 中的 rememberImagePainter 直接将图片链接传给 data 即可。还要记得获取一下网络权限。还可以看到这里图片与文案之间的间隔是用 Spacer 来实现的,当然也可以在 Text 中的 Modifier 属性设置 padding 来实现。
众所周知,Android View 体系中官方最推荐的布局是约束布局 —— ConstraintLayout,以致于在默认新建布局时就给你初始化成 ConstraintLayout。当然,ConstraintLayout 确实可以解决 View 体系中多层嵌套的问题,那么在 Compose 中也可以使用吗?
答案是肯定的。Compose 中也可以使用 ConstraintLayout,是使用 Row、Column、Box 布局的另一种解决方案。在实现更大的布局以及有许多复杂对齐要求以及布局嵌套过深的场景下,ConstraintLayout 用起来更加顺手。使用前,得引入 Compose 中的 ConstraintLayout 依赖库:
在 Compose 中使用 ConstraintLayout 有几点需要注意的:
下面是一个简单的例子:
图 9
细心的同学可能会有疑问,为啥下面的 Text 设置了父布局水平居中,但是好像是在 Button 宽度的中间呢?这是因为父布局的 ConstraintLayout 的大小默认是尽量小的容纳它的子元素,这跟 wrap_content 一样。可以将开发者选项中的显示布局边界打开看看:
图 10
当然,Compose 版本的 ConstraintLayout 也支持设置使用 guideline、barrier、chain 等。
先来看看 Barrier 的用法,就是字面意思,给一些子元素设置栅栏,将栅栏两侧的子元素分隔开的作用:
创建栅栏的函数不仅有 createEndBarrier() 方法,类似用法的总结起来有:
Compose 版本中的 Guideline 用法大同小异,还是先从例子说起:
图 12
guideline1 设置的是在父布局水平位置 50% 的地方,这里由于 ConstraintLayout 默认尺寸是 wrap_content,所以父布局的宽度会设置为 text 的两倍的宽度,这样就满足了 text 起始位置在父布局的中间,根据图中的布局分界线也可以看出。而 guideline2 是在竖直方向上距离屏幕高度三分之一的位置,需要把父布局的高度设置为屏幕高度才可以实现。
上面的例子只列举了 guideline 根据百分比来设置它的位置,其实也可以根据偏移量来设置。用法总结起来如下列所示:
看着挺多,其实就是上下左右加上国际化的情况。
ConstraintLayout 还有一个特性,就是当它的子元素过大时,ConstraintLayout 默认是可以允许子元素超出屏幕范围的,以上面的例子继续说,当横向的 Text 内容很多时,就会出现 Text 部分内容超出屏幕。。。。上代码:
图 13
图 14
OK, 这个 Dimension 的属性一共有五种:
Chain 链,与 xml 中的用法一样,就是将一系列子元素按顺序打包成一行或一列。官方将这个 api 标记为可以改进的状态,可能后续会发生变化。api 只有两个,创建横向和纵向的链:
第一个参数是需要打包在一起的所有子元素的id,第二个参数是链的类型,目前有三种类型:
代码及效果如下:
图15 Spread 效果
图16 SpreadInside 效果
图17 Packed 效果
上面谈论的都是静态设置各种约束布局的情况,没有考虑到横竖屏切换可能导致的布局适配问题。其实 ConstraintLayout 可以传入一个 ConstraintSet 类型的参数,根据这个参数可以设置不同的约束条件,可以进行灵活设置。
这里横竖屏的布局有所不同,就是通过设置不同的 ConstraintSet 来实现的,如果布局元素很多,可以分为两个 ConstraintSet 对象来分别设置。需要注意的是,ConstraintLayout 中子元素的 layoutId 是通过 Modifier 设置的,需要与 ConstraintSet 的 createRefFor 的参数保持一致。下面是横竖屏的显示效果:
图 18 竖屏显示
图 19 横屏显示
第二篇 Compose 学习笔记终于完成,Compose 的布局你学会了么?