前语
相较于传统的XML布局构建办法,Compose旨在运用更少的代码完结一种新式的布局办法,为了投合Google官方的首选开发言语Kotlin,Compose只能运用Kotlin言语开发,从《Compose编程思维》这个专题开端,凭借Google官方开发文档,带领大家从Compose入门到高手的进阶。
1 Compose入门
1.1 声明式UI
假设对flutter有了解,或许有开发经验的同伴,关于声明式UI或许就十分熟悉,它的中心思维在于运用代码(Kotlin或许Dart)完结布局细节,并且不需求手动更新UI,什么意思呢?
先从传统的Java完结办法,布局文件是在xml布局文件中声明,如下:
<TextView
android:id="@+id/tv_ui"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UI制作"/>
通过android:text
特点可以预先设置文本展现,也可以通过findViewById的办法获取TextView实例目标,通过动态设置案牍的办法刷新页面。
val view = findViewById<TextView>(R.id.tv_ui)
view.text = "动态修正";
而声明式UI(以Compose为例)则是通过可组合函数构建UI,如下:
@Composable
fun showText() {
val name by remember { mutableStateOf("Alex") }
Text(text = name)
}
例如咱们要展现文本案牍,那么可以运用Text
组件,类似于Java中的TextView
,例如name
设置为Text
需求展现的案牍,当name
的值发生改变的时分,Text
展现的案牍也会发生改变,可是这个改变是由于值发生改变然后导致组件自动刷新,而不是Java中手动设置值(即自动调用setText办法)刷新。
1.2 Compose组件思维
一个app基本的元素,便是文本和图片,在Compose中对应的组件为Text
和Image
,为什么不必TextView
或许ImageView
这种咱们常见的Android渠道中的控件命名办法?
其实这儿就涉及到了Compose组件的思维 – 独立于Android渠道。独立于Android渠道并不意味着脱离了Android渠道,像Text
最终底层仍是调用了Android原生的drawText
和drawTextRun
函数,可是并没有运用任何Android中的组件,这样做的优点便是可以完结多渠道(multi-platform)。
多渠道,意味着可以在桌面版(Windows、linux)、WEB、IOS中运用同一套代码,只需求区分渠道即可。
因此Compose开端对一切的组件起新的名字,Text
对应TextView
,Image
对应ImageView
,RecyclerView
对应LazyColumn
等等,与传统的Android组件并没有任何的联系,可是底层仍然采用了Android原生的API,为什么Compose还要运用Android原生的API,是由于Compose需求跟原生的View交互,由于它绕不开原生。
假设不想跟原生的View有一丝的牵连,那么便是Flutter了,它是直接在NDK的层面与Skia渲染器打交道了,已然深入了底层,自然跟Android原生组件没有任何联系了。
所以总结一下,Compose组件的思维便是绕开了原生的Android组件,直接调用Android底层API渲染,并没有完全脱离原生。
1.3 Compose中那些原生布局的平替
在Android的原生布局中,常用的有束缚布局(ConstrainLayout)、线性布局(LinearLayout)、帧布局(FrameLayout)、ScrollView、RecyclerView、ViewPager。
那么在Compose中对应的组件是什么呢?
- 帧布局 – Box
@Composable
fun showLayout() {
Box {
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.size(100.dp)
)
Text(text = "Alex")
}
}
- 线性布局 – Column / Row
@Composable
fun showLayout() {
Column {
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.size(100.dp)
)
Text(text = "Alex")
}
}
Column
是vertical
特点;Row
是horizontal
特点。
- 相对布局 – Box
这儿需求提一点的便是,相对布局在传统UI中用于定位子View的相对方位,以及View之间的方位联系,在Compose中仍然可以运用Box作为平替,而方位联系则是运用Modifier
来完结对应联系。
@Composable
fun textRelativeLayout() {
Box {
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.size(100.dp)
)
Text(
text = "Alex",
modifier = Modifier.align(Alignment.Center)
)
}
}
例如,运用Alignment.Center
值就可以让Text
显示在父容器的中心方位。
- ConstraintLayout
在Compose中,具有与ConstraintLayout同名的组件,用于处理比RelativeLayout更多的功用,这儿不做赘述,会有专题介绍如安在Compose中运用ConstraintLayout。
- RecyclerView – LazyColumn
@Composable
fun testRecyclerView() {
val datas = mutableListOf("A", "B", "C", "D")
LazyColumn {
items(datas) { item ->
Box(modifier = Modifier.size(50.dp)) {
Text(text = item,
modifier = Modifier.align(Alignment.Center))
}
}
}
}
LazyColumn需求一个动态的列表数据,添加到items
中,就可以展现全部的列表数据,不需求Adapter
、ViewHolder
、LayoutManager
。
并且当咱们需求给列表加上head或许foot的时分,假设运用RecyclerView,那么就需求在数据项的第0个方位和最后一个方位加上view,那么运用LazyColumn则不需求,直接通过item
就可以添加一个ItemView。
@Composable
fun testRecyclerView() {
val datas = mutableListOf("A", "B", "C", "D")
LazyColumn {
// 添加一个头部
item {
Text(text = "这是列表的头部")
}
items(datas) { item ->
Box(modifier = Modifier.size(50.dp)) {
Text(text = item,
modifier = Modifier.align(Alignment.Center))
}
}
}
}
当然除了LazyColumn,还有LazyRow
,归于横向的滑动。除此之外,LazyColumn/LazyRow也有类似于RecyclerView的缓存机制,确保列表的顺畅滑动。
除此之外,像ScrollerView、ViewPager,在Compose中平替的组件还在孵化中,像ViewPager对应的Pager(VerticalPager/HorizontalPager)组件,目前还在测验阶段,假设在项目中想要完结ViewPager的功用,可以自行完结,或许运用原生的ViewPager。
2 Modifier详解
Modifier在Compose中是一个十分重要的组成,像咱们在传统的UI中,需求声明每个组件的间隔或许相关于父容器的间隔,通常运用margin或许padding来完结。
<TextView
android:id="@+id/tv_ui"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="UI制作"
android:layout_marginBottom="20dp"
android:paddingTop="20dp"/>
而在Compose傍边,会运用Modifier来完结,在Compose傍边的每个组件中,都有一个Modifier参数变量。
@Composable
fun Text(
text: String,
// 可以设置Modifier
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
// ......
) {
2.1 设置组件padding 和 margin
如下一个可组合函数,笔直线性布局中,有一个Image和Text,其中通过Mofifier
设置了Image的padding为12dp,整个线性布局的布景是蓝色的,也是通过Modifier的background
特点设置的。
@Composable
fun testModifier() {
Column(modifier = Modifier.background(Color.Blue)){
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.padding(12.dp)
)
Text(text = "测验Modifier")
}
}
假设设置margin特点,咱们发现Modifier没有margin
这个函数,那么在Compose中怎么设置margin呢?可以通过Spacer
组件来设置。
@Composable
fun testModifier() {
Column(modifier = Modifier.background(Color.Blue)) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.padding(12.dp)
)
Spacer(modifier = Modifier.size(10.dp))
Text(
text = "测验Modifier",
modifier = Modifier.background(Color.Red)
)
}
}
看下作用:
2.2 match_parent和wrap_content怎么完结
其实Modifier除了设置padding
和background
之外,还可以设置组件的巨细,当然与传统的XML布局必须要设置layout_width和layout_height不同的是,Compose默许宽高均为wrap_content,不需求强制设置。
假设想要组件完结match_parent,那么可以运用Modifier的fillMaxHeight
、fillMaxWidth
、fillMaxSize
函数完结宽高的装备。
@Composable
fun testModifier() {
Column(
modifier = Modifier
.background(Color.Blue)
.fillMaxHeight()
) {
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier.padding(12.dp)
)
Spacer(modifier = Modifier.size(10.dp))
Text(
text = "测验Modifier",
modifier = Modifier.background(Color.Red)
)
}
}
假设要设置一个固定的值,那么可以运用Modifier的width
、height
、size
函数,那么就可以装备组件的具体宽高,假设组件是一个正方形,可以直接调用size
函数。
Image(
painter = painterResource(id = R.drawable.ic_launcher_background),
contentDescription = null,
modifier = Modifier
.padding(12.dp)
.width(100.dp)
.height(80.dp)
)
2.3 什么情况下会运用Modifier
假设,咱们要设置Text的文字巨细,和文字色彩,咱们可以运用Modifier来设置吗?其实不能,这两个特点其实需求通过Text结构办法中的参数设置。
Text(
text = "测验Modifier",
modifier = Modifier.background(Color.Red),
fontSize = 20.sp,
fontWeight = FontWeight(400),
color = Color.White
)
像文字的色彩、字重、字号都只能通过Text的结构函数中的参数设置,而不能运用Modifier。那么什么情况下会运用Modifier呢?
其实Modifier归于公共特点的调集。其实不难理解,关于Text来说它是展现文本的组件,字号、字重归于其独有的特点,而假设在Modifier中保护这些特点,关于布局、Image这些来说根本用不到的特点,反而会导致Modifier变得越来越臃肿。
所以之后在自定义Compose组件的时分,关于组件的特有特点,需求放在结构参数中。
2.4 点击事情
在Android传统UI中,每一个View都可以被设置点击事情监听器,无论View仍是ViewGroup都是可被点击的,因此Compose中点击事情也是放在了Modifier中,由于归于通用的能力。
Text(
text = "测验Modifier",
fontSize = 20.sp,
modifier = Modifier.background(Color.Red)
.padding(12.dp)
.clickable {
},
fontWeight = FontWeight(400),
color = Color.White
)
在Modifier中提供了clickable
函数,这儿需求留意一点,clickable
的摆放次序是有讲究的,由于咱们给按钮加了一个padding,所以clickable
放在了padding后边,那么点击时热区不包含padding,如下所示:
假设想要热区包含padding,那么在声明clickable
时,需求放在padding的前面,可以这么理解,想要呼应点击事情的区域,需求放在clickable之后。
Text(
text = "测验Modifier",
fontSize = 20.sp,
modifier = Modifier.background(Color.Red)
.clickable {
}
.padding(12.dp),
fontWeight = FontWeight(400),
color = Color.White
)
通过前面的这些介绍,相信同伴们关于Compose有了一些基础的了解,相信把握了前面的这些常识,通过Compose可以写出一些页面了吧,并且相较于传统的UI,这种声明式的结构显得愈加灵活且高效,当然这也是Compose的冰山一角,后续我将会介绍愈加高阶的Compose常识。