Android|Jetpack Compose UI组件入门教程

Android|Jetpack Compose UI组件入门教程
文章图片

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
Android|Jetpack Compose UI组件入门教程
文章图片

本文基于上述内容做一个摘要介绍,帮助大家更快速地了解和熟悉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的EditTextBaseTextField是不稳定的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注解
  • 不能有参数
因为预览的本质就是自动运行一下Compose函数,所以无法运行带有参数的函数。添加@Preview后,便可以在Studio中实时预览当前效果。
当有同时预览多个组件时,为了便于区分可以为其指定name
@Preview(name = "Named Preview")

Android|Jetpack Compose UI组件入门教程
文章图片


Preview Parameter 通常的预览不支持传入参数,通过使用@PreviewParameter也可以实现参数预览,准备过程会繁琐一些:
  1. 定义数据
data class Blog( val name: String, val author: String )

  1. 构建Provider
class DummyBlogProvider : PreviewParameterProvider { override val values = sequenceOf(Blog("Learning Compose", "MindOrks"), Blog("Learning Android", "MindOrks")) override val count: Int = values.count() }

  1. 传入数据
@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) ) } } }

相应的,在水平方向有RowScrollableRowLazy 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 CircularProgressIndicatorLinearProgressIndicatorCompose分别实现了圆形和线形的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

    推荐阅读