Compose基础控件

412 阅读6分钟

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
): Unit

用法

直接显示

@Composable
fun TextSample() {
    Text(text = "Hello World!")
}

从 res 中读取文字显示

@Composable
fun TextSample() {
    Text(text = stringResource(R.string.content))
}
<resources>
    <string name="content">你好,世界!</string>
</resources>

参数

color 设置字体颜色

@Composable
fun TextSample() {
    Text(text = "Hello World!", color = Color.Red)
}

text1.png

fontSize 设置文字大小

fontSize 默认是跟随父级文字大小。

接收的是一个 TextUnit,可以设置 SP(像素值) 和 EM(字体值) 单位的值

以下示例是直接使用 TextUnit 创建对象进行赋值,因为这个构造函数还是实验性的,随时都可能有改动或删除,因此需要在函数前增加@OptIn(ExperimentalUnitApi::class)注解

@OptIn(ExperimentalUnitApi::class)
@Composable
fun TextSample() {
    Text(text = "Hello World!", fontSize = TextUnit(16f, TextUnitType.Sp))
}

当然,系统对 IntDoubleFloat 三种类型进行了扩展,可以直接按照下面的方式进行使用

import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.em

@Composable
fun TextSample1() {
    Text(text = "Hello World!", fontSize = 16.0.sp))
}

@OptIn(ExperimentalUnitApi::class)
@Composable
fun TextSample2() {
    Text(text = "Hello World!", fontSize = 16.em))
}

使用 em 的效果

text2.png

fontStyle 设置文字样式

  • FontStyle.Italic 设置为斜体
  • FontStyle.Normal 设置为正常体(默认状态)

@Composable
fun TextSample() {
    Text(text = "Hello World!", fontStyle = FontStyle.Italic))
}

text3.png

fontWeight 设置文字比重

系统预设了很多比重值可以直接使用,例如 FontWeight.Bold ,也可以使用 FontWeight(100)

fontFamily 设置文字字体

同样系统也预设了几个字体供选择使用,例如 FontFamily.SansSerif。也可以加载 res 下的字体文件

text-font-folder.png

@Composable
fun TextSample() {

    val firaSansFamily = FontFamily(
        Font(R.font.firasans_light, FontWeight.Light),
        Font(R.font.firasans_regular, FontWeight.Normal),
        Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
        Font(R.font.firasans_medium, FontWeight.Medium),
        Font(R.font.firasans_bold, FontWeight.Bold)
    )

    Text(text = "Hello World!", fontFamily = firaSansFamily))
}

letterSpacing 设置字符间距

@Composable
fun TextSample() {
    Text(text = "Hello World!", letterSpacing = 15.sp))
}

textDecoration 设置文字装饰

  • TextDecoration.None 无装饰(默认)
  • TextDecoration.Underline 下划线
  • TextDecoration.LineThrough 删除线

还可以通过TextDecoration.combine()合并使用多种装饰

@Composable
fun TextSample() {
    Text(
        text = "Hello World!",
        textDecoration = TextDecoration.combine(
            listOf(
                TextDecoration.LineThrough,
                TextDecoration.Underline
            )
        )
    )
}

text4.png

textAlign 设置文本对齐方式

需要固定宽度,才有效果

  • TextAlign.Center
  • TextAlign.End
  • TextAlign.Justify

效果如图

text5.png

lineHeight 设置文本行高

@Composable
fun TextSample() {
    Text(text = "Hello World!", lineHeight = 15.sp))
}

overflow 设置文本超出时如何显示

  • TextOverflow.Ellipsis 以省略号显示
  • TextOverflow.Clip 裁剪
  • TextOverflow.Visible 尽可能显示

!!! Tip 目前省略号只能在末尾,无法自定义省略号位置

maxLines 文本显示行数

@Composable
fun TextSample() {
    Text(text = "Hello World!", maxLines = 1))
}

style 样式

上面讲到的大部分文字修饰,都可以直接通过 TextStyle 进行修饰,除此之外还多出几个样式

  • fontFeatureSettings字体的高级设置,类似 CSS 的font-feature-settings,可以参考www.w3.org/TR/css-font…
  • background 设置背景颜色
  • shadow 设置阴影
  • textIndent 设置首先缩进
