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

在这里插入图片描述

本文基于上述内容做一个摘要介绍,帮助大家更快速地了解和熟悉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 = text,
            modifier = Modifier.padding(16.dp).fillMaxWidth(),
            onValueChange = {
    
    
                text = it
            }
        )
    }
}

其中onValueChange响应文本框的输入更新。

另外,还可以使用 Material Design还提供的基于BaseTextField的默认实现TextField

@Composable
fun SimpleMaterialTextFieldComponent() {
    
    
    var text by savedInstanceState {
    
     "" }
    TextField(
        value = 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 = 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")

在这里插入图片描述


Preview Parameter


通常的预览不支持传入参数,通过使用@PreviewParameter也可以实现参数预览,准备过程会繁琐一些:

  1. 定义数据
data class Blog(
    val name: String,
    val author: String
)
  1. 构建Provider
class DummyBlogProvider : PreviewParameterProvider<Blog> {
    
    
    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<Blog>) {
    
    
    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<Blog>) {
    
    
    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))

更多参数的使用,例如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 = false },
            title = {
    
     Text(text = "Alert Dialog") },
            text = {
    
     Text("Hello! I am an Alert Dialog") },
            confirmButton = {
    
    
                TextButton(
                    onClick = {
    
    
                        openDialog.value = false
                        /* Do some other action */
                    }
                ) {
    
    
                    Text("Confirm")
                }
            },
            dismissButton = {
    
    
                TextButton(
                    onClick = {
    
    
                        openDialog.value = 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 = 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 = sliderValue,
        modifier = Modifier.padding(8.dp),
        onValueChange = {
    
     newValue ->
            sliderValue = 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

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/109689950