compose--初入compose、资源获取、标准控件与布局

这篇具有很好参考价值的文章主要介绍了compose--初入compose、资源获取、标准控件与布局。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

compose正式发布已经一年多了,越来越多的开发人员选择使用它,声明式UI也是未来的一个主流趋势,本人也是一年前学习后,并没有真正的使用,所以本着边学习,边分享的心态,准备写个compose系列的文章
首先compose目前只支持kotlin,基于google对移动端的鸿图,未来应该也不会支持其他语言,和传统安卓的xml布局不同,compose是通过kotlin定义一个一个组件,由于是通过代码定义的组件,每个组件都可以很方便的重用,这点在UI开发时确实便利了不少。至于声明式UI和命令式UI的区别,相信你会在后续实际使用时有很大的感触

一、认识compose

通过官方文档我们可以了解到compose的编程思想。官方地址:https://developer.android.google.cn/jetpack/compose/mental-model

我这边也是根据官方文档,对重要的部分和自己的想法进行融合,来介绍什么是compose。这部分内容都是概念性的,但是贯穿整个compose的学习,应该进行着重深入理解

1. 重组
1.1 安卓传统UI

先来说在安卓传统UI,大致的流程就是xml中我们定义了一系列的布局(组件)和控件后,由ActivityonCreate()触发xml解析,生成View树DecorView,并ActivityhandleResumeActivity()ViewRootImpl绑定,通过Binder通信,交由由WindowManagerService创建surface进行渲染,最终呈现在手机屏幕

compose--初入compose、资源获取、标准控件与布局

当然了,我们只需要关注在onCreate()中设置xml即可,由于布局是一次性加载的,即生成View树的过程是同步进行的

1.2 compose UI

对与compose而言,每个可组合函数(组件)的调用可能发生在与调用方不同的线程上,即每个组件添加至View树的过程,都是通过协程进行的,上树的过程未必按代码调用的顺序执行

compose--初入compose、资源获取、标准控件与布局
1.3 什么是重组?

在compose中,每个可组合函数调用直至渲染完成,称之为重组
通过异步上树虽然带来了性能的提升,但是管理方面变得困难,所以compose规定,每个可组合函数都是独立运行的存在,可组合函数内部应该仅处理的UI操作,重组的发生的时机并不由我们控制,而是由compose内部自动管理,后续我们可以使用状态来通知compose进行重组

二、创建compose项目

推荐使用最新的android studio,低版本并不支持compose,也可以查看官方文档-快速入门:https://developer.android.google.cn/jetpack/compose/setup

1.创建项目

我这边尝鲜使用MD3风格的项目,实际开发中google也推荐:UI设计从MD2转变为MD3

compose--初入compose、资源获取、标准控件与布局
2.BOM

对于compose的版本管理,官方推荐使用BOM,导入BOM后的好处是:导入compose其他库组,都将使用BOM中定义的版本,后续更新,我们只需要更新BOM的版本即可。下面是官方给出的BOM:compose版本对应关系:

库组 版本 (2022.10.00) 版本 (2022.11.00)
androidx.compose.animation:animation 1.3.0 1.3.1
androidx.compose.animation:animation-core 1.3.0 1.3.1
androidx.compose.animation:animation-graphics 1.3.0 1.3.1
androidx.compose.foundation:foundation 1.3.0 1.3.1
androidx.compose.foundation:foundation-layout 1.3.0 1.3.1
androidx.compose.material:material 1.3.0 1.3.1
androidx.compose.material:material-icons-core 1.3.0 1.3.1
androidx.compose.material:material-icons-extended 1.3.0 1.3.1
androidx.compose.material:material-ripple 1.3.0 1.3.1
androidx.compose.material3:material3 1.0.0 1.0.1
androidx.compose.material3:material3-window-size-class 1.0.0 1.0.1
androidx.compose.runtime:runtime 1.3.0 1.3.1
androidx.compose.runtime:runtime-livedata 1.3.0 1.3.1
androidx.compose.runtime:runtime-rxjava2 1.3.0 1.3.1
androidx.compose.runtime:runtime-rxjava3 1.3.0 1.3.1
androidx.compose.runtime:runtime-saveable 1.3.0 1.3.1
androidx.compose.ui:ui 1.3.0 1.3.1
androidx.compose.ui:ui-geometry 1.3.0 1.3.1
androidx.compose.ui:ui-graphics 1.3.0 1.3.1
androidx.compose.ui:ui-test 1.3.0 1.3.1
androidx.compose.ui:ui-test-junit4 1.3.0 1.3.1
androidx.compose.ui:ui-test-manifest 1.3.0 1.3.1
androidx.compose.ui:ui-text 1.3.0 1.3.1
androidx.compose.ui:ui-text-google-fonts 1.3.0 1.3.1
androidx.compose.ui:ui-tooling 1.3.0 1.3.1
androidx.compose.ui:ui-tooling-data 1.3.0 1.3.1
androidx.compose.ui:ui-tooling-preview 1.3.0 1.3.1
androidx.compose.ui:ui-unit 1.3.0 1.3.1
androidx.compose.ui:ui-util 1.3.0 1.3.1
androidx.compose.ui:ui-viewbinding 1.3.0 1.3.1

工程中导入:

dependencies {
    def composeBom = platform('androidx.compose:compose-bom:2022.10.00')
    implementation composeBom

...
    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
    implementation 'androidx.activity:activity-compose'
    implementation "androidx.compose.ui:ui"
    implementation "androidx.compose.ui:ui-tooling-preview"
    implementation 'androidx.compose.material3:material3'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.1"
    debugImplementation "androidx.compose.ui:ui-tooling"
    debugImplementation "androidx.compose.ui:ui-test-manifest"
}
3.kotlin-compose compiler版本对应

BOM中不包含Compose编译器库,所以我们需要手动对应下kotlin版本与compose compiler版本,下面是两者的兼容关系,官网也可以查询到最新的对应关系:https://developer.android.google.cn/jetpack/androidx/releases/compose-kotlin

