文章图片
Jetpack Compose是Android推出的新一代声明式UI框架,随着alpha版本的发布,其API也逐渐趋于稳定,是时候深入学习一波了。
Compose借鉴了React的设计思想,配合Kotlin函数式编程的语法特性,通过函数声明UI组件:
@Composable
fun AnyUiComponent() {
// Code for UI element
}
而开发者唯一需要做的是添加
@Composable
注解,它会在编译期将创建真正的Composer对象参与底层渲染逻辑。本文不针对底层原理作介绍,主要是向大家安利一个Compose项目
Learn Jetpack Compose for Android by example
通过例子快速上手Compose
文章图片
本文基于上述内容做一个摘要介绍,帮助大家更快速地了解和熟悉Compose的基本使用
Simple TextView
@Composable
fun SimpleText(displayText: String) {
Text(text = displayText)
}
我们使用
@Composable
创建了一个自定义组件SimpleText
,其内部嵌套调用了Compose内置组件Text,并向其传递参数完成SimpleText的定义后,我们便可以在Activity中使用
class SimpleTextActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
SimpleText(getString("I am learning Compose"))
}
}
}
}
作为声明式UI框架,Compose与React、Flutter等的使用方式类似,都是通过声明+组合的方式实现UI的搭建。如上,我们在Column中调用了我们自定义的SimpleText,用近似写XML的方式写Kotlin代码,这就是声明式的魅力。
Text Styling 通过为Text指定Style,可以设置字体、颜色等。
@Composable
fun StyleText(displayText: String, style: TextStyle? = null, maxLines: Int? = null) {
Text(
text = displayText,
modifier = Modifier.padding(16.dp),
style = style ?: TextStyle.Default,
overflow = TextOverflow.Ellipsis,
maxLines = maxLines ?: Int.MAX_VALUE
)
}
上例中定义了一个StyleText组件,内部通过Text的参数设置样式,例如:
- 设置字体:
style = TextStyle(
fontSize = 24.sp
)
- 设置粗体
fontWeight = FontWeight.Bold
TextField(EditText) Compose使用
BaseTextField
实现文本输入,类似Android的EditText
。BaseTextField
是不稳定的API,使用时需要添加@ExperimentalFoundationApi
@ExperimentalFoundationApi
@Composable
fun SimpleTextFieldComponent() {
Surface(color = Color.LightGray, modifier = Modifier.padding(16.dp)) {
var text by remember { mutableStateOf(TextFieldValue("Enter text here")) }
BaseTextField(
value = https://www.it610.com/article/text,
modifier = Modifier.padding(16.dp).fillMaxWidth(),
onValueChange = {
text = it
}
)
}
}
其中
onValueChange
响应文本框的输入更新。另外,还可以使用 Material Design还提供的基于BaseTextField的默认实现TextField
@Composable
fun SimpleMaterialTextFieldComponent() {
var text by savedInstanceState { "" }
TextField(
value = https://www.it610.com/article/text,
modifier = Modifier.padding(16.dp).fillMaxWidth(),
onValueChange = { text = it },
label = { Text("Label") }
)
}
当没有输入内容时,
label
通过Text提供默认文本显示,相当于hint我们可以通过参数对TextField或者BaseTextField进行配置,例如:
- 数字键盘
var text by remember { mutableStateOf(TextFieldValue("0123")) }
BaseTextField(value = https://www.it610.com/article/text,
keyboardType = KeyboardType.Number,
onValueChange = {
text = it
}
)
- 密码输入
keyboardType = KeyboardType.Password,
visualTransformation = PasswordVisualTransformation()
- 当没有输入时的占位显示
placeholder = { Text("MindOrks") }
更多参数的使用,例如icon、errorColor 、backgroundColor等可以参考项目中的示例
Simple Preview 最新的AndroidStudio支持基于Compose的布局预览(手头版本 AS 4.1 Preview)
// This is a Composable function to display a Text
@Composable
fun SimpleText(displayText: String) {
Text(text = displayText)
}@Preview
@Composable
fun SimpleTextPreview() {
SimpleText("Hi I am learning Compose")
}
一个简单的可预览的Composable函数需满足以下条件:
- 添加
@Preiview
注解 - 不能有参数
当有同时预览多个组件时,为了便于区分可以为其指定
name
@Preview(name = "Named Preview")
文章图片
Preview Parameter 通常的预览不支持传入参数,通过使用
@PreviewParameter
也可以实现参数预览,准备过程会繁琐一些:- 定义数据
data class Blog(
val name: String,
val author: String
)
- 构建Provider
class DummyBlogProvider : PreviewParameterProvider {
override val values =
sequenceOf(Blog("Learning Compose", "MindOrks"), Blog("Learning Android", "MindOrks"))
override val count: Int = values.count()
}
- 传入数据
@Preview
@Composable
fun BlogInfo(@PreviewParameter(DummyBlogProvider::class) blog: Blog) {
SimpleTextComponent("${blog.name} by ${blog.author}")
}
Simple Column Column类似一个Vertical的LinearLayout
@Composable
fun SimpleColumnComponent() {
Column(modifier = Modifier.padding(16.dp)) {
Text(text = "Hello! I am Text 1", color = Color.Black)
Text(text = "Hello! I am Text 2", color = Color.Blue)
}
}
Scrollable Column 当Column中的内容超出屏幕高度时,需要使用
ScrollableColumn
,相当于ScrollView@Composable
fun ScrollableColumnComponent(blogList: List) {
ScrollableColumn {
val context = ContextAmbient.current
Column {
for (blog in blogList) {
Card(
shape = RoundedCornerShape(4.dp),
modifier = Modifier.fillMaxWidth().padding(16.dp).clickable(onClick = {
Toast.makeText(context, "Author: ${blog.author}", Toast.LENGTH_SHORT).show()
}),
backgroundColor = Color(0xFFFFA867.toInt())
) {
Text(
blog.name, style = TextStyle(
fontSize = 16.sp,
textAlign = TextAlign.Center
), modifier = Modifier.padding(16.dp)
)
}
}
}
}
}
Lazy Column
LazyColumnFor
可以当item进入屏幕时再加载,而无需一次加载全部数据,相当于RecyclerView@Composable
fun LazyColumnScrollableComponent(blogList: List) {
LazyColumnFor(items = blogList, modifier = Modifier.fillMaxHeight()) { blog ->
val context = ContextAmbient.current
Card(
shape = RoundedCornerShape(4.dp),
modifier = Modifier.fillParentMaxWidth().padding(16.dp).clickable(onClick = {
Toast.makeText(context, "Author: ${blog.author}", Toast.LENGTH_SHORT).show()
}),
backgroundColor = Color(0xFFFFA867.toInt())
) {
Text(
blog.name, style = TextStyle(
fontSize = 16.sp,
textAlign = TextAlign.Center
), modifier = Modifier.padding(16.dp)
)
}
}
}
相应的,在水平方向有
Row
、ScrollableRow
、Lazy Row
可供选择。Box Box内部的子控件会在Z轴上按顺序叠加,类似FrameLayout。在Flutter中类似的Widget称为Stack,Compose早期的API中也叫做Stack,如今已废弃被Box取代。
@Composable
fun SimpleBoxComponent() {
Box(modifier = Modifier.fillMaxSize().padding(16.dp)) {
Image(imageResource(R.drawable.mindorks_cover))
Text(
modifier = Modifier.padding(start = 16.dp, top = 16.dp),
text = "I am a text over Image",
fontSize = 16.sp,
color = Color.Red
)
}
}
Button
@Composable
fun SimpleButtonComponent() {
val context = ContextAmbient.current
Button(
onClick = {
Toast.makeText(context, "Thanks for clicking!", Toast.LENGTH_LONG).show()
},
modifier = Modifier.padding(8.dp).fillMaxWidth()
) {
Text("Click Me")
}
}
如上,Button内部摆放Text用来显示按钮的文字,
onClick
响应点击事件通过参数可以进行其他配置,例如:
- 圆角
shape = RoundedCornerShape(12.dp)
- 边框
border = BorderStroke(width = 1.dp, brush = SolidColor(Color.Green))
【Android|Jetpack Compose UI组件入门教程】更多参数的使用,例如icon、color 等可以参考项目中的示例
Card Card相当于Material中的CardView
@Composable
fun SimpleCardComponent() {
Card(
backgroundColor = Color(0xFFFFA867.toInt()),
modifier = Modifier.padding(16.dp).fillMaxWidth()
) {
Text(
text = "Simple Card",
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 16.sp
),
modifier = Modifier.padding(16.dp)
)
}
}
Clickable 你可以为任意Compose组件添加Click功能,并支持单击、双击、长按等多种点击效果
@Composable
fun SimpleTextComponent() {
val context = ContextAmbient.current
Text(
text = "Click Me",
textAlign = TextAlign.Center,
color = Color.Black,
modifier = Modifier.padding(16.dp).fillMaxWidth().clickable(onClick = {
Toast.makeText(context, "Thanks for clicking! I am Text", Toast.LENGTH_SHORT).show()
}, onLongClick = {
Toast.makeText(context, "Thanks for LONG click! I am Text", Toast.LENGTH_SHORT).show()
}, onDoubleClick = {
Toast.makeText(context, "Thanks for DOUBLE click! I am Text", Toast.LENGTH_SHORT).show()
})
)
}
Image
@Composable
fun SimpleImageComponent() {
// Image is a composable that is used to display some image.
val image = imageResource(R.drawable.mindorks_cover)
Column(
modifier = Modifier.padding(16.dp)
) {
Image(image)
}
}
还可以通过Modifier设置圆角显示
Image(
image,
modifier = Modifier.fillMaxWidth().clip(shape = RoundedCornerShape(8.dp)),
contentScale = ContentScale.Fit
)
Alert Dialog
@Composable
fun AlertDialogComponent() {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = { openDialog.value = https://www.it610.com/article/false },
title = { Text(text ="Alert Dialog") },
text = { Text("Hello! I am an Alert Dialog") },
confirmButton = {
TextButton(
onClick = {
openDialog.value = https://www.it610.com/article/false
/* Do some other action */
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
openDialog.value = https://www.it610.com/article/false
/* Do some other action */
}
) {
Text("Dismiss")
}
},
backgroundColor = Color.Black,
contentColor = Color.White
)
}
}
Material AppBar Material规范中少不了AppBar的使用,
TopAppBar
实现顶部AppBar,可以设置action,并为其设置icon、onClick等@Composable
fun TopAppBarComponent() {
TopAppBar(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
title = { Text("App Name") },
navigationIcon = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Menu)
}
},
actions = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Favorite)
}
IconButton(onClick = { /* doSomething() */ }) {
Icon(Icons.Filled.Favorite)
}
}
)
}
类似的,
BottomAppBar
提供了底部AppBar的实现Material BottomNavigation BottomNavigation实现了NavigationBar效果,通过
BottomNavigationItem
往里添加Item@Composable
fun BottomNavigationWithLabelComponent() {
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Home", "Blogs", "Profile")
BottomNavigation(
modifier = Modifier.padding(16.dp).fillMaxWidth(),
backgroundColor = Color.Black,
contentColor = Color.Yellow
) {
items.forEachIndexed { index, item ->
BottomNavigationItem(
label = { Text(text = item) },
icon = { Icon(Icons.Filled.Favorite) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
}
如果不想显示item的label,可以设置
alwaysShowLabels = false
Material Checkbox
@Composable
fun SimpleCheckboxComponent() {
val checkedState = remember { mutableStateOf(true) }
Row {
Checkbox(
checked = checkedState.value,
modifier = Modifier.padding(16.dp),
onCheckedChange = { checkedState.value = https://www.it610.com/article/it },
)
Text(text ="Checkbox Example", modifier = Modifier.padding(16.dp))
}
}
onCheckedChange
响应事件处理Material ProgressBar
CircularProgressIndicator
、LinearProgressIndicatorCompose
分别实现了圆形和线形的ProgressBar,例如:@Composable
fun SimpleCircularProgressComponent() {
CircularProgressIndicator(
modifier = Modifier.padding(16.dp)
)
}
progress = 0.4f
可以设置当前进度Material Slider Slider通过滑动实现数量调节,例如音量、亮度的调节等
@Composable
fun SimpleSliderComponent() {
var sliderValue by remember { mutableStateOf(0.4f) }
Slider(
value = https://www.it610.com/article/sliderValue,
modifier = Modifier.padding(8.dp),
onValueChange = { newValue ->
sliderValue = https://www.it610.com/article/newValue
}
)
Text(
text ="Slider value: $sliderValue",
modifier = Modifier.padding(8.dp)
)
}
还可以通过传递参数
steps
,实现步进式的调节Material Snackbar Snackbar从底部弹出,比toast提供跟丰富的交互能力
@Composable
fun SimpleSnackbarComponent() {
Snackbar(
modifier = Modifier.padding(16.dp),
text = {
Text(text = "I'm a Simple Snackbar")
}
)
}
还可以为Snackbar添加更多action
action = {
Text(text = "OK", style = TextStyle(color = Color.Green))
}
Custom View 除了上面介绍的各种组件以外,Compose还允许直接在Canvas上绘制更多自定义组件
@Composable
fun CustomViewComponent() {
Canvas(modifier = Modifier.fillMaxSize().padding(16.dp)) {
drawRect(
color = Color.Red,
// topLeft is the coordinate of top-left point
topLeft = Offset(0f, 0f),
size = Size(800f, 400f)
)
drawArc(
Color.Gray,
startAngle = 0f,
sweepAngle = 120f,
useCenter = true,
size = Size(600f, 600f),
topLeft = Offset(300f, 300f)
)
}
}
Animation Compose提供了多种效果的动画能力,例如我们可以实现CrossFade的动画效果
@Composable
fun CrossFadeAnimation() {
val colors = listOf(Color.Red, Color.Green, Color.Blue, Color.Gray)
var current by remember { mutableStateOf(colors[0]) }
Column(modifier = Modifier.fillMaxSize()) {
Crossfade(current = current) { color ->
Box(Modifier.fillMaxSize().clickable(
onClick = {
current = colors.random()
}
).background(color))
Text(
modifier = Modifier.fillMaxSize(),
textAlign = TextAlign.Center,
text = "Click To See"
)
}
}
}
如上,当我们点击Box时,Box将会通过Crossfade的动画过渡到指定颜色。
更多详细实例,请移步 Learn Jetpack Compose for Android by example
推荐阅读
- Jetpack|Compose 类型稳定性注解(@Stable & @Immutable)
- Jetpack|深入理解 Jetpack Compose 内核(SlotTable 系统)
- 面试|网络安全工程师面试题整理
- 面试|SpringBoot学习笔记
- 面试|理解JS的三座大山
- 面试|瑞吉外卖项目剩余功能补充
- JAVA|12. 虚拟机与类加载机制
- 面试|MySQL 免安装版的下载与配置教程
- 面试|MySQL 入门(Case 语句很好用)