@Composable
fun TextSample() {
    Text(
        text = "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦",
        modifier = Modifier.width(110.dp),
        style = TextStyle(
            background = Color.White,
            shadow = Shadow(
                color = Color.Red,
                offset = Offset(5f, 5f),
                blurRadius = 10f
            ),
            textIndent = TextIndent(20.sp)
        )
    )
}

text6.png

SelectionContainer 文字复制

默认情况下 Text 并不能进行复制等操作,我们需要设置 SelectionContainer 来包装 Text

@Composable
fun TextSample() {
    SelectionContainer(
        Text(
            text = "锄禾日当午,汗滴禾下土。谁知盘中餐,粒粒皆辛苦"
        )
    )
}

text8.gif

Text 语句中设置不同样式

如果想让一个 Text 语句中有不同的样式,需要使用到 AnnotaedString

AnnotaedString 是一个数据类,包含文本,以及多种样式

@Composable
fun TextSample() {
    Text(
        buildAnnotatedString {
            withStyle(style = SpanStyle(Color.Red)) {
                append("锄禾日当午,")
            }
            withStyle(style = SpanStyle(Color.Green)) {
                append("汗滴禾下土。")
            }
            withStyle(style = SpanStyle(Color.Blue)) {
                append("谁知盘中餐,")
            }
            withStyle(style = SpanStyle(Color.Yellow)) {
                append("粒粒皆辛苦")
            }
        }
    )
}

text7.png

ClickableText文本点击控件

想要让文本可以接收到点击事件,可以使用 ClickableText,控件带有一个 onClick 参数,参数回调中还可以知道当前点击字条的 offset 是多少

简单用法

@Composable
fun TextSample() {
    ClickableText(
        buildAnnotatedString {
            withStyle(style = SpanStyle(Color.Red)) {
                append("锄禾日当午,")
            }
            withStyle(style = SpanStyle(Color.Green)) {
                append("汗滴禾下土。")
            }
            withStyle(style = SpanStyle(Color.Blue)) {
                append("谁知盘中餐,")
            }
            withStyle(style = SpanStyle(Color.Yellow)) {
                append("粒粒皆辛苦")
            }
        }, onClick = { offset ->
            Log.d("TextSample", "offset:$offset")
        }
    )
}

高级用法

从上面 设置不同样式文本点击 我们知道了如何在 Text 语句内设置不同的样式,也知道了如何获得点击的文字,那我们是不是可以实现在文本内设置部分文字可以点击呢!

比如『点击登录代表您知悉和同意用户协议隐私政策

@Composable
fun TextSample() {
    val annotatedString = buildAnnotatedString {
        append("点击登录代表您知悉和同意")

        //往字符串中添加一个注解,直到遇到 pop() 。tag 为注解标识,annotation 为传递内容
        pushStringAnnotation("protocol", annotation = "https://docs.bughub.icu/compose")
        withStyle(style = SpanStyle(Color.Blue)) {
            append("用户协议")
        }
        pop()

        append("和")

        pushStringAnnotation("privacy", annotation = "https://randywei.gitee.com")
        withStyle(style = SpanStyle(Color.Blue)) {
            append("隐私政策")
        }
        pop()
    }

    ClickableText(
        annotatedString, onClick = { offset ->
            //从字符串中查找注解
            annotatedString.getStringAnnotations("protocol", start = offset, end = offset)
                .firstOrNull()?.let { annotation ->
                    Log.d("TextSample", "点击了用户协议:${annotation.item}")
                }

            annotatedString.getStringAnnotations("privacy", start = offset, end = offset)
                .firstOrNull()?.let { annotation ->
                    Log.d("TextSample", "点击了隐私政策:${annotation.item}")
                }
        }
    )
}

text9.gif

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
): Unit

基本用法

@Composable
fun ButtonSample() {
    Button(
        onClick = {
            Log.d("ButtonSample", "click the button")
        },
    ) {
        Text(text = "这里有一个按钮")
    }
}

button1.png

