Jetpack Compose学习笔记

Compose项目依赖配置

compose_ui_version = '1.4.3'
id 'org.jetbrains.kotlin.android' version '1.8.10' apply false
//app build.gradle
composeOptions {
        kotlinCompilerExtensionVersion '1.4.3'
    }

implementation("io.coil-kt:coil-compose:2.4.0")
implementation "io.coil-kt:coil-svg:2.4.0" // Gradle
//landscapist可以缩放,coil是转换成位图显示无法无损缩放
implementation "com.github.skydoves:landscapist-coil:1.3.2"

1. Button

@Composable
fun ButtonStateDemo() {
    //记录按压状态
    val interactionState = remember {
        MutableInteractionSource()
    }
    val (text, textColor, buttonColor) = when {
        //判断是否是按压状态
        interactionState.collectIsPressedAsState().value -> {
            ButtonState("press", Color.White, Color.Blue)
        }
        else -> {
            ButtonState("unPress", Color.Black, Color.Green)
        }
    }
    Button(
        onClick = { /*TODO*/ },
        modifier = Modifier.background(buttonColor),
        interactionSource = interactionState
    ) {
        Icon(Icons.Filled.Favorite, null, modifier = Modifier.size(ButtonDefaults.IconSize))
        Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing))
        Text(text = text, color = textColor)
    }
}
data class ButtonState(var text: String, var textColor: Color, var buttonColor: Color)

2. Card

Card 是 Compose 中一个布局组件,我们用它可以来创造出一些类似于卡片界面

@Composable
fun CardDemo() {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(10.dp)//外边距
            .clickable {
                // 设置点击波纹效果,注意如果 CardDemo() 函数不在 MaterialTheme 下调用
                // 将无法显示波纹效果
            },
        // 设置阴影
        elevation = 10.dp
    ) {
        Column(
            modifier = Modifier
                .fillMaxWidth()
                //内边距
                .padding(10.dp)
        ) {
            Text(
                text = buildAnnotatedString {
                    append("欢迎来到")
                    withStyle(
                        style = SpanStyle(
                            fontWeight = FontWeight.W900,
                            color = Color.Magenta
                        )
                    ) {
                        append("Jetpack Compose")
                    }
                })
            Text(text = buildAnnotatedString {
                append("你现在学习的章节是")
                withStyle(style = SpanStyle(fontWeight = FontWeight.W900, color = Color.Green)) {
                    append("Card")
                }
            })
        }
    }

3.Icon

Icon 组件用于帮助开发者显示一系列的小图标。Icon 组件支持三种不同类型的图片设置:

Icon(imageVector = ImageVector.vectorResource(
    id = R.drawable.ic_svg, contentDescription = "矢量图资源")

Icon(bitmap = ImageBitmap.imageResource(
    id = R.drawable.ic_png), contentDescription = "图片资源")

Icon(painter = painterResource(
    id = R.drawable.ic_both), contentDescription = "任意类型资源")

Material 自带的包仅有一些常用的图标,如需使用其他所有的 Material 图标,可以添加依赖项,

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

Icon加载资源图片显示黑色没有加载出图片?别慌,因为默认的tint模式是AmbientContentColor.current,我们需要去掉它默认的着色模式,所以需要将tint的属性设置为Color.Unspecified

@Composable
fun IconDemo() {
    Icon(
        imageVector = ImageVector.vectorResource(id = R.drawable.visual_pwd),
        contentDescription = null,
        tint = Color.Unspecified
    )
}

4. IconButton

IconButton 是一个可点击的图标,用于表示动作(复制,粘贴,保存,等等)。IconButton 的整体最小触摸目标尺寸为 48 x 48dp,以满足无障碍准则。content 会在 IconButton 内居中。

content 通常应该是一个图标,使用 androidx.compose.material.icons.Icons 中的一个图标。如果使用自定义图标,请注意内部图标的典型尺寸是 24 x 24 dp

@Composable
fun IconButtonDemo() {
    Row {
        IconButton(onClick = { /*TODO*/ }) {
            Icon(Icons.Filled.Search, null)
        }
        IconButton(onClick = { /*TODO*/ }) {
            Icon(Icons.Filled.ArrowBack, null)
        }
        IconButton(onClick = { /*TODO*/ }) {
            Icon(Icons.Filled.Done, null)
        }
    }
}

有些时候我们想要取消按钮点击所产生的波纹要怎么办?

IconButton 的源码中其实将 Box 里的 modifier.clickable 参数 Indication 设置成波纹了,那我们只需要复制源码的代码添加到自己的项目中,并且将 indication 设置为 null 就好了

@Composable
fun MyIconButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    content: @Composable () -> Unit
) {
    // 这也是源码的一部分
    val IconButtonSizeModifier = Modifier.size(48.dp)
    Box(
        modifier = modifier
            .clickable(
                onClick = onClick,
                enabled = enabled,
                role = Role.Button,
                interactionSource = interactionSource,
                indication = null
            )
            .then(IconButtonSizeModifier),
        contentAlignment = Alignment.Center
    ) { content() }
}

