序言
在使用列表的时候,以前是使用 Layout 布局里面添加Recyclerview进行列表的显示,但是在Compose里面,是没有这个Recyclerview使用的,那Compose怎么实现列表数据呢?
使用 【LazyColumn】
首先创建一个Compose项目
在创建Compose项目的时候,有时候会创建失败,因为使用的各种配置文件和Android studio版本可能导致的不兼容问题,出现各种意外情况。
我使用的Android studio版本是
在创建项目之后,配置文件如下,这样是可以正常运行的,用来学习Compose的使用方式是足够了。
setting.gradle
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
}
}
rootProject.name = "MyCompose"
include ':app'
项目下的gradle文件
buildscript {
ext {
compose_version = '1.0.1'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.2"
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21'
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app下面的gradle文件
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 30
defaultConfig {
applicationId "com.example.mycompose"
minSdk 23
targetSdk 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.21'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.0-alpha06'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}
gradle目录下的 wrapper文件夹下面的 gradle-wrapper.properties文件
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
里面的版本不高,但是挺适合相应的Android studio版本的
在使用Recyclerview进行列表加载的时候,是需要创建一个数据bean的,在使用Compose的时候也不例外
创建一个数据Bean
data class Item(val id: Int, val name: String)
项目运行截图
项目运行代码
class ListActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Green
) {
DynamicItemsList()
}
}
}
}
}
@Composable
fun DynamicItemsList() {
val items by remember { mutableStateOf(mutableStateListOf<Item>()) }
fun addItem() {
val newItem = Item(id = items.size + 1, name = "Item ${items.size + 1}")
items.add(newItem)
}
Column(
Modifier
.fillMaxSize()
.padding(16.dp)
) {
Button(onClick = { addItem() }, modifier = Modifier.fillMaxWidth()) {
Text(text = "ADD Item")
}
Spacer(modifier = Modifier.height(10.dp))
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
) {
items(items) { item ->
ItemView(item = item)
}
}
}
}
@Composable
fun ItemView(item: Item) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.background(Color.LightGray)
.border(2.dp, color = Color.Red)
.height(50.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item.name,
style = MaterialTheme.typography.body1,
fontSize = 14.sp
)
}
}
分析一下代码
//ComponentActivity 是一个基础活动类,提供了与用户界面的相关功能,例如生命周期管理和设置视图等。它是 Jetpack Compose 的一个活动基类。
class ListActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//setContent 是 Jetpack Compose 的一个方法,用于设置 Compose 的内容。它接受一个 Composable 函数作为参数,这个函数定义了 UI 的组成部分。
setContent {
//MyComposeTheme 是一个自定义的主题函数,用于定义 Compose 应用的主题和样式。它通常会包含对颜色、形状、字体等主题属性的定义。使用主题函数可以确保应用的 UI 风格一致,并且可以通过主题轻松地调整样式。
MyComposeTheme {
//Surface 是一个 Compose 组件,用于绘制具有背景颜色和边界的矩形区域。它通常用于为 UI 元素提供背景和边框。
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Green
) {
DynamicItemsList()
}
}
}
}
}
Surface 源码
@Composable
fun Surface(
modifier: Modifier = Modifier,
shape: Shape = RectangleShape,
color: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(color),
border: BorderStroke? = null,
elevation: Dp = 0.dp,
content: @Composable () -> Unit
) {
Surface(
modifier = modifier,
shape = shape,
color = color,
contentColor = contentColor,
border = border,
elevation = elevation,
content = content,
clickAndSemanticsModifier = Modifier
.semantics(mergeDescendants = false) {}
.pointerInput(Unit) { }
)
}
modifier:用于修饰组件的外观和行为,默认为 Modifier(即没有任何修饰)。
shape:定义组件的形状,默认为 RectangleShape(矩形)。
color:定义组件的背景色,默认为 MaterialTheme.colors.surface(主题的表面颜色)。
contentColor:定义组件的内容颜色,默认为基于 color 计算得到的值。
border:可选的边框样式,默认为 null(没有边框)。
elevation:组件的阴影高度,默认为 0.dp(没有阴影)。
content:接收一个 Composable 函数作为参数,用于定义组件内部的内容。
这个 Surface 是Android Compose自带的,但是 DynamicItemsList 是我们自己自定义的
@Composable
fun DynamicItemsList() {
val items by remember { mutableStateOf(mutableStateListOf<Item>()) }
fun addItem() {
val newItem = Item(id = items.size + 1, name = "Item ${items.size + 1}")
items.add(newItem)
}
Column(
Modifier
.fillMaxSize()
.padding(16.dp)
) {
Button(onClick = { addItem() }, modifier = Modifier.fillMaxWidth()) {
Text(text = "ADD Item")
}
Spacer(modifier = Modifier.height(10.dp))
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
) {
items(items) { item ->
ItemView(item = item)
}
}
}
}
这个 DynamicItemsList 里面的这个 ItemView 也是我们自定义的
@Composable
fun ItemView(item: Item) {
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.background(Color.LightGray)
.border(2.dp, color = Color.Red)
.height(50.dp),
contentAlignment = Alignment.Center
) {
Text(
text = item.name,
style = MaterialTheme.typography.body1,
fontSize = 14.sp
)
}
}
在Compose中,要是自定义一个Compose函数,需要满足:
1.使用 @Composable 注解:标记函数为 Composable 函数,使其能够用于声明式 UI 构建。
2.函数参数:可以包含需要的参数来定义组件的外观和行为。参数通常包括修饰符、颜色、形状等。
3.函数体:在函数体中调用其他 Composable 函数来构建 UI。
例如,定义一个简单的自定义按钮组件:
@Composable
fun CustomButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
backgroundColor: Color = Color.Blue,
content: @Composable () -> Unit
) {
Button(
onClick = onClick,
modifier = modifier,
colors = ButtonDefaults.buttonColors(backgroundColor = backgroundColor),
content = content
)
}
在这个示例中:
1.@Composable 标记函数为 Composable。
2.CustomButton 接收 onClick 回调、修饰符、背景色和内容作为参数。
3.函数体内部使用 Button Composable 函数构建实际的 UI 元素,并应用传入的参数。
回到项目代码里面,在这个 ItemView 函数里面
@Composable
//ItemView 函数接受一个 Item 类型的参数,用于传递要显示的数据。
fun ItemView(item: Item) {
//Box 是一个容器 Composable,用于排列其子组件。
Box(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp)
.background(Color.LightGray)
.border(2.dp, color = Color.Red)
.height(50.dp),
contentAlignment = Alignment.Center
) {
//Text 用于显示文本内容。
Text(
text = item.name,
style = MaterialTheme.typography.body1,
fontSize = 14.sp
)
}
}
其中里面用到了 Text 和 Box两个函数
Text
@Composable
fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
fontSize: TextUnit = TextUnit.Unspecified,
fontStyle: FontStyle? = null,
fontWeight: FontWeight? = null,
fontFamily: FontFamily? = null,
letterSpacing: TextUnit = TextUnit.Unspecified,
textDecoration: TextDecoration? = null,
textAlign: TextAlign? = null,
lineHeight: TextUnit = TextUnit.Unspecified,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
onTextLayout: (TextLayoutResult) -> Unit = {},
style: TextStyle = LocalTextStyle.current
) {
Text(
AnnotatedString(text),
modifier,
color,
fontSize,
fontStyle,
fontWeight,
fontFamily,
letterSpacing,
textDecoration,
textAlign,
lineHeight,
overflow,
softWrap,
maxLines,
emptyMap(),
onTextLayout,
style
)
}
text: String:要显示的文本内容。
modifier: Modifier = Modifier:用于修饰和布局组件的 Modifier,默认为 Modifier。
color: Color = Color.Unspecified:文本颜色,默认为 Color.Unspecified 表示不设置颜色。
fontSize: TextUnit = TextUnit.Unspecified:文本字体大小,默认为 TextUnit.Unspecified 表示不设置。
fontStyle: FontStyle? = null:字体样式,默认为 null 表示不设置。
fontWeight: FontWeight? = null:字体粗细,默认为 null 表示不设置。
fontFamily: FontFamily? = null:字体家族,默认为 null 表示使用默认字体。
letterSpacing: TextUnit = TextUnit.Unspecified:字母间距,默认为 TextUnit.Unspecified 表示不设置。
textDecoration: TextDecoration? = null:文本装饰(如下划线),默认为 null 表示不设置。
textAlign: TextAlign? = null:文本对齐方式,默认为 null 表示不设置。
lineHeight: TextUnit = TextUnit.Unspecified:行高,默认为 TextUnit.Unspecified 表示不设置。
overflow: TextOverflow = TextOverflow.Clip:文本溢出处理方式,默认为 TextOverflow.Clip。
softWrap: Boolean = true:是否启用软换行,默认为 true。
maxLines: Int = Int.MAX_VALUE:最大行数,默认为 Int.MAX_VALUE,表示不限制行数。
onTextLayout: (TextLayoutResult) -> Unit = {}:文本布局完成后的回调函数,默认为空函数。
style: TextStyle = LocalTextStyle.current:文本样式,默认为当前主题的文本样式。
Box
@Composable
inline fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: @Composable BoxScope.() -> Unit
) {
val measurePolicy = rememberBoxMeasurePolicy(contentAlignment, propagateMinConstraints)
Layout(
content = { BoxScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
modifier: Modifier = Modifier
Modifier 是用于修饰和布局组件的类。默认值是 Modifier,即不进行任何额外修饰。
contentAlignment: Alignment = Alignment.TopStart
控制 Box 内部内容的对齐方式。Alignment.TopStart 表示默认的内容对齐方式是顶部开始(即左上角)。
propagateMinConstraints: Boolean = false
如果为 true,Box 会将其最小尺寸约束传播给子组件。默认值为 false,即不会传播最小约束。
content: @Composable BoxScope.() -> Unit
这是一个 Composable Lambda,用于定义 Box 内部的子组件内容。BoxScope 是一个作用域,在 content 中可以访问。
接下来是我们定义的另一个函数 DynamicItemsList
@Composable
fun DynamicItemsList() {
//items:这是一个可变的状态列表,使用 mutableStateListOf 创建。by remember 确保状态在组合过程中保持一致。
//mutableStateListOf<Item>():创建一个可以观察的、可变的 List。这意味着,当列表的内容发生变化时,Compose 会自动重新组合相关的 UI 部分。
//mutableStateOf(...):包装 mutableStateListOf,使其成为一个状态对象。Compose 能够检测到 mutableStateOf 包装的对象的变化,从而重新组合 UI。
//remember { ... }:在组合过程中记住 mutableStateOf 返回的状态对象,确保状态在重组时保持一致。
//val items by ...:使用 by 委托将 items 绑定到状态对象。这样,items 就是一个直接的、可读写的可变状态列表。
val items by remember { mutableStateOf(mutableStateListOf<Item>()) }
//addItem:创建一个新的 Item 对象并将其添加到 items 列表中。id 和 name 基于列表的大小生成。
fun addItem() {
val newItem = Item(id = items.size + 1, name = "Item ${items.size + 1}")
items.add(newItem)
}
//Column:垂直布局容器
Column(
Modifier
.fillMaxSize()
.padding(16.dp)
) {
//Button按钮
Button(onClick = { addItem() }, modifier = Modifier.fillMaxWidth()) {
Text(text = "ADD Item")
}
//Spacer添加垂直间隔。
Spacer(modifier = Modifier.height(10.dp))
//LazyColumn用于垂直展示可滚动的列表。LazyRow用于横向滚动
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
) {
//items(items):将 items 列表中的每个 item 传递给 ItemView 组件。
items(items) { item ->
ItemView(item = item)
}
}
}
}
【Column】
@Composable
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
) {
val measurePolicy = columnMeasurePolicy(verticalArrangement, horizontalAlignment)
Layout(
content = { ColumnScopeInstance.content() },
measurePolicy = measurePolicy,
modifier = modifier
)
}
modifier: Modifier = Modifier: Modifier 是用于修饰和调整布局的参数,例如设置边距、填充、对齐等。默认值为 Modifier,即没有附加任何修饰符。
verticalArrangement: Arrangement.Vertical = Arrangement.Top: Arrangement.Vertical 定义了子项在垂直方向上的排列方式。Arrangement.Top 表示子项从容器顶部开始排列。其他值如 Arrangement.Center、Arrangement.Bottom 等可以调整排列方式。
horizontalAlignment: Alignment.Horizontal = Alignment.Start: Alignment.Horizontal 定义了子项在水平方向上的对齐方式。Alignment.Start 表示子项在容器的开始(左侧)对齐。其他值如 Alignment.CenterHorizontally、Alignment.End 等可以调整对齐方式。
content: @Composable ColumnScope.() -> Unit: 这是一个 lambda 函数类型的参数,代表了 Column 的子内容。ColumnScope 是一个特定的作用域,允许在 content 中使用 Column 相关的函数来添加子项。
【Button】
@Composable
fun Button(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
elevation: ButtonElevation? = ButtonDefaults.elevation(),
shape: Shape = MaterialTheme.shapes.small,
border: BorderStroke? = null,
colors: ButtonColors = ButtonDefaults.buttonColors(),
contentPadding: PaddingValues = ButtonDefaults.ContentPadding,
content: @Composable RowScope.() -> Unit
) {
val contentColor by colors.contentColor(enabled)
Surface(
modifier = modifier,
shape = shape,
color = colors.backgroundColor(enabled).value,
contentColor = contentColor.copy(alpha = 1f),
border = border,
elevation = elevation?.elevation(enabled, interactionSource)?.value ?: 0.dp,
onClick = onClick,
enabled = enabled,
role = Role.Button,
interactionSource = interactionSource,
indication = rememberRipple()
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(
value = MaterialTheme.typography.button
) {
Row(
Modifier
.defaultMinSize(
minWidth = ButtonDefaults.MinWidth,
minHeight = ButtonDefaults.MinHeight
)
.padding(contentPadding),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
content = content
)
}
}
}
}
onClick: () -> Unit: 点击按钮时调用的回调函数。
modifier: Modifier = Modifier: 用于修饰按钮的 Modifier,可以设置尺寸、边距等。
enabled: Boolean = true: 按钮是否启用,默认为 true。
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }: 用于处理按钮的交互状态。
elevation: ButtonElevation? = ButtonDefaults.elevation(): 按钮的阴影效果,默认为 ButtonDefaults.elevation(),即系统默认值。
shape: Shape = MaterialTheme.shapes.small: 按钮的形状,默认为 MaterialTheme 中定义的小形状。
border: BorderStroke? = null: 按钮的边框样式,默认为 null,即没有边框。
colors: ButtonColors = ButtonDefaults.buttonColors(): 按钮的颜色配置,默认为系统默认颜色。
contentPadding: PaddingValues = ButtonDefaults.ContentPadding: 按钮内容的内边距,默认为系统默认值。
content: @Composable RowScope.() -> Unit: 按钮的内容,使用 RowScope 作用域来定义其布局。
【Spacer】
@Composable
fun Spacer(modifier: Modifier) {
Layout({}, modifier) { _, constraints ->
with(constraints) {
val width = if (hasFixedWidth) maxWidth else 0
val height = if (hasFixedHeight) maxHeight else 0
layout(width, height) {}
}
}
}
【LazyColumn】
@Composable
fun LazyColumn(
modifier: Modifier = Modifier,
state: LazyListState = rememberLazyListState(),
contentPadding: PaddingValues = PaddingValues(0.dp),
reverseLayout: Boolean = false,
verticalArrangement: Arrangement.Vertical =
if (!reverseLayout) Arrangement.Top else Arrangement.Bottom,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior(),
content: LazyListScope.() -> Unit
) {
LazyList(
stateOfItemsProvider = rememberStateOfItemsProvider(content),
modifier = modifier,
state = state,
contentPadding = contentPadding,
flingBehavior = flingBehavior,
horizontalAlignment = horizontalAlignment,
verticalArrangement = verticalArrangement,
isVertical = true,
reverseLayout = reverseLayout
)
}
modifier: Modifier = Modifier:
modifier 用于设置 LazyColumn 的外观和布局特性,如宽度、高度、边距等。
state: LazyListState = rememberLazyListState():
state 管理列表的滚动状态,包括当前位置、可见项等。
rememberLazyListState() 是一个用于记住列表状态的函数。
contentPadding: PaddingValues = PaddingValues(0.dp):
contentPadding 设置列表内容的内边距。
默认值是 0.dp,表示没有内边距。
reverseLayout: Boolean = false:
reverseLayout 决定列表的滚动方向。
如果为 true,列表将从底部开始显示到顶部;否则,从顶部开始显示到底部。
verticalArrangement: Arrangement.Vertical:
verticalArrangement 定义了垂直方向上子项的排列方式。
根据 reverseLayout 的值,设置为 Arrangement.Top 或 Arrangement.Bottom。
horizontalAlignment: Alignment.Horizontal = Alignment.Start:
horizontalAlignment 设置列表项在水平方向上的对齐方式。
默认是 Alignment.Start,即左对齐。
flingBehavior: FlingBehavior = ScrollableDefaults.flingBehavior():
flingBehavior 决定列表滚动的惯性行为。
默认使用 ScrollableDefaults.flingBehavior(),这是一个标准的惯性滚动行为。
content: LazyListScope.() -> Unit:
content 是一个 lambda 表达式,用于定义列表中的项。
在 LazyListScope 中,你可以使用 items 或 item 等函数来添加列表项。
我们发现,怎么每个函数都带一个【Modifier】?
【Modifier】 是 Jetpack Compose 中的一个核心概念,用于修改和定制组件的布局、外观和行为。每个 Compose 组件都可以接收一个 Modifier 参数来进行这些调整。
以下是一些常用的 Modifier 属性及其使用方法:
padding:
作用: 设置组件的内边距。
用法: Modifier.padding(all = 16.dp) 或 Modifier.padding(start = 8.dp, end = 8.dp)
background:
作用: 设置组件的背景颜色或背景图像。
用法: Modifier.background(Color.Gray) 或 Modifier.background(brush = Brush.linearGradient(listOf(Color.Red, Color.Blue)))
fillMaxWidth:
作用: 使组件宽度填充父容器的最大宽度。
用法: Modifier.fillMaxWidth()
fillMaxHeight:
作用: 使组件高度填充父容器的最大高度。
用法: Modifier.fillMaxHeight()
width 和 height:
作用: 设置组件的宽度和高度。
用法: Modifier.width(100.dp) 或 Modifier.height(200.dp)
size:
作用: 同时设置组件的宽度和高度。
用法: Modifier.size(100.dp)
align:
作用: 设置组件在父容器中的对齐方式。
用法: Modifier.align(Alignment.Center)
border:
作用: 为组件添加边框。
用法: Modifier.border(2.dp, Color.Black)
clickable:
作用: 设置组件可以点击,并指定点击事件。
用法: Modifier.clickable { /* Handle click */ }
graphicsLayer:
作用: 应用图形变换,如缩放、旋转、透明度等。
用法: Modifier.graphicsLayer(scaleX = 1.5f, scaleY = 1.5f)
通过组合和链式调用不同的 Modifier,可以很方便地实现复杂的 UI 布局和样式。