参数

  • enabled 是否启用或禁用

button2.png

  • elevation 投影
  • border 边框线
@Composable
fun ButtonSample() {
    Button(
        onClick = {
            Log.d("ButtonSample", "click the button")
        },
        border = BorderStroke(1.dp,Color.Red)
    ) {
        Text(text = "这里有一个按钮")
    }
}

button3.png

  • colors设置颜色,可以设置背景颜色、前景颜色、禁用状态和启动状态下的颜色
@Composable
fun ButtonSample() {
    Button(
        onClick = {
            Log.d("ButtonSample", "click the button")
        },
        colors = ButtonDefaults.buttonColors(
            backgroundColor = Color.Yellow,
            contentColor = Color.Green
        )
    ) {
        Text(text = "这里有一个按钮")
    }
}

button4.png

  • contentPadding 内容内间距

TextButton

TextButton一般是用来显示文字按钮的

@Composable
fun ButtonSample() {
    TextButton(
        onClick = {
            Log.d("ButtonSample", "click the button")
        },
    ) {
        Text(text = "TextButton")
    }
}

button5.png

OutlinedButton

用于实现带有边框的按钮

@Composable
fun OutlinedButtonDemo(){
    OutlinedButton(onClick = {
        println("点击1")
        Log.d("OutlinedButtonDemo", "click the button")
    },
    border = BorderStroke(2.0.dp, Color.Green),
    colors = ButtonDefaults.buttonColors(Color.Red)
    ) {
        Text("Outlined Button")
    }
}

IconButton

用来显示图标按钮

@Composable
fun ButtonSample() {
    IconButton(
        onClick = {
            Log.d("ButtonSample", "click the button")
        },
    ) {
        Icon(imageVector = Icons.Default.Stairs, contentDescription = null)
    }
}

button6.png

Icon

@Composable
fun Icon(
    imageVector: ImageVector,//bitmap: ImageBitmap,painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current)
)

用法一

可以直接引用官方的图标库 ,例如:Icons.Default.AccountBox

@Composable
fun IconSample() {
    Icon(imageVector = Icons.Default.AccountBox, contentDescription = null)
}

icon1.png

在官方网站上,我们看到提供的图标库中,有些可能无法正常显示。是因为默认 SDK 中只是包含部分图标,如果需要使用更多图标需要引入扩展库

implementation "androidx.compose.material:material-icons-extended:$compose_version"	

用法二

可以使用 drawble 里面的图片

@Composable
fun IconSample() {
  Icon(
    painter = painterResource(id = R.drawable.ic_android_black_24dp),
    contentDescription = null,
    tint = Color.Blue
  )
}

用法三

可以引用 ImageBitmap

@Composable
fun IconSample() {
    var bitmap:ImageBitmap ? = null
    with(LocalContext.current){
         bitmap = ImageBitmap.imageResource(resources,R.drawable.newbanner4)
    }
    bitmap?.let { Icon(bitmap = it, contentDescription = null) }
}

参数

  • tint 设置图标颜色
@Composable
fun IconSample() {
    Icon(imageVector = Icons.Default.Deck, contentDescription = null, tint = Color.Red)
}

icon2.png

Image

@Composable
fun Image(
    painter: Painter,
    contentDescription: String?,
    modifier: Modifier = Modifier,
    alignment: Alignment = Alignment.Center,
    contentScale: ContentScale = ContentScale.Fit,
    alpha: Float = DefaultAlpha,
    colorFilter: ColorFilter? = null
): Unit

图片跟 Icon 差不多也可以通过三种方式引入图片,本页只展示一种方式

参数

  • contentScale 设置图片的伸展方式:ContentScale.Inside、ContentScale.Crop 等
  • colorFilter 设置颜色滤镜
@Composable
fun ImageSample() {
    Image(
        painter = painterResource(id = R.drawable.newbanner4),
        contentDescription = null,
        contentScale = ContentScale.Inside,
        colorFilter = ColorFilter.tint(Color.Red, blendMode = BlendMode.Color)
    )
}

image.png

TextField

属性