5.!!! Image

Image 可以帮我们加载一张图片。我们可以使用 Surface 来帮助我们设置形状,或者在 Image 组件中使用 modifier.clip() 来裁剪形状。

@Composable
fun ImageDemo() {
    Surface(
        //修改形状
        shape = CircleShape,
        //利用 Surface 中的 border 参数来设置边框
        border = BorderStroke(2.dp,Color.Green)
    ) {
        Image(
            painter = painterResource(id = R.drawable.car2),
            contentDescription = null,
            modifier = Modifier.size(350.dp),//.clip(CircleShape)
            //默认fit模式缩放
            contentScale = ContentScale.Crop
        )
    }
}

使用 Coil 来动态加载图片,Compose 自带的 Image 只能加载资源管理器中的图片文件,如果想加载网络图片或者是其他本地路径下的文件,则需要考虑其他的库,比如 [Coil](Jetpack Compose - Coil

implementation("io.coil-kt:coil-compose:2.4.0")

Image(
        painter = rememberAsyncImagePainter(model = "https://kuriname.com/wp-content/uploads/2019/07/Doupo-Cangqiong-Season-3-Episode-3.jpg"),
        contentDescription = null,
  )

//直接使用Coil库控件
AsyncImage(model = "https://kuriname.com/wp-content/uploads/2019/07/Doupo-Cangqiong-Season-3-Episode-3.jpg", contentDescription = null)

//如果需要自定义尺寸
AsyncImage(
        model = ImageRequest.Builder(LocalContext.current)                            
        .data(imageUrl)
        .size(480, 320)
        .build(),
        contentDescription = null,
        contentScale = ContentScale.Crop
    )

加载 Svg 图像

添加依赖
implementation "io.coil-kt:coil-svg:2.4.0" // Gradle
implementation("io.coil-kt:coil-svg:2.4.0") // KTS
//landscapist可以缩放,coil是转换成位图显示无法无损缩放
implementation "com.github.skydoves:landscapist-coil:1.3.2"
@Composable
fun ImageSvgDemo() {
    val context = LocalContext.current
    val imageLoader = ImageLoader.Builder(context)
        .components {
            add(SvgDecoder.Factory())
        }
        .build()
    var flag by remember { mutableStateOf(false) }
    val size by animateDpAsState(targetValue = if(flag) 450.dp else 50.dp)
    CoilImage(
        imageModel = "https://www.svgrepo.com/show/71379/square-root-of-x-math-formula.svg",
        contentDescription = null,
        modifier = Modifier
            .size(size)
            .clickable(
                onClick = {
                    flag = !flag
                },
                indication = null,
                interactionSource = MutableInteractionSource()
            ),
        imageLoader = imageLoader
    )
}

6.TextField -> EditText

无边框的输入框TextField

@Composable
fun TextField() {
    val input = remember { mutableStateOf("") }
    var isHidden by remember { mutableStateOf(true) }
    //rememberSaveable与remember相似,但存储的值将使用保存的实例状态机制 
    // 在活动或进程重新创建后继续存在(例如,在 Android 应用程序中旋转屏幕时会发生这种情况)。
    var isError by rememberSaveable { mutableStateOf(false) }
    fun validate(text: String) {
        isError = text.count() < 8
    }
    TextField(
        modifier = Modifier
            .width(200.dp)
            .height(60.dp)
            .background(Color.White)
            .padding(all = 3.dp),
        value = input.value,
        onValueChange = {
            input.value = it
        },
        //单行显示,设置后maxLines失效
        singleLine = true,
        //是否显示密码形态
        visualTransformation = if (isHidden) PasswordVisualTransformation() else VisualTransformation.None,
        //输入框形状
        shape = MaterialTheme.shapes.small,
        colors = TextFieldDefaults.textFieldColors(
            textColor = Color.Black, //光标颜色
            cursorColor = Color.Blue
        ),
        //没有输入时显示hint
    // placeholder = {
    // Text(text = "请输入密钥", fontSize = 16.sp)
    // },
        // 是否可用,如果为false,将不可选中,不可输入,呈现出禁用状态
        enabled = true,
        //是否只读,如果是true,则不可编辑,但是可以选中,可以触发复制
        readOnly = false,
        //文本样式,和Text的TextStyle是一样的
        textStyle = TextStyle.Default,
        //显示在文本字段内的可选标签
        label = { Text(if (isError) "长度不能少于8位" else "请输入密码") },
        //输入内容是否错误,如果为true,则label,Icon等会相应的展示错误的显示状态
        isError = isError,
        //ImeAction
        keyboardActions = KeyboardActions { validate(input.value) },
        //软件键盘选项
        keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Password),
        //输入框头部显示icon
        //leadingIcon = { },
        //输入框末尾显示icon
        trailingIcon = {
            IconButton(onClick = {
                isHidden = !isHidden
            }) {
                Icon(
                    imageVector = ImageVector.vectorResource(id = if (isHidden) R.drawable.unvisual_pwd else R.drawable.visual_pwd),
                    contentDescription = if (isHidden) "不显示密钥" else "显示密钥",
                    modifier = Modifier
                        .width(20.dp)
                        .padding(horizontal = 3.dp)
                )
            }
        },
    )
}