Compose Compiler 版本 兼容的 Kotlin 版本
1.4.0-alpha01 1.7.20
1.3.2 1.7.20
1.3.1 1.7.10
1.3.0 1.7.10
1.3.0-rc02 1.7.10
1.3.0-rc01 1.7.10
1.3.0-beta01 1.7.10
1.2.0 1.7.0
1.2.0-rc02 1.6.21
1.2.0-rc01 1.6.21
1.2.0-beta03 1.6.21
1.2.0-beta02 1.6.21
1.2.0-beta01 1.6.21
1.2.0-alpha08 1.6.20
1.2.0-alpha07 1.6.10
1.2.0-alpha06 1.6.10
1.2.0-alpha05 1.6.10
1.2.0-alpha04 1.6.10
1.2.0-alpha03 1.6.10
1.2.0-alpha02 1.6.10
1.2.0-alpha01 1.6.10
1.1.1 1.6.10
1.1.0 1.6.10
1.1.0-rc03 1.6.10
1.1.0-rc02 1.6.10
1.1.0-rc01 1.6.0
1.1.0-beta04 1.6.0
1.1.0-beta03 1.5.31
1.1.0-beta02 1.5.31
1.1.0-beta01 1.5.31
1.1.0-alpha06 1.5.31
1.1.0-alpha05 1.5.31
1.0.5 1.5.31
1.0.4 1.5.31
1.1.0-alpha04 1.5.30
1.1.0-alpha03 1.5.30
1.0.3 1.5.30
1.1.0-alpha02 1.5.21
1.1.0-alpha01 1.5.21
1.0.2 1.5.21
1.0.1 1.5.21
1.0.0 1.5.10
1.0.0-rc02 1.5.10
1.0.0-rc01 1.5.10

我这边使用的是1.3.1,对应kotlin版本是1.7.10,工程中build.gradle

android {
    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = "1.3.1"
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }
}

主工程中build.gradle:

plugins {
    id 'com.android.application' version '7.3.1' apply false
    id 'com.android.library' version '7.3.1' apply false
    id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}
4.预览compose函数与启动
4.1 预览compose函数

引入了ui-tooling-preview库组后,我们可以使用@Preview注解可组合函数,并实现预览组件

compose--初入compose、资源获取、标准控件与布局
4.2 启动

启动到模拟器的效果:

compose--初入compose、资源获取、标准控件与布局

三、资源获取

xml中,我们常常会使用资源id获取到资源文件,比如:color、drawable、string等,在compose中,通过以下函数获取,这些函数都位于androidx.compose.ui.res包下:

compose--初入compose、资源获取、标准控件与布局

当然我们并不需要使用里面全部的类,掌握下面列出的即可:

资源获取方式 描述
stringResource 获取对应id的string资源,并支持传入多个参数,来实现字符串格式化
colorResource 获取对应id的color资源
painterResource 获取对应id的图片资源,可以是一个vector,也可以是drawable
dimensionResource 获取对应id的dimen资源,由于compose推荐使用md主题设置dimen,用的也不多

四、标准控件

compose本身内置了一些组件,官方说法所有组件都是可组合函数,这边仅仅是便于传统开发理解,分成控件和布局来介绍,这些内置可组合函数分散在各个不同的库组内,如:androidx.compose.foundationandroidx.compose.foundation.layoutandroidx.compose.material3
其中控件大多位于md包下,他们都具有MD风格,也是官方推荐使用的组件:

compose--初入compose、资源获取、标准控件与布局
1.Text

Text用于呈现一段文字,是使用最多的组件,官方也详细的介绍了该组件:https://developer.android.google.cn/jetpack/compose/text

1.1 基本使用

所有compose函数都要由@Composable注解,并且每个可组合函数都是可以重用的组件:

@Composable
@Preview
fun MyText() {
    Text(text = "hello world!")
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
1.2 使用资源获取文本

通过stringResource(id)获取String,可以达到同样的效果

@Composable
@Preview
fun MyText() {
    Text(text = stringResource(id = R.string.hello))
}
1.3 AnnotatedString

传统UI的TextView,可以通过Span来改变文本的内嵌样式,比如个别字颜色设置、设置背景颜色等效果compose中可以使用AnnotatedString来达到这种效果,通过buildAnnotatedString()构建一个AnnotatedStringAnnotatedString可以包含多个 SpanStyle(点击跳转API)ParagraphStyle(点击跳转API)

  • SpanStyle:设置文本的内嵌样式
  • ParagraphStyle:设置文本的行高,对齐方式,文字方向和文字缩进样式

例子:

@Composable
@Preview
fun MyText() {
    Text(
        text = buildAnnotatedString {
            withStyle(
                style = ParagraphStyle(
                    lineHeight = 30.sp,//行高
                    textAlign = TextAlign.Left,//左对齐
                    textIndent = TextIndent(firstLine = 10.sp)//缩进
                )
            ) {
                withStyle(
                    style = SpanStyle(
                        fontSize = 20.sp,
                        color = Color.Red,//设置颜色为红色
                        fontWeight = FontWeight.Medium//加粗
                    )
                ) {
                    append("hi\n")
                }
            }

            withStyle(
                style = ParagraphStyle(
                    lineHeight = 60.sp,
                )
            ) {
                withStyle(
                    style = SpanStyle(
                        color = Color.Red,
                        shadow = Shadow(//设置阴影
                            color = Color.Blue,//阴影颜色
                            blurRadius = 3f,//虚化
                            offset = Offset(5f, 20f)//x,y轴的偏移
                        )
                    )
                ) {
                    append("你好\n")
                }
            }
        }
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
1.4 其他参数

其他参数可以通过源码查看:

@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//更丰富的字体样式,包含上面大多数设置,以及SpanStyle和ParagraphStyle
) {
...
}

其中Modifier后续会详细介绍,举例使用里面的几个参数设置,如使用TextStyle去除首行的顶部行间距:

<string name="hello">hello!\nworld</string>
@Composable
@Preview
fun MyText() {
    Text(
        text = stringResource(id = R.string.hello),
        fontWeight = FontWeight.Medium,
        overflow = TextOverflow.Clip,
        //将当前的style和另一个合并,以另一个设置的属性为优先
        style = LocalTextStyle.current.merge(
            TextStyle(
                lineHeight = 2.5.em,
                platformStyle = PlatformTextStyle(
                    includeFontPadding = false//配合trim
                ),
                lineHeightStyle = LineHeightStyle(
                    alignment = LineHeightStyle.Alignment.Center,
                    // trim生效需要includeFontPadding = false
                    // trim是指将行间距尽可能的去除
                    // FirstLineTop:将第一行顶部的行间距去除
                    trim = LineHeightStyle.Trim.FirstLineTop
                )
            )
        )
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
2.Image

Image用于展现图片

2.1 基本使用

必传入参为图片资源对象painter和内容描述contentDescriptioncontentDescription主要是为了残疾人使用的,国外对于残疾人使用也非常的重视,此外使用python自动化测试也可以通过contentDescription找到该组件:

@Composable
@Preview
fun MyImage() {
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_foreground),//指定图片资源
        contentDescription = "my image" //描述,残疾人以及自动化测试使用
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
2.2 其他参数

相较于TextImage的参数少很多:

@Composable
fun Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,//修饰符
    alignment: Alignment = Alignment.Center,//图片对齐方式
    contentScale: ContentScale = ContentScale.Fit,//图片的拉伸方式
    alpha: Float = DefaultAlpha,//图片透明度
    colorFilter: ColorFilter? = null//通过ColorFilter对颜色矩阵进行变换
) {
    
}

参数还是比较简单的,ContentScale的几种方式可以通过官网认识:ContentScale介绍(点击跳转),其中ColorFilter和传统UI自定义控件时,使用的高级渲染效果相同,ColorFilter分别拥有三个伴生方法,对应不同的渲染方式:

  • tint(color: Color, blendMode: BlendMode = BlendMode.SrcIn):高级渲染,参考Xfermod(点击跳转)
  • colorMatrix(colorMatrix: ColorMatrix):颜色矩阵变换,参考ColorMatrixColorFilter(点击跳转)
  • lighting(multiply: Color, add: Color):颜色向量增加,参考LightingColorFilter(点击跳转)

使用tint例子,使用SrcIn模式合成一个红色:

@Composable
@Preview
fun MyImage() {
    Image(
        painter = painterResource(id = R.drawable.ic_launcher_foreground),
        contentDescription = "my image",
        colorFilter = ColorFilter.tint(
            color = Color.Red,
            blendMode = BlendMode.SrcIn
        )
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局

使用colorMatrix例子,颜色增强:

@Composable
@Preview
fun MyImage() {
    Row {
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "my image1",
            colorFilter = ColorFilter.colorMatrix(
                ColorMatrix().apply {
                    setToScale(1.2f, 1.2f, 1.2f, 1f)//颜色增强
                }
            )
        )

        Spacer(modifier = Modifier.width(10.dp))

        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "my image2",
        )
    }
}

预览效果,左边为颜色增强后:

compose--初入compose、资源获取、标准控件与布局

使用lighting例子,添加红色向量:

@Composable
@Preview
fun MyImage() {
    Row {
        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "my image1",
            // 红色向量添加255,红色加绿色 = 黄色
            colorFilter = ColorFilter.lighting(
                Color(red = 0xff, green = 0xff, blue = 0xff),
                Color(red = 0xff, green = 0, blue = 0)
            )
        )

        Spacer(modifier = Modifier.width(10.dp))

        Image(
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = "my image2",
        )
    }
}

预览效果,左边为添加红色向量后:

compose--初入compose、资源获取、标准控件与布局
2.3 Icon

同样用于显示图标,Icon功能比Image少,只支持tint,并且该tint为一个Color对象,不支持模式,只支持染色:

@Composable
@Preview
fun MyImage() {
    Icon(
        painter = painterResource(id = R.drawable.ic_launcher_foreground),
        contentDescription = "icon",
        tint = Color.Blue // 将图标染成蓝色
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
3.TextField

TextField就是输入框,并且需要用到state,关于state后续会详细介绍

3.1 基本使用

TextField必须传入的两个参数,一个是value,一个是onValueChange ,结合之前的重组概念来理解,每次重组都会重新调用可组合函数,所以输入框内容value必须是一个全局对象,在compose中,可以使用remember函数来使得一个变量成为全局变量,从而不受重组时代码调用导致重新初始化操作的影响
此外,只有state的改变才能通知compose进行重组,所以value又必须是一个state对象,并在onValueChange中对state进行改变,才能够进行组件的刷新

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyTextField() {
    var text by remember { mutableStateOf("") }// 定义state对象:text ,并设为全局
    TextField(
        value = text,//text 与TextField进行绑定
        onValueChange = { text = it },//当输入框值发生变换时,改变text值,从而引起状态的刷新,进而重组
        label = { Text("hint") }//提示
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
3.2 TextFieldValue

value的参数类型除了支持String外,还支持TextFieldValueTextFieldValue具有更好的自定义性,如使用AnnotatedString使文本具有样式、TextRange指定光标位置:

@Immutable
class TextFieldValue constructor(
    val annotatedString: AnnotatedString,//带样式的字符串
    selection: TextRange = TextRange.Zero,//
    composition: TextRange? = null
) {
...
}

例子:

@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
@Preview
@Composable
fun TextFieldValuePreview(
) {
    val textFieldValueState = remember {
        mutableStateOf(
            TextFieldValue(
                annotatedString = buildAnnotatedString {
                    append("hi")

                    withStyle(
                        style = SpanStyle(
                            color = Color.Red,
                            //设置阴影
                            shadow = Shadow(
                                color = Color.Blue,//阴影颜色
                                blurRadius = 3f,//虚化
                            )
                        )
                    ) {
                        append("你好\n")
                    }
                },
                selection = TextRange(2)// 光标默认显示在第二个字符位置
            )
        )
    }

    val showKeyboard = remember { mutableStateOf(true) }
    val focusRequester = remember { FocusRequester() }
    val keyboard = LocalSoftwareKeyboardController.current

    // 显示键盘
    LaunchedEffect(focusRequester) {
        if (showKeyboard.value) {
            focusRequester.requestFocus()
            delay(100)
            keyboard?.show()
        }
    }

    TextField(
        modifier = Modifier.focusRequester(focusRequester),
        value = textFieldValueState.value,
        onValueChange = {
        }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
3.3 其他参数
@ExperimentalMaterial3Api
@Composable
fun TextField(
    value: String,
    onValueChange: (String) -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,// 是否可用
    readOnly: Boolean = false,// 是否只读
    textStyle: TextStyle = LocalTextStyle.current,// 和Text一样支持的TextStyle
    label: @Composable (() -> Unit)? = null,//提示,有内容时自动缩小并上移
    placeholder: @Composable (() -> Unit)? = null,//提示,有内容时自动消失
    leadingIcon: @Composable (() -> Unit)? = null,//文本前的图标
    trailingIcon: @Composable (() -> Unit)? = null,//文本尾的图标
    supportingText: @Composable (() -> Unit)? = null,//文本下方的文本
    isError: Boolean = false,//是否错误,错误会将label、下划线、下方文本、文本尾的图标的图标染红
    visualTransformation: VisualTransformation = VisualTransformation.None,//输入内容的视觉类型,如密码显示*
    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,//键盘类型和imeAction
    keyboardActions: KeyboardActions = KeyboardActions.Default,//imeAction触发时的回调
    singleLine: Boolean = false,//是否单行
    maxLines: Int = Int.MAX_VALUE,//最大行数
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },//传入状态,从而监听用户触摸操作,如点击、拖拽
    shape: Shape = TextFieldDefaults.filledShape,//设置背景形状
    colors: TextFieldColors = TextFieldDefaults.textFieldColors()// 颜色集,通过设置相应的颜色,可以改变如错误发生时的颜色
) {
...
}

例子:

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyTextField() {
    var text by remember { mutableStateOf("") }
    TextField(
        value = text,
        onValueChange = { text = it },
        placeholder = { Text("haha") },
        leadingIcon = {//设置文本前图片
            Icon(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "leadingIcon"
            )
        },
        trailingIcon = {//设置文本后图片
            Icon(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "leadingIcon"
            )
        },
        supportingText = {//设置文本下的文本
            Text("supportingText")
        },
        isError = true,// 设置发生错误
        visualTransformation = PasswordVisualTransformation(),//视觉为密码
        shape = RoundedCornerShape(10.dp),//背景为圆角
        colors = TextFieldDefaults.textFieldColors(//错误时,下划线显示黄色
            errorIndicatorColor = Color.Yellow
        )
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
3.4 OutlinedTextField

OutlinedTextField是含有一个边框的输入框,其他用法和TextField相同

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyTextField() {
    var text by remember { mutableStateOf("") }

    OutlinedTextField(
        modifier = Modifier.padding(start = 10.dp, top = 10.dp),
        value = text,
        onValueChange = { text = it },
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number,
            imeAction = ImeAction.Search
        ),
        keyboardActions = KeyboardActions { 
            
        }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
4. Button

Button需要传入一个点击事件onClicklambda表达式,和一个content内容组件的lambda表达式,border边框支持Shader(点击跳转详情),其他参数说明如下:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Button(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,// 是否可用
    shape: Shape = ButtonDefaults.shape,// 背景形状
    colors: ButtonColors = ButtonDefaults.buttonColors(),//颜色集,背景、内容的可用和非可用颜色
    elevation: ButtonElevation? = ButtonDefaults.buttonElevation(),//阴影,默认、按下、不可用等状态下的阴影
    border: BorderStroke? = null,//边框,支持Shader
    contentPadding: PaddingValues = ButtonDefaults.ContentPadding,// 内容组件的padding
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },//触摸事件的状态改变
    content: @Composable RowScope.() -> Unit//按钮的内容组件
) {

}
4.1 基本使用

Buttoncontent是一个RowScope的作用域,也就是以行来摆放组件

例子:

@Preview
@Composable
fun MyButton() {
    Button(
        onClick = { /*TODO*/ },
        colors = ButtonDefaults.buttonColors(
            containerColor = Color.Cyan,
            contentColor = Color.Red
        ),
        elevation = ButtonDefaults.buttonElevation(defaultElevation = 3.dp),
        border = BorderStroke(
            1.dp,
            Brush.linearGradient(
                0f to Color.Transparent,
                1f to Color.DarkGray
            )
        ),
        contentPadding = PaddingValues(
            start = 10.dp,
            top = 5.dp
        ),
        content = {
            Text("点我")
            Text("点我")
        }
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
4.2 IconButton

IconButtoncontent需要传入一个Icon组件,其他用法和Button相同:

@Composable
fun MyIconButton() {
    IconButton(
        onClick = { /*TODO*/ },
        colors = IconButtonDefaults.iconButtonColors(contentColor = Color.Green),
        content = {
            Icon(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "icon"
            )
        }
    )
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
4.3 IconToggleButton

IconToggleButton具有选中和未选中状态,checked入参需要配合state对象使用,onCheckedChange用于选中状态切换的处理,其他用法和Button相同:

@Preview
@Composable
fun MyIconToggleButton() {
    var checked by remember { mutableStateOf(false) }

    IconToggleButton(
        checked = checked,
        onCheckedChange = {
            checked = it
        },
        modifier = Modifier
            .width(100.dp)
            .height(100.dp),
        colors = IconButtonDefaults.iconToggleButtonColors(
            contentColor = Color.Green,//选中为绿色
            checkedContentColor = Color.Red//非选中为红色
        ),
        content = {
            Icon(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "icon"
            )
        }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.4 Switch

Switch为开关样式的IconToggleButton组件,thumbContent参数支持指定开关按钮的Icon,其他用法与IconToggleButton相同:

@Preview
@Composable
fun MySwitch() {
    var checked by remember { mutableStateOf(false) }

    Switch(
        checked = checked,
        onCheckedChange = { checked = it },
        thumbContent = {
            Icon(
                painter = painterResource(id = R.drawable.ic_launcher_foreground),
                contentDescription = "icon"
            )
        }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.5 RadioButton

RadioButton为单选框

@Preview
@Composable
fun MyRadioButton() {
    var selected by remember { mutableStateOf(false) }

    RadioButton(
        selected = selected,
        onClick = { selected = !selected }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.6 Checkbox

Checkbox为复选框

@Preview
@Composable
fun MyCheckbox() {
    var selected by remember { mutableStateOf(false) }

    Checkbox(
        checked = selected,
        onCheckedChange = { selected = it }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.7 ExtendedFloatingActionButton

ExtendedFloatingActionButton为悬浮按钮,控制expanded参数可以展开和缩小,此外还支持shape设置背景形状、elevation设置阴影:

@Composable
fun ExtendedFloatingActionButton(
    text: @Composable () -> Unit,// 文字
    icon: @Composable () -> Unit,// 图标
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    expanded: Boolean = true,// 是否展开
    shape: Shape = FloatingActionButtonDefaults.extendedFabShape,//背景形状
    containerColor: Color = FloatingActionButtonDefaults.containerColor,//容器颜色
    contentColor: Color = contentColorFor(containerColor),//内容组件颜色
    elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),//阴影
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
...
}

例子:

@Preview
@Composable
fun MyExtendedFloatingActionButton() {
    var expanded by remember { mutableStateOf(false) }
    
    ExtendedFloatingActionButton(
        text = { Text(text = "点我") },
        icon = { /*TODO*/ },
        onClick = { expanded = !expanded },
        expanded = expanded,
        shape = RoundedCornerShape(30.dp)
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
5.Spacer

Spacer表示间距,用来代表一片隔离区域,隔离组件与组件

@Preview
@Composable
fun MySpacer() {
    Row {
        Text("hi")
        Spacer(modifier = Modifier.width(20.dp))
        Text("hi")
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
6.Divider

Divider可以用来表示一条分割线,默认是一条横向的,所以通过Modifier来改变

@Preview
@Composable
fun MyDivider() {
    Row() {
        Text(
            "hi",
            modifier = Modifier.weight(1f)
        )
        Divider(
            color = Color.Blue,
            modifier = Modifier
                .fillMaxHeight()//充满整个组件
                .width(1.dp)//宽度为1dp
        )
        Text(
            "hi",
            modifier = Modifier.weight(1f)
        )
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
6.1 IntrinsicSize

从上面的预览效果可以知道,将Divider设置为最大高度后,MyDivider组件充满了整个屏幕,如果想到达到Divider的高度不计入MyDivider的高度,并随着MyDivider的高度进行填充,就需要用到IntrinsicSizeIntrinsicSize表示允许父组件优先查询下子组件的高度,所以设置给父组件,这边给Row设置Modifier

@Preview
@Composable
fun MyDivider2() {
    Row(modifier = Modifier.height(IntrinsicSize.Min)) {//高度设置为IntrinsicSize
        Text(
            "hi",
            modifier = Modifier.weight(1f)
        )
        Divider(
            color = Color.Red,
            modifier = Modifier
                .fillMaxHeight()//充满整个组件
                .width(1.dp)//宽度为1dp
        )
        Text(
            "hi",
            modifier = Modifier.weight(1f)
        )
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局

五、标准布局

compose中的布局也不多,最基础的为ColumnRowBox,官方给出的定义如下图:

compose--初入compose、资源获取、标准控件与布局
1.Row

上面我们使用过一个Row,它的作用域是RowScope,同横向LinearLayout

@Composable
inline fun Row(
    modifier: Modifier = Modifier,
    horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,// 内容组件水平排列方式
    verticalAlignment: Alignment.Vertical = Alignment.Top,//内容组件垂直对齐方式
    content: @Composable RowScope.() -> Unit// 内容组件
) {
    
}
1.1 Arrangement

关于Arrangement的几种方式,官方给出的图示:

compose--初入compose、资源获取、标准控件与布局
1.2 基本使用
@Preview
@Composable
fun MyRow() {
    Row(
        modifier = Modifier.width(100.dp),
        horizontalArrangement = Arrangement.End,//内容组件往右对齐
        verticalAlignment = Alignment.CenterVertically//内容组件垂直居中
    ) {
        Text("hi")
        Text("你好\n 张三")
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
2.Column

Column就是竖直方向摆放组件的布局,用法上和Row相同,同竖向LinearLayout

@Composable
inline fun Column(
    modifier: Modifier = Modifier,
    verticalArrangement: Arrangement.Vertical = Arrangement.Top,//内容组件垂直对齐方式
    horizontalAlignment: Alignment.Horizontal = Alignment.Start,//内容组件水平对齐方式
    content: @Composable ColumnScope.() -> Unit//内容组件
) {
    
}
2.1 基本使用
@Preview
@Composable
fun MyColumn() {
    Column(
        modifier = Modifier.height(100.dp),
        horizontalAlignment = Alignment.CenterHorizontally,//内容组件水平居中
        verticalArrangement = Arrangement.SpaceBetween//内容组件垂直分布到两侧
    ) {
        Text("hi")
        Text("你好\n 张三")
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
3.Box

Box类似FrameLayout,可以堆叠摆放子组件

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,//内容组件的对齐方式
    propagateMinConstraints: Boolean = false,//是否指定内容组件使用该组件的最小约束(最小宽高)
    content: @Composable BoxScope.() -> Unit
) {

}
3.1 基本使用

下面两个Image的宽高设定为40dp,由于Box设置了最小约束为50dp和70dp,所以Image变大了:

@Preview
@Composable
fun MyBox() {
    Box(
        modifier = Modifier
            .sizeIn(50.dp, 70.dp),//设置内容组件的最小宽度和高度为50dp、70dp,配合propagateMinConstraint=true使用
        propagateMinConstraints = true,//使内容组件最小宽度和高度生效
        contentAlignment = Alignment.BottomEnd
    ) {
        // propagateMinConstraints,内部需要一个组件撑大整体的大小
        Box(Modifier.size(50.dp,150.dp).background(Color.Cyan))
        Image(
            modifier = Modifier.size(40.dp, 40.dp),
            painter = painterResource(id = R.drawable.ic_launcher_background),
            contentDescription = null,
            contentScale = ContentScale.FillHeight // 图片高度拉伸
        )
        Image(
            modifier = Modifier.size(40.dp, 40.dp),
            painter = painterResource(id = R.drawable.ic_launcher_foreground),
            contentDescription = null,
            contentScale = ContentScale.FillHeight // 图片高度拉伸
        )
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
4.Scaffold

Scaffold预设了很多槽位(存放子组件)和功能,Scaffold的学习可以通过官网:Scaffold官方示例(有些参数只有MD2版本才有)

4.1 topBar

槽位topBar就是给顶部子组件准备的,如:TopAppBar

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffold() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = {//标题
                    Text(
                        modifier = Modifier.padding(start = 10.dp),
                        text = "topBar"
                    )
                },
                navigationIcon = {//导航图标
                    Icon(Icons.Default.ArrowBack, contentDescription = null)
                },
                actions = {//右侧按钮
                    IconButton(onClick = { /*TODO*/ }) {
                        Icon(Icons.Filled.Favorite, contentDescription = null)
                    }
                    IconButton(onClick = { /*TODO*/ }) {
                        Icon(Icons.Filled.Search, contentDescription = null)
                    }
                },
                colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
            )
        }
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

预览效果:

compose--初入compose、资源获取、标准控件与布局
4.2 bottomBar

topBar对应,bottomBar是用来存放底部子组件的槽位,如:BottomAppBarBottomNavigation

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffold() {
    Scaffold(
        topBar = {
           ...
        },
        bottomBar = {
            BottomAppBar(
                containerColor = MaterialTheme.colorScheme.primaryContainer,
                tonalElevation = 2.dp,
            ) {
                IconButton(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Home, contentDescription = null)
                }
                Spacer(modifier = Modifier.weight(1f))
                IconButton(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.ShoppingCart, contentDescription = null)
                }
                IconButton(onClick = { /*TODO*/ }) {
                    Icon(Icons.Filled.Info, contentDescription = null)
                }
            }
        }
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.3 floatingActionButton

floatingActionButton是专门为FloatingActionButton准备的槽位,配合floatingActionButtonPosition可以改变槽位的位置,目前只支持底部居中和底部靠右:

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffold() {
    Scaffold(
        topBar = {
            ...
        },
        bottomBar = {
            ...
        },
        floatingActionButton = {
            FloatingActionButton(onClick = { /*TODO*/ }) {
                Text(text = "hi")
            }
        },
        floatingActionButtonPosition = FabPosition.Center
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.4 snackbarHost

snackbarHost槽位用于展示一个提示SnackbarHost,需要通过SnackbarHostState来控制该子组件的显示:

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffold2() {
    val scaffoldState by remember { mutableStateOf(SnackbarHostState()) }
    val scope = rememberCoroutineScope()

    Scaffold(
        topBar = {
            ...
        },
        bottomBar = {
            ...
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {
                scope.launch {
                    scaffoldState.showSnackbar("hi,this is snack bar")
                }
            }) {
                Text(text = "hi")
            }
        },
        snackbarHost = { SnackbarHost(hostState = scaffoldState) },
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局

SnackbarHostState还支持显示的时长,相应的点击动作,基于协程返回消失或点击动作的结果:

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffold2() {
    val scaffoldState by remember { mutableStateOf(SnackbarHostState()) }
    val scope = rememberCoroutineScope()

    Scaffold(
        topBar = {
            ...
        },
        bottomBar = {
            ...
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {
                scope.launch {
                    val result = scaffoldState.showSnackbar(
                        message = "hi,this is snack bar",
                        duration = SnackbarDuration.Short,
                        actionLabel = "click"
                    )

                    when (result) {
                        SnackbarResult.ActionPerformed -> {
                            /* Handle snackbar action performed */
                            scaffoldState.currentSnackbarData?.dismiss()
                        }
                        SnackbarResult.Dismissed -> {
                            /* Handle snackbar dismissed */
                        }
                    }
                }
            }) {
                Text(text = "hi")
            }
        },
        snackbarHost = { SnackbarHost(hostState = scaffoldState) },
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局
4.5 MD2-drawerContent

drawerContent是抽屉菜单的槽位,它是一个ColumnScope,注意目前MD3版本并不支持,如果要使用,需要MD2Scaffold

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyScaffoldDrawer() {
    val drawerState = rememberScaffoldState()
    val scope = rememberCoroutineScope()

    androidx.compose.material.Scaffold(
        topBar = {
            TopAppBar(
                title = {//标题
                    Text(
                        modifier = Modifier.padding(start = 10.dp),
                        text = "topBar"
                    )
                },
                navigationIcon = {//导航图标
                    Icon(
                        modifier = Modifier.clickable {
                            scope.launch {
                                drawerState.drawerState.apply {
                                    if (isClosed) open() else close()
                                }
                            }
                        },
                        imageVector = Icons.Default.List,
                        contentDescription = null
                    )
                },
                colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = MaterialTheme.colorScheme.primaryContainer)
            )
        },
        drawerContent = {
            Text("title")

            Row(modifier = Modifier.padding(start = 10.dp,top = 10.dp)) {
                Image(Icons.Default.Phone, contentDescription = null)
                Text(text = "my phone")
            }
            Row(modifier = Modifier.padding(start = 10.dp,top = 10.dp)) {
                Image(Icons.Default.Call, contentDescription = null)
                Text(text = "call")
            }
            Row(modifier = Modifier.padding(start = 10.dp,top = 10.dp)) {
                Image(Icons.Default.Delete, contentDescription = null)
                Text(text = "delete cache")
            }
        },
        scaffoldState = drawerState
    ) { paddings ->
        Box(modifier = Modifier.padding(paddings))
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局
5. MD2-ModalDrawer

ModalDrawer仅仅是抽屉栏,同样只在MD2中才有,需要DrawerState控制展开和收起:

@Preview
@Composable
fun MyModalDrawer() {
    val drawerState =
        androidx.compose.material.rememberDrawerState(androidx.compose.material.DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    ModalDrawer(
        drawerState = drawerState,
        drawerContent = {
            // Drawer content
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Text("hi")
            }
        }
    ) {
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            // Screen content
            Button(onClick = {
                scope.launch {
                    drawerState.apply {
                        if (isClosed) open() else close()
                    }
                }
            }) {
                Text("点我展开抽屉")
            }
        }
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局

此外BottomDrawer代表底部的抽屉栏,用法上和ModalDrawer差不多

6.MD2-ModalBottomSheetLayout

ModalBottomSheetLayout是底部菜单,需要使用ModalBottomSheetState控制显示和消失:

@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
fun MyModalBottomSheetLayout() {
    val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
    val scope = rememberCoroutineScope()

    ModalBottomSheetLayout(
        sheetState = sheetState,
        sheetContent = {
            // Sheet content
            Box(
                modifier = Modifier.height(400.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("Sheet")
            }
        }
    ) {
        // Screen content
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            Button(onClick = {
                scope.launch {
                    sheetState.apply {
                        if (isVisible) hide() else show()
                    }
                }
            }) {
                Text("点我展开")
            }
        }
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局

此外,BottomSheetScaffold代表带有底部sheetContent槽位的Scaffold,用法和Scaffold差不多

7.MD2-BackdropScaffold

BackdropScaffold官方的说法为背景幕,就是两个布局可以堆叠,并前面的布局可以下移隐藏,通过BackdropScaffoldState控制是否隐藏:

@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
fun MyBackdropScaffold() {
    val scaffoldState = rememberBackdropScaffoldState(
        BackdropValue.Concealed
    )
    val scope = rememberCoroutineScope()

    BackdropScaffold(
        scaffoldState = scaffoldState,
        appBar = {
            // Top app bar
            androidx.compose.material.TopAppBar(
                title = {//标题
                    Text(
                        modifier = Modifier.padding(start = 10.dp),
                        text = "topBar"
                    )
                },
                navigationIcon = {//导航图标
                    Icon(
                        modifier = Modifier.clickable {
                            scope.launch {
                                scaffoldState.apply {
                                    if (isConcealed) reveal() else conceal()
                                }
                            }
                        },
                        imageVector = Icons.Default.List,
                        contentDescription = null
                    )
                }
            )
        },
        backLayerContent = {
            // Back layer content
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.White)
            ) {

            }
        },
        frontLayerContent = {
            // Front layer content
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .background(Color.Magenta)
            ) {

            }
        }
    )
}

效果:

compose--初入compose、资源获取、标准控件与布局
8.MD3-ModalNavigationDrawer

ModalNavigationDrawer是MD3中的抽屉栏,配合ModalDrawerSheet组件,可以达到抽屉栏列表MD3风格的样式:

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
fun MyPermanentNavigationDrawer() {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
    val scope = rememberCoroutineScope()

    ModalNavigationDrawer(
        drawerState = drawerState,
        modifier = Modifier.fillMaxHeight(),
        drawerContent = {
            ModalDrawerSheet() {
                NavigationRailItem(
                    selected = true,
                    onClick = {},
                    icon = { Icon(Icons.Default.Home, contentDescription = null) },
                    label = { Text("home") }
                )
                NavigationRailItem(
                    selected = false,
                    onClick = {},
                    icon = { Icon(Icons.Default.Info, contentDescription = null) },
                    label = { Text("info") }
                )
                NavigationRailItem(
                    selected = false,
                    onClick = {},
                    icon = { Icon(Icons.Default.Call, contentDescription = null) },
                    label = { Text("call") }
                )
            }
        },
    ) {
        Scaffold(
            topBar = {
                CenterAlignedTopAppBar(
                    title = { Text("CenterAlignedTopAppBar") },
                    navigationIcon = {
                        Icon(
                            modifier = Modifier.clickable {
                                scope.launch {
                                    drawerState.apply {
                                        if (isClosed) open() else close()
                                    }
                                }
                            },
                            imageVector = Icons.Default.List,
                            contentDescription = null
                        )
                    },
                    actions = { /* App bar actions */ })
            },
        ) { paddings ->
            Box(modifier = Modifier.padding(paddings))
        }
    }
}

效果:

compose--初入compose、资源获取、标准控件与布局

六、总结

最后总结下这篇文章的各个组件的作用,当然了compose中还有其他的组件,以及后续会出更多的新组件,目前也介绍了大部分组件的使用:文章来源地址https://www.toymoban.com/news/detail-483963.html

组件 分类 备注
Text 文本
TextField 文本输入
OutlinedTextField 文本输入 带边框
Image 图片
Icon 图标 渲染方式比Image少
Button 按钮
IconButton 图标按钮
IconToggleButton 选中状态图标按钮 通过State切换是否选中
Switch 开关样式图标按钮 通过State切换是否选中
RadioButton 单选按钮 通过State切换是否选中
Checkbox 复选按钮 通过State切换是否选中
FloatingActionButton 悬浮按钮
ExtendedFloatingActionButton 可展开悬浮按钮 通过State切换是否展开
SnackbarHost 提示消息 通过SnackbarHostState是否显示
Spacer 间距
Divider 分割线
Row 横向布局
Column 纵向布局
Box 堆叠布局
Scaffold 槽位布局 通过ScaffoldState切换是否展开抽屉栏
TopAppBar 标题导航栏
CenterAlignedTopAppBar 标题居中导航栏
BottomAppBar 底部导航栏
BottomNavigation 底部导航栏
ModalDrawer 抽屉栏 通过DrawerState切换是否展开抽屉栏
ModalBottomSheetLayout 底部抽屉菜单栏 通过ModalBottomSheetState切换是否显示菜单栏
BackdropScaffold 背景幕 通过BackdropScaffoldState切换是否前幕布下移
ModalNavigationDrawer 抽屉栏 通过DrawerState切换是否展开抽屉栏
ModalDrawerSheet 抽屉栏菜单布局

到了这里,关于compose--初入compose、资源获取、标准控件与布局的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android布局和控件:创建用户界面的XML布局文件和常用UI控件详解

    在Android应用开发中,创建用户界面是一个重要的任务。通过使用XML布局文件和常用的UI控件,开发人员可以设计和构建出吸引人且功能丰富的应用界面。本文将详细介绍如何使用XML布局文件来创建Android应用的用户界面,并深入探讨一些常用UI控件的属性和用法。 XML布局文件是

    2024年02月17日
    浏览(43)
  • Qt - 控件和布局

    https://www.bilibili.com/video/BV1g4411H78N?p=6 添加控件到窗口,需要添加内容到 mywidget.cpp 按钮控件常用API 创建 QPushButton * btn = new QPushButton 设置父亲 setParent(this) 设置文本 setText(\\\"文字\\\") 设置位置 move(宽,高) 重新指定窗口大小 resize 设置窗口标题 setWindowTitle 设置窗口固定大小 setFixed

    2024年02月16日
    浏览(46)
  • Qt使用代码放置控件并布局

            上一章中我们从头一步一步地新建项目并运行了窗口,本章就正式踏入Qt界面编程之路。在本章节,我们先用代码编写界面,进行简单的控件放置,然后用代码进行布局;之后使用Qt Designer进行控件放置和界面布局,这使得复杂界面的设计变得简单起来。      

    2024年02月07日
    浏览(45)
  • Qt鼠标拖动控件交换布局位置

    方法很简单,交换鼠标起点控件指针和鼠标落点控件指针的值,然后重新布局 Qt鼠标拖动控件交换布局位置 源文件 头文件 下面是只交换控件显示的内容: 添加鼠标贴图,跟随鼠标移动: 一种实现:重写void paintEvent(QPaintEvent *event);方法 另一种实现:

    2024年02月11日
    浏览(39)
  • 安卓实验1 界面设计:控件和布局

    实验题目:界面设计:控件和布局 实验目的 1了解Android编程原理 2掌握基本布局管理器、控件的应用 3掌握控件的事件处理编程 实验内容 编写一个小费计算器,界面如下。在Amount框中输入就餐费用,在15%标准小费率列下将按15%计算小费(Tip)的金额和应付总金额(Total),在

    2024年02月06日
    浏览(39)
  • WPF(一) WPF基本控件与布局

    ​ WPF(Windows Presentation Foundation)是微软推出的基于Windows的用户界面框架,中文译为“Windows呈现基础”,属于.NET Framework 3.0的一部分。WPF类似于WinForm技术框架,但是相比于WinForm,WPF对大部分基础功能进行了更加强大的拓展,并且引入了XAML标记语言,真正实现了开发人员和设

    2024年02月02日
    浏览(60)
  • Qt Designer 控件箱中的控件介绍及布局比列分配

    控件箱介绍                 Qt Designer的控件箱(Widget Box)包含了各种常用的控件,用户可以通过拖放的方式将这些控件添加到窗体设计器中,用于构建用户界面。以下是一些常见控件箱中的控件及其功能的讲解: 1. 基本控件 : 标签(Label) :用于显示文本或图像。

    2024年04月13日
    浏览(41)
  • Flutter 实现按位置大小比例布局的控件

    做视频监控项目时需要需要展示多分屏,比如2x2、3x3、414等等,如果每一种分屏都单独实现会很麻烦,而且不能支持用户定制。最好的方式还是实现一个通用的分屏容器,而且采样比例计算位置大小,可以适配任意尺寸。 最直观的实现方式是获取控件宽高然后按比例计算,但

    2024年02月13日
    浏览(42)
  • 【Android】控件与布局入门 - 简易计算器

    目录 1. 基础开发环境 2. 计算器的布局和相关按钮 3. 计算器的主要运算逻辑 4. APK 文件 5. 项目源码 JDK:JDK17 Android Studio:Android Studio Giraffe | 2022.3.1 Android SDK:Android API 34 Gradle: gradle-8.0-bin.zip 使用 LinearLayout 和 GridLayout 实现计算器的交互前端。 layout 文件如下 相关 values 如下:

    2024年02月14日
    浏览(51)
  • 三、深入浅出WPF之控件与布局

    图形化用户界面:Graphic User Interface ,它的便捷之处在于对数据的直观性表达,把抽象性的对象通过界面的形式展现出来。很多编程都要自己的GUI工具:像java的Swing、c++的QT 、C#的winform等等. 在日常工作中我们打交道最多的控件无外乎5类: (1)布局控件 特点:均派生于 Syste

    2024年02月15日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包