@Composable
fun TextField(
    value: String?,
    onValueChange: ((String) -> Unit)?,
    modifier: Modifier? = Modifier,
    enabled: Boolean? = true,
    readOnly: Boolean? = false,
    textStyle: TextStyle? = LocalTextStyle.current,
    label: (@Composable () -> Unit)? = null,
    placeholder: (@Composable () -> Unit)? = null,
    leadingIcon: (@Composable () -> Unit)? = null,
    trailingIcon: (@Composable () -> Unit)? = null,
    isError: Boolean? = false,
    visualTransformation: VisualTransformation? = VisualTransformation.None,
    keyboardOptions: KeyboardOptions? = KeyboardOptions.Default,
    keyboardActions: KeyboardActions? = KeyboardActions(),
    singleLine: Boolean? = false,
    maxLines: Int? = Int.MAX_VALUE,
    interactionSource: MutableInteractionSource? = remember { MutableInteractionSource() },
    shape: Shape? = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
    colors: TextFieldColors? = TextFieldDefaults.textFieldColors()
): Unit

参数

  • enabled 是否启用禁用,无法聚集,不可编辑,无法复制
  • readOnly 是否只读,不可编辑,可复制
  • textStyle 设置文本样式
  • lable 显示在输入框边框的文本(可选)
  • placeHolder 未输入状态下提示文字
  • leadingIcon 显示在输入框左侧的图标
  • trailingIcon 显示在输入框右侧的图标
  • keyboradOptions 键盘类型,如数字键盘
  • keyboradActions 功能键事件监听
  • singleLine 是否单行显示,如果设置为 true,则忽略maxLines
  • maxLines 大于等于1
@Composable
fun TextFieldSample() {
    //接收用户输入的值
    var name by remember { mutableStateOf("") }

    TextField(
        value = name,
        onValueChange = {
            name = it
        },
        label = {
            Text("name")
        },
        placeholder = {
            Text(text = "pls input name")
        },
        leadingIcon = {
            Icon(imageVector = Icons.Default.AccountBox, contentDescription = null)
        },
        keyboardActions = KeyboardActions(onDone = {

        }),
        keyboardOptions = KeyboardOptions(
         	  imeAction = ImeAction.Done,
          	keyboardType = KeyboardType.Number),
        singleLine = true,
    )
}

textfield.gif

OutlinedTextField

与 TextField 只是样式不同

@Composable
fun TextFieldSample() {
    var name by remember { mutableStateOf("") }
    OutlinedTextField(value = name, onValueChange = {
        name = it
    },label = {Text("name")})
}

textfield1.gif

BasicTextField

最原始的文本输入控件,可以进行深度自定义

@Composable
fun TextFieldSample3() {
    var name by remember { mutableStateOf("") }
    BasicTextField(value = name, onValueChange = {
        name = it
    })
}

textfiled2.gif

Slider

属性

@Composable
fun Slider(
    value: Float,
    onValueChange: (Float) -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
    /*@IntRange(from = 0)*/
    steps: Int = 0,
    onValueChangeFinished: (() -> Unit)? = null,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: SliderColors = SliderDefaults.colors()
) :Unit

参数

  • value 当前值
  • valueRange 可选值的范围,默认是0f~1f
  • steps 步频
@Composable
fun SliderSample() {
    var value by remember {
        mutableStateOf(0f)
    }
    Slider(value = value, onValueChange = {
        value = it
    }, valueRange = 0f..100f, steps = 4)
}

未设置 Step slider1.gif

设置 Steps slider2.gif

RangeSlider

范围选择器

因为还是个实验性的 API,所以需要增加@OptIn(ExperimentalMaterialApi::class)

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun SliderSample2() {
  	//注意此时值是一个范围
    var values by remember {
        mutableStateOf(5f..10f)
    }
    RangeSlider(values = values, onValueChange = {
        values = it
    }, valueRange = 0f..30f,steps = 3)
}

slider3.png

Checkbox

@Composable
fun Checkbox(
    checked: Boolean,
    onCheckedChange: ((Boolean) -> Unit)?,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: CheckboxColors = CheckboxDefaults.colors()
)
  • checked 是否选中
  • onCheckedChange 选择回调
  • enabled 是否启用