有边框的输入框OutlinedTextField

@Composable
fun InputTextBorder() {
    Column(Modifier.statusBarsPadding()) {
        var text by remember { mutableStateOf("") }
        //带边框的输入框
        OutlinedTextField(
            value = text,
            onValueChange = { text = it },
            placeholder = { Text(text = "请输入搜索关键字", color = Color.LightGray, fontSize = 12.sp) },
            modifier = Modifier
                .padding(horizontal = 5.dp)
                .fillMaxWidth()
                .background(Color.White),
            singleLine = true,
            leadingIcon = {
                Image(
                    painter = painterResource(id = R.drawable.car2),
                    contentDescription = "图标",
                    modifier = Modifier.size(20.dp)
                )
            },
            shape = RoundedCornerShape(12.dp),
            colors = TextFieldDefaults.outlinedTextFieldColors(
                focusedBorderColor = Color.DarkGray,         //有焦点的边框颜色
                unfocusedBorderColor = Color.LightGray,      //无焦点的边框颜色
                cursorColor = Color.Blue
            ),
        )
    }
}

BasicTextField可以实现高度自定义编辑框

@Composable
@Preview
fun BasicTextFieldDemo(){
    var text by remember { mutableStateOf("") }
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color(0xFFD3D3D3)),
        contentAlignment = Alignment.Center
    ) {
        BasicTextField(
            value = text,
            onValueChange = {
                text = it
            },
            modifier = Modifier
                .background(Color.White, CircleShape)
                .height(35.dp)
                .fillMaxWidth(),
            decorationBox = { innerTextField ->
                Row(
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = Modifier.padding(horizontal = 10.dp)
                ) {
                    IconButton(
                        onClick = { }
                    ) {
                        Icon(painterResource(id = R.drawable.visual_pwd), null, tint = Color.Unspecified)
                    }
                    Box(
                        modifier = Modifier.weight(1f),
                        contentAlignment = Alignment.CenterStart
                    ) {
                        innerTextField()
                    }
                    IconButton(
                        onClick = { },
                    ) {
                        Icon(Icons.Filled.Send, null)
                    }
                }
            }
        )
    }
}

7.Slider -> ProgressBar

Slider 反映了一个沿条的数值范围,用户可以从中选择一个单一的数值。它们是调整音量、亮度或应用图像过滤器等设置的理想选择。

@Composable
fun Slider(){
    var progress by remember{ mutableStateOf(0f)}
    Slider(
        value = progress,
        colors = SliderDefaults.colors(
            thumbColor = Color.White, // 圆圈的颜色
            activeTrackColor = Color.Blue
        ),
        onValueChange = {
            progress = it
        },
    )
}

8. Text ->TextView

