Jetpack Compose 是 Android 推荐的用于构建原生界面的现代工具包。它简化并加速了 Android 上的界面开发,它支持丰富的 UI 控件。
1,下面的代码示例应用到了Text,Switch,Slide等。
在模块级的build.gradle.kts(:app)文件中,
dependencies {
implementation(libs.material3.android)
}
在libs.versions.toml文件中
[versions]
material3Android = "1.3.2"
[libraries]
material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
// 加入上述依赖后,就可以使用material3的相关特性。
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import android.view.Window
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
// 用于设置背景,在 Compose 中,使用Modifier.background()可以为组件设置背景颜色、渐变等效果。
import androidx.compose.foundation.background
// 用于创建垂直排列子组件的布局容器。在Column中,子组件会按照添加的顺序从上到下依次排列。
import androidx.compose.foundation.layout.Column
// 用于创建水平排列子组件的布局容器。在Row中,子组件会按照添加的顺序从左到右依次排列。
import androidx.compose.foundation.layout.Row
// 属于布局修饰符。Modifier.fillMaxWidth()可以使组件的宽度填充其父容器的宽度。
import androidx.compose.foundation.layout.fillMaxWidth
// 提供了设置内边距的功能。Modifier.padding()可用于为组件设置四周、单边或对称边距。
import androidx.compose.foundation.layout.padding
// 引入了 Material 3 风格的开关组件。通过Switch可以创建一个可切换状态的开关按钮,用于实现开启 / 关闭功能.
import androidx.compose.material3.Switch
// 提供了用于显示文本的组件。可以设置文本的内容、颜色、字体大小、粗细等属性。
import androidx.compose.material3.Text
// 用于创建滑块组件。滑块可用于在一定范围内选择数值,比如音量调节、亮度调节等场景。
import androidx.compose.material3.Slider
// Composable 是 Compose 中非常重要的注解。被@Composable注解的函数可以用于构建 UI 界面,这些函数在调用时会被 Compose 框架处理,根据状态变化重新组合 UI。
import androidx.compose.runtime.Composable
// mutableIntStateOf专门用于创建可变的整数值状态。
import androidx.compose.runtime.mutableIntStateOf
// mutableStateOf用于创建可变状态。
import androidx.compose.runtime.mutableStateOf
// getValue和setValue是 Kotlin 委托相关的导入,用于支持属性委托的语法糖,在处理可变状态变量时起到关键作用。
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
// Modifier 用于为 Compose 组件添加各种修饰。
import androidx.compose.ui.Modifier
// 用于表示颜色。在 Compose 中,可以使用Color类定义的常量(如Color.Red、Color.Blue)或通过 RGB、ARGB 值创建自定义颜色,用于设置组件的前景色、背景色等。
import androidx.compose.ui.graphics.Color
// 用于在开发环境中预览 Composable 函数的 UI 效果。被@Preview注解的函数会在 Android Studio 的 Compose Preview 面板中显示对应的 UI,方便开发者实时查看和调整 UI,而无需在真实设备或模拟器上运行应用。
import androidx.compose.ui.tooling.preview.Preview
// dp:定义了密度无关像素(dp)单位。在 Compose 中,使用dp来指定尺寸、边距、间距等,以确保在不同分辨率和屏幕密度的设备上都能保持一致的视觉效果。
import androidx.compose.ui.unit.dp
// sp:定义了缩放像素(sp)单位,主要用于设置文本大小。sp会根据用户系统设置的字体缩放比例进行调整,保证文本在不同设备和用户设置下都能清晰可读。
import androidx.compose.ui.unit.sp
// setContent:用于在 Android 活动中设置 Compose 内容。在Activity中,通过setContent函数将 Compose 构建的 UI 设置为活动的内容,实现 Compose 与 Android 原生 Activity 的集成。
import androidx.activity.compose.setContent
// Arrangement:用于定义布局容器中子元素的排列方式。
import androidx.compose.foundation.layout.Arrangement
// wrapContentWidth:属于布局修饰符。Modifier.wrapContentWidth()可以让组件的宽度根据其内容自动调整,而不是填充父容器的宽度。常用于需要自适应宽度的场景。
import androidx.compose.foundation.layout.wrapContentWidth
// SliderDefaults:提供了Slider组件的默认样式和配置。可以使用SliderDefaults中的属性来定制Slider的外观,如颜色、拇指大小等。
import androidx.compose.material3.SliderDefaults
// Alignment:用于定义组件在布局容器中的对齐方式。比如Alignment.Center表示在水平和垂直方向都居中对齐;Alignment.TopStart表示在左上角对齐等。
import androidx.compose.ui.Alignment
// LocalContext:提供了对当前Context的访问。在 Compose 中,有些操作(如加载资源、获取系统服务等)需要Context,通过LocalContext.current可以获取当前的Context对象。
import androidx.compose.ui.platform.LocalContext
// stringResource:用于从资源文件中获取字符串资源。在多语言支持场景下非常有用,通过资源 ID 可以获取不同语言版本的字符串。
import androidx.compose.ui.res.stringResource
// FontWeight:用于设置文本的字体粗细。可以使用FontWeight类中的常量(如FontWeight.Bold、FontWeight.Normal)来设置文本的粗细程度。
import androidx.compose.ui.text.font.FontWeight
import com.eric.rong.R
class EricBrightnessActivity : AppCompatActivity() {
// companion object :定义一个伴生对象,用于存放静态常量。
companion object {
const val TAG = "EricBrightnessActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// setContent :使用 Jetpack Compose 设置 Activity 的内容视图,
// 调用 EricBrightnessSettingUI 组合函数来构建 UI。
setContent {
EricBrightnessSettingUI()
}
}
// @SuppressLint :抑制指定的 Lint 警告,这里抑制 UnrememberedMutableState 和 StringFormatInvalid 警告。
@SuppressLint("UnrememberedMutableState", "StringFormatInvalid")
// @Composable :标记该函数为组合函数,用于构建 UI。
@Composable
fun EricBrightnessSettingUI() {
var autoBrightness = getAutoBrightness()
Log.v(TAG, "autoBrightness: $autoBrightness")
// 使用 mutableStateOf 创建一个可变状态,用于跟踪自动开关的状态。
var isAutoEnabled by mutableStateOf(autoBrightness)
var brightness = getScreenBrightness().toInt()
if (autoBrightness) {
brightness = getDefaultBrightness().toInt()
}
Log.v(TAG, "brightness: $brightness")
// 使用 mutableIntStateOf 创建一个可变的整数状态,用于跟踪当前屏幕亮度值。
var brightnessValue by mutableIntStateOf(brightness)
// 获取当前的 Context 对象
val context = LocalContext.current
// Column :创建一个垂直布局容器。
Column(
// Modifier :用于修改组件的属性,这里设置容器宽度填充父布局、内边距为 20dp,背景颜色为深灰色。
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
.background(Color.Gray)
) {
// Text :显示文本,文本内容从资源文件中获取,颜色为白色,字体大小为 25sp,居中显示并在底部添加 20dp 的内边距。
Text(
text = stringResource(id = R.string.setting_brightness),
color = Color.White,
fontSize = 25.sp,
// 将文本的字体粗细设置为粗体。FontWeight 是 Compose 提供的用于表示字体粗细的枚举类。
fontWeight = FontWeight.Bold,
// 使用 Modifier 为 Text 组件添加修饰。
modifier = Modifier
.fillMaxWidth()
// wrapContentWidth 是 Android Compose 中 Modifier 的一个扩展函数,用于设置组件在水平方向上的宽度策略.
// wrapContentWidth 会让组件的宽度刚好包裹其内容,即组件的宽度会根据其内部内容的实际宽度来确定,而不是填充父容器的可用宽度。
// wrapContentWidth() 这个重载函数没有参数,会让组件的宽度刚好包裹内容,并且默认将内容在水平方向上左对齐。
// wrapContentWidth(align: Alignment.Horizontal = Alignment.Start) 这个重载函数接受一个 Alignment.Horizontal 类型的参数,用于指定内容在水平方向上的对齐方式。
// 常见的对齐方式有:Alignment.Start :左对齐(默认值); Alignment.CenterHorizontally :居中对齐; Alignment.End :右对齐
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(bottom = 20.dp)
)
// Row :创建一个水平布局容器,设置宽度填充父布局,内边距为左、右 95dp,上 20dp,水平方向两端对齐,垂直方向居中对齐。
// Row 是 Jetpack Compose 中的一个布局组件,用于在水平方向上排列其子组件。
Row(
// 使用 Modifier 为 Row 布局添加修饰。
modifier = Modifier
// 让 Row 布局的宽度填充其父容器的最大宽度
.fillMaxWidth()
// 为 Row 布局添加内边距
.padding(start = 95.dp, end = 95.dp, top = 20.dp),
// 设置 Row 布局中子组件的水平排列方式
// 在 Jetpack Compose 中,Arrangement 是一个用于定义布局容器(如 Row、Column、LazyRow、LazyColumn 等)中子元素排列方式的工具类。
// Arrangement.SpaceBetween 表示子组件会在水平方向上均匀分布,并且第一个子组件会紧贴布局的左侧,最后一个子组件会紧贴布局的右侧,中间的空白空间会平均分配给各个子组件之间。
// Arrangement.Start 将子元素从布局容器的起始端(通常是左侧)开始排列,子元素之间没有额外的间距。也即是元素从左到右依次排列,紧密相连
// Arrangement.End 将子元素从布局容器的结束端(通常是右侧)开始排列,子元素之间没有额外的间距。
// Arrangement.Center 将子元素在布局容器的水平方向上居中排列,子元素之间没有额外的间距。也即多个子元素会在水平方向上居中显示,彼此相邻。
// Arrangement.SpaceAround 子元素会在水平方向上均匀分布,每个子元素的两侧会有相等的间距,并且布局容器的起始端和结束端与第一个和最后一个子元素之间的间距是子元素之间间距的一半。也即是元素之间以及元素与容器边缘都有间距,边缘间距是元素间间距的一半。
// Arrangement.SpaceEvenly 子元素会在水平方向上均匀分布,所有相邻子元素之间的间距以及第一个子元素与布局容器起始端、最后一个子元素与布局容器结束端之间的间距都相等。
// Arrangement.spacedBy 指定子元素之间的固定间距。比如 horizontalArrangement = Arrangement.spacedBy(16.dp) 表示元素会从左到右依次排列,且元素之间的间距为 16 dp。
horizontalArrangement = Arrangement.SpaceBetween,
// 设置 Row 布局中子组件的垂直对齐方式。
// Alignment.CenterVertically 表示子组件会在垂直方向上居中对齐。
verticalAlignment = Alignment.CenterVertically
) {
Text(
// 使用 stringResource 函数从资源文件中获取字符串资源
text = stringResource(id = R.string.lock_screen_title),
color = Color.White,
fontSize = 20.sp
)
// Switch :创建一个开关组件,当开关状态改变时,更新 isAutoEnabled 状态. Switch 是 Jetpack Compose 中用于创建开关组件的组件。
Switch(
// onCheckedChange = {...}:设置开关状态改变时的回调函数。当用户切换开关的状态时,会执行这个回调函数。
onCheckedChange = {
// 将开关的新状态赋值给 isAutoEnabled 变量。it 是 Kotlin 中 lambda 表达式的默认参数名,代表开关的新状态(true 或 false)。
isAutoEnabled = it
Log.v(TAG, "isAutoEnabled: $isAutoEnabled")
setAutoBrightness(isAutoEnabled)
},
checked = isAutoEnabled,
)
}
// Row :创建一个水平布局容器,设置宽度填充父布局,内边距为左、右 95dp,上 20dp,垂直方向居中对齐。
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 95.dp, end = 95.dp, top = 20.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
// <string name="current_brightness">current brightness: %1$d%</string>
text = context.getString(R.string.setting_brightness, brightnessValue),
color = Color.White,
fontSize = 16.sp
)
}
// Slider :创建一个滑动条组件。
Slider(
// onValueChange :当滑动条的值改变时,更新 brightnessValue 状态
onValueChange = {
brightnessValue = it.toInt()
Log.v(TAG, "Brightness value changed to: $brightnessValue")
setScreenBrightness(window,brightnessValue.toFloat())
},
// value :设置滑动条的当前值。
value = brightnessValue.toFloat(),
// valueRange :设置滑动条的值范围为 0 到 100。
valueRange = 0f..100f,
// modifier :设置滑动条宽度填充父布局,内边距为左、右 95dp,上 20dp。
modifier = Modifier
.fillMaxWidth()
.padding(start = 95.dp, end = 95.dp, top = 20.dp),
// colors :设置滑动条的颜色,包括激活部分、未激活部分和滑块的颜色。
colors = SliderDefaults.colors(
activeTrackColor = Color(0xFF4CAF50), // 滑动条左侧激活部分颜色
inactiveTrackColor = Color.Gray, // 滑动条右侧未激活部分颜色
thumbColor = Color(0xFF4CAF50) // 滑块颜色
)
)
}
}
// @Preview :标记该函数为预览函数,用于在 Android Studio 的预览窗口中查看 EricBrightnessSettingUI 组合函数构建的 UI。
@Preview(showBackground = true)
@Composable
fun ScreenBrightnessSettingsPreview() {
// 调用 EricBrightnessSettingUI 组合函数来构建预览 UI。
EricBrightnessSettingUI()
}
}
2,下面的代码将会用到Image,Checkbox,Text。
// @SuppressLint("UnrememberedMutableState"):这是一个注解,用于抑制特定的 Lint 警告。这里抑制的是关于未正确使用可变状态的警告,通常在使用mutableStateOf时,状态变量应该被remember包裹以避免在重组时丢失状态,但此处使用该注解来忽略此检查。
@SuppressLint("UnrememberedMutableState")
// @Composable:这是 Jetpack Compose 中的注解,标记该函数为可组合函数,意味着它用于构建 UI 界面。
@Composable
fun AssistantSettings() {
// 使用mutableStateOf创建一个可变状态变量,by关键字用于委托属性,sensorEnabled,同时 Compose 能追踪其变化并相应地更新 UI。
var sensorEnabled by mutableStateOf(false)
var accelerationEnabled by mutableStateOf(false)
var isFahrenheit by mutableStateOf(true)
// 使用remember和mutableStateOf创建一个可变状态变量backToSetting,
// remember用于在 Compose 重组时保留状态,避免状态丢失。
// 该变量用于记录是否点击了返回设置页面的操作,初始值为false。
var backToSetting by remember { mutableStateOf(false) }
var backToMenu by remember { mutableStateOf(false) }
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight()
.padding(16.dp)
.background(Color.Black)
) {
Text(
text = "text1",
color = Color.White,
fontSize = 18.sp,
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally)
.padding(bottom = 16.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.align(Alignment.CenterHorizontally)
) {
// Box:Compose 中的布局容器,用于包裹其他组件,并根据子组件大小调整自身大小。
Box(
modifier = Modifier
.wrapContentHeight()
.padding(16.dp)
.wrapContentWidth()
.padding(start = 75.dp)
// 将Box在垂直方向上居中对齐
.align(Alignment.CenterVertically)
) {
// Image:Compose 中用于显示图片的组件。
Image(
// 从资源文件中加载图片资源
painter = painterResource(id = R.drawable.eric_arrow),
// 为图片提供描述,用于无障碍服务,方便屏幕阅读器等辅助工具向视障用户解释图片内容。
contentDescription = "back",
modifier = Modifier
.size(32.dp)
// .clickable:使图片可点击,点击时执行相应操作
.clickable {
Log.v(TAG, "clicked image")
backToSetting = true
}
)
if (backToSetting) {
backToSetting = false
val cls = AliceActivity::class.java
val mainIntent = Intent(this@EricAssistantSettingActivity, cls)
startActivity(mainIntent)
}
}
// 其余设置内容
Column(
modifier = Modifier
// .weight(1f):使该Column在水平方向上占据剩余空间,1f表示权重为 1,即按比例分配剩余空间。
.weight(1f)
.wrapContentHeight()
.padding(start = 16.dp, end = 75.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = "text2",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Checkbox:Compose 中的复选框组件。
Checkbox(
// 根据sensorEnabled状态显示复选框是否被选中
checked = sensorEnabled,
// 当复选框状态改变时,更新sensorEnabled状态变量,it表示新的复选框状态(true或false)。
onCheckedChange = {
sensorEnabled = it
}
)
Text(
text = "text3",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = accelerationEnabled,
onCheckedChange = { accelerationEnabled = it })
Text(
text = "text4",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = accelerationEnabled,
onCheckedChange = { accelerationEnabled = it })
Text(
text = "text5",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "temp unit",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = isFahrenheit,
onClick = { isFahrenheit = true }
)
Text(
text = "(℉)",
color = Color.White,
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = !isFahrenheit,
onClick = { isFahrenheit = false }
)
Text(
text = "(℃)",
color = Color.White,
)
}
}
}
Box(
modifier = Modifier
.wrapContentHeight()
.padding(16.dp)
.wrapContentWidth()
.padding(start = 75.dp)
.align(Alignment.CenterHorizontally)
) {
Image(
painter = painterResource(id = R.drawable.eric_back),
contentDescription = "text6",
modifier = Modifier
.size(32.dp)
.clickable {
backToMenu = true
}
)
if (backToMenu) {
backToMenu = false
val cls = MainActivity::class.java
val mainIntent = Intent(this@EricAssistantSettingActivity, cls)
startActivity(mainIntent)
}
}
}
}
// 定义了一个名为SensorAssistantSettingActivityPreview的可组合函数,在其中调用AssistantSettings函数来展示设置页面的预览。
@Composable
@Preview
fun SensorAssistantSettingActivityPreview() {
AssistantSettings()
}
3,下面的代码将会用到LazyColumn,Spacer,Button等。
// LazyColumn 是一个高效的列表布局,适用于显示大量数据,
// items 是其扩展函数,用于遍历数据并为每个数据项创建子项。
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
// remember:用于记住状态,确保在 UI 重组时状态保持不变。
import androidx.compose.runtime.remember
// ContentScale:用于设置图片的缩放模式。
import androidx.compose.ui.layout.ContentScale
// painterResource:用于从资源中加载图片资源。
import androidx.compose.ui.res.painterResource
// @Composable:这是 Jetpack Compose 中的注解,用于标记一个函数是可组合函数。可组合函数用于构建 UI 界面,会在 UI 重组时被调用。
@Composable
fun MusicDevicesUI() {
// 使用 remember 函数来记住 musicDevices 的状态。
// remember 函数会在 UI 重组时保持状态不变,避免重复创建。
// 创建一个包含设备名称的不可变列表 deviceNames,用于后续初始化音乐设备列表。
val deviceNames = listOf("设备1", "设备2", "设备3", "设备4", "设备5")
// 使用 remember 函数来记住 musicDevices 列表的状态,确保在组件重组时不会重新创建。
// mutableStateListOf 创建一个可变的状态列表,apply 函数用于对列表进行操作。
// 通过 forEach 遍历 deviceNames 列表,为每个设备名称创建一个 MusicDevice 对象,并将其 isChecked 状态初始化为 false,然后添加到 musicDevices 列表中。
val musicDevices = remember {
mutableStateListOf<MusicDevice>().apply {
deviceNames.forEach {
name ->
add(MusicDevice(name, mutableStateOf(false)))
}
}
}
// 声明一个名为 selectedDevices 的可变状态列表,用于存储被选中的音乐设备,同样使用 remember 来保持状态。
val selectedDevices = remember { mutableStateListOf<MusicDevice>() }
// 定义了一个函数 updateSelectedDevices,用于在点击保存按钮时更新 selectedDevices 列表。首先清空 selectedDevices 列表,然后遍历 musicDevices 列表,当设备的 isChecked 状态为 true 时,将该设备添加到 selectedDevices 列表中。
fun updateSelectedDevices() {
selectedDevices.clear()
musicDevices.forEach { device ->
if (device.isChecked.value) {
selectedDevices.add(device)
}
}
}
// 定义了一个函数 updateDeviceCheckStates,用于根据 selectedDevices 列表更新所有设备的选中状态。遍历 musicDevices 列表,将每个设备的 isChecked 状态设置为该设备是否在 selectedDevices 列表中。
fun updateDeviceCheckStates() {
musicDevices.forEach { device ->
device.isChecked.value = selectedDevices.contains(device)
}
}
// Column:这是 Jetpack Compose 中的布局组件,用于垂直排列其子组件。
Column(
// 使用 Modifier 对 Column 进行修饰。
// fillMaxSize() 使 Column 占据整个可用空间,background(Color.Black) 将 Column 的背景颜色设置为黑色。
modifier = Modifier.fillMaxSize().background(Color.Black),
// 设置子组件在垂直方向上的排列方式为居中排列。
verticalArrangement = Arrangement.Center,
// 设置子组件在水平方向上的对齐方式为水平居中对齐。
horizontalAlignment = Alignment.CenterHorizontally
) {
// Row:这是 Jetpack Compose 中的布局组件,用于水平排列其子组件。
Row(
// 使用 Modifier 使 Row 占据父容器的最大宽度。
modifier = Modifier.fillMaxWidth(),
// 设置子组件在水平方向上从起始位置(通常是左侧)开始排列。
horizontalArrangement = Arrangement.Start,
// 设置子组件在垂直方向上居中对齐。
verticalAlignment = Alignment.CenterVertically
) {
// Text:这是 Jetpack Compose 中用于显示文本的组件。
Text(
text = "MusicDev",
color = Color.White,
// 设置文本的样式,这里指定字体大小为 20sp(缩放像素)。
style = TextStyle(fontSize = 20.sp),
modifier = Modifier
.wrapContentWidth()
// .weight(1f)
.padding(start = 176.dp, top = 30.dp, bottom = 34.dp),
// TextAlign.Center:设置文本的水平对齐方式为居中对齐。
textAlign = TextAlign.Center
)
// Spacer:这是 Jetpack Compose 中的一个组件,用于在布局中添加空白空间。
// modifier = Modifier.width(64.dp) 设置空白空间的宽度为 64dp,这里用于在文本和图片之间添加间隔。
Spacer(modifier = Modifier.width(64.dp))
// Image:这是 Jetpack Compose 中用于显示图片的组件。
Image(
// 使用 painterResource 函数从资源文件中加载图片资源
painter = painterResource(id = R.drawable.eric_iv),
contentDescription = null,
// 使用 Modifier 设置图片的大小为 64dp。
modifier = Modifier.size(64.dp)
)
}
// LazyColumn:这是 Jetpack Compose 中的一个高效的列表布局组件,适用于显示大量数据,只会在需要时才创建和显示子项,以提高性能。
LazyColumn (
// Modifier.weight(1f):使用 Modifier 为 LazyColumn 设置权重为 1,使其占据 Column 中剩余空间的相应比例。
modifier = Modifier.weight(1f)
) {
// items(musicDevices):这是 LazyColumn 的一个扩展函数,用于遍历 musicDevices 列表,并为每个列表项创建一个子项。device 是一个参数,代表 musicDevices 列表中的每个 MusicDevices 对象。
items(musicDevices) {
device ->
// Row:用于水平排列每个设备项的子组件。
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
.background(if (device.isChecked.value) Color.Gray else Color.Transparent)
.clickable {
device.isChecked.value =!device.isChecked.value
},,
// Arrangement.SpaceBetween:设置子组件在水平方向上均匀分布,两端对齐。
horizontalArrangement = Arrangement.SpaceBetween,
// Alignment.CenterVertically:设置子组件在垂直方向上居中对齐。
verticalAlignment = Alignment.CenterVertically
) {
// Box:这是 Jetpack Compose 中的一个布局容器,用于堆叠子组件。
Box(
modifier = Modifier
.padding(start = 76.dp)
.size(64.dp)
) {
Image(
painter = painterResource(id = R.drawable.eric_iv),
contentDescription = null,
// ContentScale.Crop:设置图片的缩放模式为裁剪模式,使图片填满 Box 并可能裁剪掉部分内容。
contentScale = ContentScale.Crop,
// Modifier.fillMaxSize():使图片占据 Box 的整个空间。
modifier = Modifier.fillMaxSize()
)
}
Text(
text = device.name,
color = Color.White,
modifier = Modifier
.padding(start = 32.dp)
.weight(1f)
)
// Checkbox:这是 Jetpack Compose 中的复选框组件。
Checkbox(
// onCheckedChange = {... }:当复选框的状态发生变化时,更新设备的 isChecked 属性。
checked = device.isChecked.value,
onCheckedChange = { isChecked ->
device.isChecked.value = isChecked
},
modifier = Modifier.padding(end = 64.dp)
)
}
}
}
// Button:这是 Jetpack Compose 中的按钮组件。
Button(
// onClick = {... }:点击按钮时的回调函数
onClick = {
Log.v(TAG, "btn is clicked")
},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 196.dp, vertical = 14.dp)
) {
// Text(text = "OK"):在按钮中显示文本 “OK”。
Text(text = "OK")
}
}
}
@Preview(showBackground = true)
@Composable
fun MusicDevicesUIPreview() {
MusicDevicesUI()
}
// 定义了一个数据类 MusicDevice,用于表示音乐设备。它包含两个属性:
//name:表示设备的名称,是可变的字符串类型。
//isChecked:表示设备是否被选中的状态,使用 MutableState<Boolean> 来包装布尔值,使其成为可观察的状态,以便在状态变化时通知 UI 进行更新。
data class MusicDevice(
var name: String,
var isChecked: MutableState<Boolean>
)
下面的示例也是使用LazyColumn实现一个上下滑动的列表,可以点击列表中的项目启动跳转。
// @SuppressLint 是一个注解,用于抑制编译器的特定警告。这里抑制了 UnrememberedMutableState 警告,该警告通常在使用可变状态但未正确记忆时触发。
@SuppressLint("UnrememberedMutableState")
// @Composable 注解表明这是一个 Jetpack Compose 组件函数。EricSceneUI 是组件的名称,用于构建特定的用户界面。
@Composable
fun EricSceneUI() {
// LocalContext.current 用于获取当前的 Android 上下文,在后续启动新的 Activity 时会用到。
val context = LocalContext.current
// ericReturnImg 和 ericToMain 是布尔类型的可变状态,用于控制是否返回到主界面。
// remember 函数用于记忆状态,确保在组件重新组合时状态不会丢失。
var ericReturnImg by remember {
mutableStateOf(false)
}
var ericToMain by remember {
mutableStateOf(false)
}
var selectedIndex by mutableIntStateOf(0)
// rememberLazyListState 用于创建并记忆一个 LazyListState 对象,该对象用于控制 LazyColumn 的滚动状态。
val ericListState = rememberLazyListState()
val itemLists = listOf("List0","List1","List2","List3","List4","List5")
// LaunchedEffect 用于在组件首次组合时启动一个协程。
LaunchedEffect(Unit) {
// snapshotFlow 用于创建一个流,该流会在 ericListState.firstVisibleItemIndex 发生变化时发出新的值。
// collect 用于收集流中的值,当 firstVisibleItemIndex 变化时,会获取当前可见项的信息,并将中间项的索引赋值给 selectedIndex。
snapshotFlow {
ericListState.firstVisibleItemIndex
}.collect {
val visibleItemsInfo = ericListState.layoutInfo.visibleItemsInfo
if (visibleItemsInfo.isNotEmpty()) {
val midIndex = visibleItemsInfo.size / 2
selectedIndex = visibleItemsInfo[midIndex].index
}
}
}
// Box 是一个布局组件,用于将其子组件堆叠在一起。
// Modifier.fillMaxSize() 使 Box 填充整个可用空间,background(Color.Black) 将背景颜色设置为黑色。
Box(
modifier = Modifier.fillMaxSize()
.background(Color.Black)
) {
// IconButton 是一个可点击的图标按钮,点击时将 ericReturnImg 设置为 true。
IconButton(
onClick = {
ericReturnImg = true
},
modifier = Modifier.align(Alignment.CenterStart)
) {
// Icon 用于显示一个图标,这里使用了 Android 系统自带的 ic_menu_revert 图标。
Icon(
painter = painterResource(id = android.R.drawable.ic_menu_revert),
tint = Color.White,
contentDescription = "EricBack"
)
// 当 ericReturnImg 为 true 时,创建一个 Intent 对象,用于启动 MainActivity,并调用 startActivity 方法启动该 Activity。
if (ericReturnImg) {
ericReturnImg = false
val mainIntent = Intent(context, MainActivity::class.java)
startActivity(mainIntent)
}
}
// LazyColumn 是一个懒加载的垂直列表组件,使用 ericListState 控制滚动状态。
LazyColumn(
state = ericListState,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.padding(start = 96.dp)
.padding(top = 110.dp, bottom = 130.dp)
.height((64.dp + 8.dp * 5) * 6)
) {
// items 用于为列表中的每个元素创建一个子组件。
items(itemLists.size) {
index ->
// animateDpAsState 和 animateColorAsState 用于创建动画效果,当选中项发生变化时,列表项的内边距和文本颜色会平滑过渡。
val isCentered by animateDpAsState(
if (index == selectedIndex) 32.dp else 16.dp,
label = "itemPadding"
)
val isSelected = index == selectedIndex
val leftPadding = when (index - selectedIndex) {
-2, 2 -> 64.dp
-1, 1 -> 32.dp
0 -> 16.dp
else -> 64.dp
}
val textColor by animateColorAsState(
if (isSelected) Color.White else Color.Gray,
label = "textColor"
)
// Row 用于水平排列图标和文本,点击列表项时将 selectedIndex 设置为当前项的索引。
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.padding(horizontal = isCentered)
.padding(vertical = 10.dp)
.padding(start = leftPadding)
.fillMaxWidth()
.clickable {
selectedIndex = index
}
) {
Icon(
painter = painterResource(id = android.R.drawable.ic_menu_info_details),
contentDescription = null,
tint = textColor,
modifier = Modifier.padding(end = 16.dp)
)
Text(
text = itemLists[index],
style = MaterialTheme.typography.bodyLarge,
color = if (index == selectedIndex) Color.White else Color.Gray
)
// 当列表项被选中时,会显示一个播放图标按钮,点击该按钮会打印日志。
if (isSelected) {
IconButton(
onClick = {
Log.v(TAG, "start...")
},
modifier = Modifier
.align(Alignment.CenterVertically)
.fillMaxWidth()
.weight(1f)
.wrapContentWidth(Alignment.End)
.padding(end = 64.dp)
) {
Icon(
painter = painterResource(id = android.R.drawable.ic_media_play),
tint = Color.White,
contentDescription = "Start"
)
}
}
}
}
}
IconButton(
onClick = {
ericToMain = true
},
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 96.dp)
) {
Icon(
painter = painterResource(id = android.R.drawable.ic_menu_add),
tint = Color.White,
contentDescription = "Eric Bottom Icon"
)
}
}
}
对于上面的代码在实现跳转时可以加上一个动画,动画效果是当前页面逐渐变小之后跳转到新页面。
// animateFloatAsState 是 Compose 中用于创建一个可动画化的 Float 状态的函数。
// 通过 by 关键字使用委托属性, scale 会随着动画的进行自动更新。
val scale by animateFloatAsState(
// targetValue :根据 ericToMain 这个布尔值来决定动画的目标值。如果 ericToMain 为 true ,目标值是 0.1f ;否则是 1f 。
targetValue = if (ericToMain) 0.1f else 1f,
// animationSpec :定义动画的规格。这里使用 tween 函数创建一个补间动画,
animationSpec = tween(
// 动画持续时间为 500 毫秒,
durationMillis = 500,
// 缓动函数为 FastOutSlowInEasing ,意味着动画开始时速度较快,结束时速度变慢。
easing = FastOutSlowInEasing
),
// label :用于调试目的的标签,方便在调试工具中识别这个动画。
label = "scale"
)
// alpha 值通常是一个范围在 0 到 1 或者 0 到 255 之间的数值,用来表示一个颜色或者对象的透明程度。
// 取值范围 0 - 1 :0 表示完全透明,1 表示完全不透明。
// 取值范围 0 - 255 :0 表示完全透明,255 表示完全不透明。
// alpha 值为 0 时,意味着该元素完全透明,就像一块完全清澈的玻璃,不会对背后的内容产生遮挡,观察者看到的是元素背后的内容。
val alpha by animateFloatAsState(
targetValue = if (ericToMain) 0.3f else 1f,
animationSpec = tween(
durationMillis = 500,
easing = FastOutSlowInEasing
),
label = "alpha"
)
// LaunchedEffect 是 Compose 中的一个函数,用于在组合中启动一个协程。当 ericToMain 的值发生变化时,协程会重新启动。
LaunchedEffect(ericToMain) {
if (ericToMain) {
delay(400)
ericToMain = false
val cls = MainActivity::class.java
val mainIntent = Intent(context, cls)
startActivity(mainIntent)
}
}
// Box 是一个布局组件,用于将其子组件堆叠在一起。
// Modifier.fillMaxSize() 使 Box 填充整个可用空间,background(Color.Black) 将背景颜色设置为黑色。
Box(
modifier = Modifier.fillMaxSize()
.background(Color.Black)
// 根据前面定义的 scale 动画值对 Box 进行缩放。
.scale(scale)
// 根据前面定义的 alpha 动画值设置 Box 的透明度。
.alpha(alpha)
) {
// IconButton 是一个可点击的图标按钮,点击时将 ericToMain 设置为 true。
IconButton(
onClick = {
ericToMain = true
},
modifier = Modifier.align(Alignment.CenterStart)
) {
// Icon 用于显示一个图标,这里使用了 Android 系统自带的 ic_menu_revert 图标。
Icon(
painter = painterResource(id = android.R.drawable.ic_menu_revert),
tint = Color.White,
contentDescription = "Back"
)
}
}
4,下面的代码示例用到了Card,Progress,Animation
// Kotlin 注解,用于启用对 ExperimentalMaterial3Api 的访问。ExperimentalMaterial3Api 通常表示相关的 API 还处于实验阶段,使用它可能会有一些不稳定的情况,但可以提前体验新功能。
@OptIn(ExperimentalMaterial3Api::class)
// @Composable 是 Jetpack Compose 的注解,用于标记一个函数为可组合函数,可组合函数用于构建 UI 界面。
@Composable
fun setAnimationUI() {
// Surface 是 Jetpack Compose 中的一个容器组件,用于作为其他 UI 元素的背景表面。
Surface(
// 设置 Surface 的宽度填充整个父容器的宽度
modifier = Modifier.fillMaxWidth(),
color = Color.White
) {
// Column 是一个垂直排列子元素的布局容器。
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
// 设置子元素在垂直方向上的间距为 16dp
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// 顶部的 Card
// 创建了一个 Card 组件,用于显示一些内容
Card(
// 使用默认的修饰符
modifier = Modifier,
// 设置 Card 的形状为圆角矩形,圆角半径为 8dp。
shape = RoundedCornerShape(8.dp),
// 设置 Card 的阴影效果,阴影高度为 4dp。
elevation = CardDefaults.cardElevation(4.dp)
) {
// Text 组件:在 Card 内部显示文本 “Eric”。
Text(
text = "Eric",
modifier = Modifier.padding(16.dp),
// 设置文本的样式,字体大小为 20sp,字体粗细为粗体。
style = TextStyle(fontSize = 20.sp, fontWeight = FontWeight.Bold),
color = Color.Black
)
}
// 创建了一个 Row 布局,包含两个输入框。
Row(
modifier = Modifier.fillMaxWidth(),
// 使子元素在水平方向上均匀分布,两端对齐。
horizontalArrangement = Arrangement.SpaceBetween
) {
// 创建一个可变状态变量 text1,用于存储输入框的文本内容,初始值为空字符串。
// remember 函数用于在界面重组时保持状态。
var text1 by remember { mutableStateOf("") }
TextField(
// 将输入框的值绑定到 text1 变量。
value = text1,
// 当输入框的值发生变化时,更新 text1 变量。
onValueChange = { text1 = it },
// 设置输入框的标签文本为 “输入框 1”。
label = { Text("输入框1") },
// 设置输入框的颜色,聚焦时指示器颜色为蓝色,未聚焦时指示器颜色为灰色。
colors = textFieldColors(
focusedIndicatorColor = Color.Blue,
unfocusedIndicatorColor = Color.Gray
)
)
// 创建一个可变状态变量 text2,用于存储输入框的文本内容,初始值为空字符串。
var text2 by remember { mutableStateOf("") }
OutlinedTextField(
value = text2,
onValueChange = { text2 = it },
label = { Text("输入框2") }
)
}
// 创建了一个 Row 布局,包含一个圆形进度指示器和一个线性进度指示器。
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
// 创建一个圆形进度指示器。
CircularProgressIndicator()
// 创建一个线性进度指示器。
LinearProgressIndicator(
progress = { 0.5f },
modifier = Modifier.weight(1f),
)
}
// mutableStateOf(false):创建一个可变的状态对象,初始值为 false。
// 借助委托属性 by 把 showDialog 变量和状态对象关联起来。
// 这意味着 showDialog 的值发生改变时,Compose 会重新组合相关的 UI 部分。初始时,showDialog 为 false,表明对话框处于隐藏状态。
var showDialog by remember { mutableStateOf(false) }
Button(
onClick = { showDialog = true },
modifier = Modifier.fillMaxWidth()
) {
Text("点击弹出对话框")
}
var showAnimation by remember { mutableStateOf(false) }
// 对话框
if (showDialog) {
// Dialog:Compose 里用于创建对话框的组件。
Dialog(
// 当对话框被关闭时(例如点击对话框外部),将 showDialog 的值设为 false,从而隐藏对话框。
onDismissRequest = {
showDialog = false
}
) {
// Card:Compose 中的组件,用于创建卡片式布局,通常会有圆角和阴影效果。
Card(
modifier = Modifier.padding(16.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 在对话框中显示文本 “这是一个对话框”。
Text(text = "这是一个对话框")
Button(
onClick = {
showDialog = false
showAnimation = true
},
modifier = Modifier.fillMaxWidth()
) {
Text("确定")
}
}
}
}
}
// animateFloatAsState:Compose 中的动画函数,用于创建一个可动画化的 Float 类型的状态。
val alpha by animateFloatAsState(
// 目标值依据 showAnimation 的值来确定,若为 true 则目标值为 1f(完全不透明),若为 false 则目标值为 0f(完全透明)。
targetValue = if (showAnimation) 1f else 0f,
// 设置动画的时长为 1500 毫秒,采用 tween 动画曲线。
animationSpec = tween(1500),
label = "",
finishedListener = {
// 动画结束后将 showAnimation 设置为 false
showAnimation = false
}
)
// AnimatedVisibility:Compose 中的组件,用于在组件显示和隐藏时添加动画效果。
AnimatedVisibility(
visible = showAnimation,
// 组件显示时使用淡入动画
enter = fadeIn(animationSpec = tween(1500)),
// 组件隐藏时使用淡出动画
exit = fadeOut(animationSpec = tween(1500))
) {
Text(
text = "这是一个渐变动画",
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.wrapContentWidth(Alignment.CenterHorizontally),
style = TextStyle(fontSize = 36.sp, fontWeight = FontWeight.Bold),
// 设置文本颜色为蓝色,并且其透明度由 alpha 变量控制,随着动画的进行,文本会逐渐从透明变为不透明。
color = Color.Blue.copy(alpha = alpha)
)
}
}
}
}
@Preview(showBackground = true)
@Composable
fun EricAnimationActivityPreview() {
setAnimationUI()
}