@Composable
fun CheckBoxSample() {
    //接收选中状态
    var checkedList by remember {
        mutableStateOf(
            listOf(false, false)
        )
    }
    Column {
        checkedList.forEachIndexed { i, item ->
            Checkbox(checked = item, onCheckedChange = {
                checkedList = checkedList.mapIndexed { j, b ->
                    if (i == j) {
                        !b
                    } else {
                        b
                    }
                }
            })
        }
    }
}

CheckBox.gif

CircularProgressIndicator

属性

@Composable
fun CircularProgressIndicator(
    progress: Float?,
    modifier: Modifier? = Modifier,
    color: Color? = MaterialTheme.colors.primary,
    strokeWidth: Dp? = ProgressIndicatorDefaults.StrokeWidth
): Unit

参数

  • progress 当前进度0.0~1.0之间,不传递为滚动进度
  • color 进度条颜色
  • strokeWidth 进度条大小
@Composable
fun CircularProgressIndicatorSample() {
    CircularProgressIndicator(
        color = Color.Red, strokeWidth = 10.dp
    )
}

progress1.gif

不传 porgress参数情况

@Composable
fun CircularProgressIndicatorSample1() {
    CircularProgressIndicator(progress = 0.5f)
}

progress2.png

LinearProgressIndicator

@Composable
fun LinearProgressIndicatorSample() {
    LinearProgressIndicator(color = Color.Red, backgroundColor = Color.Green)
}

progress3.gif

@Composable
fun LinearProgressIndicatorSample1() {
    LinearProgressIndicator(progress = 0.3f, color = Color.Red, backgroundColor = Color.Green)
}

progress4.png

Switch

属性

@Composable
fun Switch(
    checked: Boolean?,
    onCheckedChange: ((Boolean) -> Unit)?,
    modifier: Modifier? = Modifier,
    enabled: Boolean? = true,
    interactionSource: MutableInteractionSource? = remember { MutableInteractionSource() },
    colors: SwitchColors? = SwitchDefaults.colors()
): Unit

用法

@Composable
fun SwitchSample() {
  	//声明一个变量来记住选中状态
    var switch by remember { mutableStateOf(false) }
    Switch(checked = switch, onCheckedChange = {
      	//当进行切换操作时,更改状态
        switch = it
    })
}

switch.gif

RadioButton

@Composable
fun RadioButton(
    selected: Boolean,
    onClick: (() -> Unit)?,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: RadioButtonColors = RadioButtonDefaults.colors()
):Unit
  • checked 是否选中
  • onCheckedChange 选择回调
  • enabled 是否启用
@Composable
fun RadioButtonSample1() {
    var checkedList by remember { mutableStateOf(listOf(false, false)) }
    LazyColumn() {
        items(checkedList.size) { i ->
            RadioButton(selected = checkedList[i], onClick = {
                checkedList = checkedList.mapIndexed { j, _ ->
                    i == j
                }
            })
        }
    }
}

RadioButton.gif

Divider

设置分割线

@Composable
fun ColumnSample() {
    Column(
        modifier = Modifier
            .requiredHeight(100.dp)
            .background(Color.White),
    ) {
        Text(
            text = "First ", modifier = Modifier
                .background(Color.Red)
                .weight(0.7f,fill=false)
        )
        
	     Divider()
				
        Text("Second", modifier = Modifier
            .background(Color.Green)
            .weight(0.3f))
    }
}

Spacer

空白区域,通过 modifier 设置空白区域的大小

@Composable
fun SpacerSample() {
    Column(modifier =  Modifier.background(Color.White)) {
        Row(modifier = Modifier.size(100.dp)) {
            Text("First", modifier = Modifier.background(Color.Red))

            Spacer(modifier = Modifier.weight(1f))

            Text("Second", modifier = Modifier.background(Color.Green))
        }

        Divider()

        Text("Column Item 0", modifier = Modifier.background(Color.Red))

        Spacer(modifier = Modifier.weight(1f))

        Text("Column Item 1", modifier = Modifier.background(Color.Green))
    }
}

Spacer.png