@Composable
fun Text() {
    Column(modifier = Modifier.fillMaxWidth().background(Color.White)) {
        Text(
            //从res加载文字,stringResource(id = R.string.content)
//      text = "这是一个标题,不是很长",
            //如果我们想让一个 Text 语句中使用不同的样式,比如粗体提醒,特殊颜色
            //则我们需要使用到 AnnotatedString
            text = buildAnnotatedString {
                append("你好:")
                withStyle(SpanStyle(Color.Green, fontWeight = FontWeight.W900)) {
                    append("\"特殊格式\"")
                }
            },
            style = MaterialTheme.typography.h6,//配置文本的行高,颜色,粗体等设置
            maxLines = 1,
            letterSpacing = 7.sp,//文字间距
            overflow = TextOverflow.Ellipsis, //文字溢出
            textAlign = TextAlign.Left,//设置文本对齐方式
            fontFamily = FontFamily.Serif,//可以自定义字体
            // 你也可以加载 res/font 下的字体
//        fontFamily = FontFamily(
//            Font(R.font.pingfang, FontWeight.W700)
//        )
            //有的时候也许您需要将文本当作按钮,那么只需要添加 Modifier.clickable 即可
            modifier = Modifier
                .clickable(
                    onClick = {},
                    //以下两条取消波纹效果
                    indication = null,
                    interactionSource = MutableInteractionSource()
                )
                //设置底色
                .background(Color.LightGray)
        )
        //可点击的text,文字超链接,按下标返回,打字效果还行
        ClickableText(text = buildAnnotatedString {
            withStyle(SpanStyle(Color.Blue, fontWeight = FontWeight.W900)) {
                append("用户协议")
            }
        }, onClick = { offset ->
            Log.d("lgf", "Hi,你按到了第 $offset 位的文字")
        })
        //默认情况下 Text 并不能进行复制等操作,我们需要设置 SelectionContainer 来包装 Text
        SelectionContainer(modifier = Modifier.fillMaxWidth()) {
            Text(
                text = "这段文字可以复制",
                modifier = Modifier
                    .width(200.dp)
                    .background(Color.Magenta),
                textAlign = TextAlign.Center
            )
        }
        //文字强调效果,Material Design 建议采用不同的不透明度来传达这些不同的重要程度,你可以通过 LocalContentAlpha 实现此功能
        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
            Text("这里是禁用后的效果")
        }
    }
}

9. Box ->FrameLayout

Box 是一个能够将里面的子项依次按照顺序堆叠的布局组件

fun BoxDemo(){
    Box(
        modifier = Modifier
            .size(300.dp)
            .background(Color(140, 67, 86)),
        //很有用,嵌套操作子控件位置
        contentAlignment = Alignment.Center
    ) {
        Text(text = "Hello World")
    }
}

10. Row

Row 组件能够将里面的子项按照从左到右的方向水平排列。和 Column 组件一起配合,我们可以构建出很丰富的界面。

@Composable
fun RowDemo() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        //horizontalArrangement 参数帮助我们合理地分配了按钮的水平位置
        horizontalArrangement = Arrangement.SpaceBetween
    ) {
        IconButton(
            onClick = { /*TODO*/ }
        ) {
            Icon(Icons.Filled.Favorite, null)
        }
        IconButton(
            onClick = { /*TODO*/ },
        ) {
            Icon(painterResource(id = R.drawable.visual_pwd), null)
        }
        IconButton(
            onClick = { /*TODO*/ },
        ) {
            Icon(Icons.Filled.Share, null)
        }
    }
}

11. Column

Column 是一个布局组件,它能够将里面的子项按照从上到下的顺序垂直排列,verticalArrangmenthorizontalAlignment 参数分别可以帮助我们安排子项的垂直/水平位置,在默认的情况下,子项会以垂直方向上靠上(Arrangment.Top),水平方向上靠左(Alignment.Start)来排布。

12. Flow Layout

Flow Layout 在 Compose 1.4 中“转正”。在此之前,你需要使用 Accompanist 的实现

@Composable
fun FlowLayoutDemo() {
    val filters = listOf(
        "Washer/Dryer", "Ramp access", "Garden", "Cats OK", "Dogs OK", "Smoke-free"
    )
    FlowRow(
        horizontalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        filters.forEach { title ->
            var selected by remember { mutableStateOf(false) }
            val leadingIcon: @Composable () -> Unit = { Icon(Icons.Default.Check, null) }
            FilterChip(
                selected,
                onClick = { selected = !selected },
                leadingIcon = if (selected) leadingIcon else null
            ){
                Text(text = title)
            }
        }
    }
}

13. Surface

Surface 从字面上来理解,是一个平面,在 Material Design 设计准则中也同样如此,我们可以将很多的组件摆放在这个平面之上,我们可以设置这个平面的边框,圆角,颜色等等。接下来,我们来用 Surface 组件做出一些不同的效果。

Surface(
        shape = RoundedCornerShape(8.dp),
        elevation = 10.dp,
        modifier = Modifier
            .width(300.dp)
            .height(100.dp),
        //改变圆角内部分
        color = Color.LightGray,
        //会改变文字颜色
        contentColor = Color.Yellow
    ) {
        Row(
            modifier = Modifier
                .clickable {}
        ) {
            Image(
                painter = painterResource(id = R.drawable.car2),
                contentDescription = stringResource(R.string.app_name),
                modifier = Modifier.size(100.dp),
                contentScale = ContentScale.Crop
            )
            Spacer(Modifier.padding(horizontal = 12.dp))
            Column(
                modifier = Modifier.fillMaxHeight(),
                verticalArrangement = Arrangement.Center
            ) {
                Text(
                    text = "Liratie",
                    style = MaterialTheme.typography.h6
                )
                Spacer(Modifier.padding(vertical = 8.dp))
                Text(
                    text = "礼谙"
                )
            }
        }
    }

14. Scaffold

@Composable
fun ScaffoldDemo() {
    var selectedItem by remember { mutableStateOf(0) }
    val items = listOf("主页", "我喜欢的", "设置")
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text("主页")
                },
                navigationIcon = {
                    IconButton(
                        onClick = { } //do something
                    ) {
                        Icon(Icons.Filled.ArrowBack, null)
                    }
                },
                actions = {
                    IconButton(
                        onClick = { } //do something
                    ) {
                        Icon(Icons.Filled.Search, null)
                    }
                    IconButton(
                        onClick = { } //do something
                    ) {
                        Icon(Icons.Filled.MoreVert, null)
                    }
                }
            )
        },
        bottomBar = {
            BottomNavigation {
                items.forEachIndexed { index, item ->
                    BottomNavigationItem(
                        icon = {
                            when (index) {
                                0 -> Icon(Icons.Filled.Home, contentDescription = null)
                                1 -> Icon(Icons.Filled.Favorite, contentDescription = null)
                                else -> Icon(Icons.Filled.Settings, contentDescription = null)
                            }
                        },
                        label = { Text(item) },
                        selected = selectedItem == index,
                        onClick = { selectedItem = index }
                    )
                }
            }
        }
    ) {
    }
}

15. Pager ->ViewPager

Pager 即传统 View 体系中 ViewPager 的替代,但在使用上大大降低了复杂度。它包括 VerticalPagerHorizontalPager 两类,分别对应纵向和横向的滑动。Pager 的底层基于 LazyColumn/LazyRow 实现,在使用上也基本与它们等同

HorizontalPager(pageCount = 10) { page ->
        // 每一页的内容,比如显示个文本
        Text(
            text = "Page: $page",
            modifier = Modifier.fillMaxWidth().height(100.dp),
            color = Color.Blue
        )
    }

待补充


16. Animation

animate*AsState 函数是 Compose 中最简单的动画 API,用于为单个值制作动画。你只需提供结束值(或目标值),API 就会从当前值到指定值开始动画。

ComposeFloatColorDpSizeBoundsOffsetRectIntIntOffsetIntSize 提供 animate*AsState 函数。通过为带有通用类型的 animateValueAsState 提供 TwoWayConverter,可以轻松添加对其他数据类型的支持。

@Composable
fun AnimationDemo() {
    var change by remember{ mutableStateOf(false) }
    var flag by remember{ mutableStateOf(false) }
    val buttonSize by animateDpAsState(
        targetValue = if(change) 32.dp else 24.dp
    )
    if(buttonSize == 32.dp) {
        change = false
    }
    IconButton(
        onClick = {
            change = true
            flag = !flag
        }
    ) {
        Icon(Icons.Rounded.Favorite,
            contentDescription = null,
            modifier = Modifier.size(buttonSize),
            tint = if(flag) Color.Red else Color.Gray
        )
    }
}

Animatable 是一个能够提供初始值的基础 APIanimate*AsState 系列 API 其内部均使用 Animatable 定制完成的。当你希望对自定义的数据类型进行动画计算时,可以选择使用 Animatable 来完成,使用Animatable重新实现上述案例

var change by remember{ mutableStateOf(false) }
    var flag by remember{ mutableStateOf(false) }
    //在 Composable 使用 Animatable 时,其必须包裹在 rememebr 中
    val buttonSizeVariable = remember {
        Animatable(24.dp, Dp.Companion.VectorConverter)
    }
    LaunchedEffect(change) {
        buttonSizeVariable.animateTo(if(change) 32.dp else 24.dp)
    }
    if(buttonSizeVariable.value == 32.dp) {
        change = false
    }
    IconButton(
        onClick = {
            change = true
            flag = !flag
        }
    ) {
        Icon(
            Icons.Rounded.Favorite,
            contentDescription = null,
            modifier = Modifier.size(buttonSizeVariable.value),
            tint = if(flag) Color.Red else Color.Gray
        )
    }

updateTransiton 是 Jetpack Compose 中实现过渡动画的关键 API 。所谓过渡动画,即当依赖的某个状态发生改变时连锁发生的一系列动画效果。前面我们所提到的 AnimateStateAnimatable 都是针对一个属性进行变换的,而 updateTransition 允许开发者将多个属性数值绑定到一个状态,当这个状态发生改变时,多个属性同时进行变换。

@Composable
fun SwitchBlock(){
    var selectedState: SwitchState by remember { mutableStateOf(SwitchState.CLOSE) }
    val transition = updateTransition(selectedState, label = "switch_transition")
    val selectBarPadding by transition.animateDp(transitionSpec = { tween(1000) }, label = "") {
        when (it) {
            SwitchState.CLOSE -> 40.dp
            SwitchState.OPEN -> 0.dp
        }
    }
    val textAlpha by transition.animateFloat(transitionSpec = { tween(1000) }, label = "") {
        when (it) {
            SwitchState.CLOSE -> 1f
            SwitchState.OPEN -> 0f
        }
    }
    Box(
        modifier = Modifier
            .size(150.dp)
            .padding(8.dp)
            .clip(RoundedCornerShape(10.dp))
            .clickable {
                selectedState = if (selectedState == SwitchState.OPEN) SwitchState.CLOSE else SwitchState.OPEN
            }
    ) {
        Image(
            painter = painterResource(id = R.drawable.car2),
            contentDescription = "node_background",
            contentScale = ContentScale.FillBounds
        )
        Text(
            text = "点我",
            fontSize = 30.sp,
            fontWeight = FontWeight.W900,
            color = Color.White,
            modifier = Modifier
                .align(Alignment.Center)
                .alpha(textAlpha)
        )
        Box(modifier = Modifier
            .align(Alignment.BottomCenter)
            .fillMaxWidth()
            .height(40.dp)
            .padding(top = selectBarPadding)
            .background(Color(0xFF5FB878))
        ) {
            Row(modifier = Modifier
                .align(Alignment.Center)
                .alpha(1 - textAlpha)
            ) {
                Icon(painter = painterResource(id = R.drawable.visual_pwd), contentDescription = "star", tint = Color.White)
                Spacer(modifier = Modifier.width(2.dp))
                Text(
                    text = "已选择",
                    fontSize = 20.sp,
                    fontWeight = FontWeight.W900,
                    color = Color.White
                )
            }
        }
    }
}

17. 自定义主题方案

18. 手势识别

当需要更大灵活性时,您可以通过 pointerInput 修饰符提供点按手势检测器

Image(
        painter = painterResource(id = R.drawable.car2),
        contentDescription = null,
        modifier = Modifier.size(300.dp).pointerInput(Unit){
            detectTapGestures(
                onPress = {
                          Log.i("lgf","onPress")
                },
                onDoubleTap = {Log.i("lgf","onDoubleTap")},
                onTap = {Log.i("lgf","onTap")},
                onLongPress = {Log.i("lgf","onLongPress")}
            )
        }
    )

对于长按点击、双击等复合类点击手势,我们可以使用 CombinedClickable 修饰符来实现手势监听。与 Clickable 修饰符一样,他同样也可以监听单击手势,并且也会为被点击的组件施加一个波纹涟漪效果动画的蒙层。

val enableState by remember {
        mutableStateOf<Boolean>(true)
    }
    Image(
        painter = painterResource(id = R.drawable.car2),
        contentDescription = null,
        modifier = Modifier
            .size(300.dp)
            .combinedClickable(enabled = enableState, onLongClick = {
                Log.i("lgf","onLongClick")
            }, onDoubleClick = {
                Log.i("lgf","onDoubleClick")
            }) {
                Log.i("lgf","onClick")
            }
    )

个人感觉双击有点问题

2.如何获取context

LocalContext.current

3.类似RecycleView的item点击事件处理

函数类型参数传递到Item中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值