详解Jetpack Compose中的Modifier修饰符

news2024/11/23 2:30:39

前言

        本文将会介绍Jetpack Compose中的Modifier。在谷歌官方文档中它的描述是这么一句话:Modifier元素是一个有序、不可变的集合,它可以往Jetpack Compose UI元素中添加修饰或者各种行为。例如,背景、填充和单击事件监听器装饰或添加行为到文本或按钮。  

        Modifier修饰符的作用是快速的修改组件的显示大小、边距、边框、背景颜色、剪裁、点击等等,Modifier在Compose的全部组件上都有存在,需要熟练的掌握。另外Modifier是可以被扩展函数扩展的,所以此篇博客只举例通用的Modifier,不举例只在某些组件下才能使用的Modifier修饰符(例如Box(align属性)与constraintlayout(constrainAs方法))。

        Modifier 可以通过链式调用的写法来为组件应用一系列的样式,每个基础的 Composable 组件都有一个 modifier 属性。

有序性

官方对修饰符定义的这个特性包含两个层面的意思,一是修饰符的使用是链式的它是有先后排列顺序的,二是这些顺序不仅仅是排列上的先后同时也是属性对UI展示效果起作用的先后顺序。接下来举一个例子来说明修饰符的有序的特性。

Box {
  val padding = 16.dp
  val onClick = Unit;
  Box(
      Modifier
          .clickable(onClick = { onClick })
          .padding(padding)
          .align(Alignment.Center)
          .size(50.dp, 50.dp)
          .background(Color.Green)
  )
}
Box {
    val padding = 16.dp
    val onClick = Unit;
    Box(
        Modifier
            .padding(padding)
            .clickable(onClick = { onClick })
            .align(Alignment.Center)
            .size(50.dp, 50.dp)
            .background(Color.Green)
    )
}

上面两段代码的意思都是在一个绿色背景宽高为50,50的Box控件中设置点击事件和Padding。首先我们可以直观的看到修饰符的使用是链式的符合咱们说的第一个有序性。可以清楚的看到第一张图波纹是作用范围是涵盖了边距的而第二张水波动画的范围仅仅作用在绿色矩形内部。第一段代码是先设置的点击事件而后设置的边距,而第二段代码是先设置边距而后设置了点击事件,就出现了不同的动画表现。我们通过这个例子就可以直观的看到修饰符的第二个有序性,也就是修饰属性对于UI展示作用的有序性,即属性发生作用的先后顺序和设置顺序是一致的。

不可变性

对于修饰符的不可变性笔者认为就是当设置的属性值确定时,被修饰的UI就确定了不会再变了。下面就展示一个位于屏幕中心,背景为绿色,旋转45度,宽度等比例拉伸2倍的正方形。

Box {
  Box(
      Modifier
          .rotate(45f)
          .scale(2f)
          .align(Alignment.Center)
          .size(50.dp, 50.dp)
          .background(Color.Green)
  )
}

如图:

 

设置背景

设置背景颜色

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好",
            color = Color.Gray,
            modifier = Modifier.background(Color.White)
        )
    }

效果图:

 

设置圆角背景

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好",
            color = Color.Gray,
            modifier = Modifier.background(Color.White, shape = RoundedCornerShape(10.dp))
        )
    }

效果图:

渐变色背景

    @Preview
    @Composable
    fun MyText() {
        val colors = listOf(Color(0xFF005599),Color(0xFF3FFFED))
        Text(
            text = "你好",
            color = Color.White,
            modifier = Modifier.background(brush = Brush.linearGradient(colors),//设置线性渐变效果
                shape = RoundedCornerShape(10.dp),
                alpha = 1f)//设置透明度
        )
    }

效果图

设置宽高

设置指定宽高

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好",
            color = Color.White,
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
        )
    }

效果:

填满宽高

    @Preview
    @Composable
    fun MyText() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.Black)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.White)
                    .fillMaxSize()//填满宽高
            )
        }
    }

效果图:

自适应子组件高宽

 IntrinsicSize.Min 这个是关键

    @Preview
    @Composable
    fun MyText() {
        Box(
            modifier = Modifier
                .width(IntrinsicSize.Min)
                .height(IntrinsicSize.Min)
                .background(Color.Gray)
        ) {
            Text(
                text = "你好世界",
                modifier = Modifier.width(60.dp)
            )
        }
    }

效果图:

按比例填充宽高

    @Preview
    @Composable
    fun MyText() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.Black)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.White)
                    .fillMaxSize(0.5f)//以50%比例填满宽高
            )
        }
    }

效果:

 

填满宽度或者高度

    @Preview
    @Composable
    fun MyText() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.Black)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Red)
                    .fillMaxWidth() //填满宽度
            )
            Text(
                text = "世界",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Yellow)
                    .fillMaxHeight() //填满高度
            )
        }
    }

效果:

按比例填充宽度与高度

    @Preview
    @Composable
    fun MyText() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.Black)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Red)
                    .fillMaxWidth(0.5f) //填满宽度
            )
            Text(
                text = "世界",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Yellow)
                    .fillMaxHeight(0.5f) //填满高度
            )
        }
    }

效果图:

默认最小宽高

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好",
            color = Color.Black,
            modifier = Modifier
                .background(Color.Red)
                .defaultMinSize(minWidth = 50.dp, minHeight = 50.dp)
        )
    }

设置边距

    @Preview
    @Composable
    fun MyText() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(150.dp)
                .background(Color.Black)
        ) {
            Text(
                text = "A",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Red)
                    .padding(5.dp) //设置全部边距
            )

            Text(
                text = "B",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Green)
                    .padding(horizontal = 20.dp) //设置横向边距
            )

            Text(
                text = "C",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Yellow)
                    .padding(vertical = 10.dp) //设置竖向边距
            )

            Text(
                text = "D",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.Blue)
                    .padding(0.dp,5.dp,50.dp,0.dp) //设置指定方向边距
            )
        }
    }

效果图:

设置边框

 一共有三种重载方法

  1. fun Modifier.border(border: BorderStroke, shape: Shape = RectangleShape) = border(width = border.width, brush = border.brush, shape = shape)
  2. fun Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape) = border(width, SolidColor(color), shape)
  3. fun Modifier.border(width: Dp, brush: Brush, shape: Shape): Modifier

第一种实现渐变色边框

    @Preview
    @Composable
    fun MyLayout() {
        val colors = listOf(Color(0xFF005599), Color(0xFF3FFFED))
        Column(
            modifier = Modifier
                .width(25.dp)
                .height(25.dp)
                .background(Color.White)
                .border(  //设置渐变边框
                    border = BorderStroke(1.dp, Brush.linearGradient(colors)), //设置边框粗细与边框渐变色
                    shape = RoundedCornerShape(5.dp) //圆角形状
                )
        ){ }
    }

效果图:

第二种实现单色边框

    @Preview
    @Composable
    fun MyLayout() {
        Column(
            modifier = Modifier
                .width(25.dp)
                .height(25.dp)
                .background(Color.White)
                .border(
                    width = 1.dp, //设置边框粗细
                    color = Color(0xFF000000), //边框颜色
                    shape = CutCornerShape (5.dp,5.dp,5.dp,5.dp) //切角矩形形状
                )
        ){ }
    }

效果图:

第三种实现渐变色边框

 其实与第一种差不多,只是没有了BorderStroke包装

    @Preview
    @Composable
    fun MyLayout() {
        val colors = listOf(Color(0xFF005599), Color(0xFF3FFFED))
        Column(
            modifier = Modifier
                .width(25.dp)
                .height(25.dp)
                .background(Color.White)
                .border(
                    width = 1.dp,
                    brush = Brush.linearGradient(colors),
                    shape = RoundedCornerShape(0.dp,0.dp,5.dp,5.dp)
                )
        ){ }
    }

效果图:

偏移位置

offset这个偏移是偏移自身这个组件 

    @Preview
    @Composable
    fun MyLayout() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.White)
        ){ 
            Text(text = "你好", color = Color.Black,
                //相对偏移 这个修改会根据布局方向自动调整水平偏移量(应该是为了阿拉伯语设计的):
                //当布局方向为从左到右时,正x偏移量会将内容向右移动,当布局方向为从右到左时,正x偏移量会将内容向左移动。
                modifier = Modifier.offset(x = 20.dp,y = 25.dp))

            Text(text = "世界", color = Color.Black,
                //绝对偏移 这个修改将不考虑布局方向:正的x偏移总是将内容向右移动
                modifier = Modifier.absoluteOffset(x = 45.dp,y = 20.dp))
        }
    }

效果图:

 

缩放

    @Preview
    @Composable
    fun MyScale() {
        val scale = remember {
            mutableStateOf(1.5f)
        }
        Column(
            modifier = Modifier
                .fillMaxSize()
                .background(Color.Gray)
        ) {
            //默认
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
            )
            //比例缩放
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .padding(top = 30.dp, start = 30.dp)
                    .scale(scale.value) //放大1.5倍
            )
            //指定宽高缩放
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .padding(top = 70.dp, start = 90.dp)
                    .scale(3f,2f) //宽高缩放
            )
        }
    }

效果图:

旋转

    @Preview
    @Composable
    fun MyRotate() {
        val rotate = remember { mutableStateOf(180f) }
        Column(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .padding(100.dp)
                    .rotate(rotate.value) //设置旋转
            )
        }
    }

效果图:

旋转与偏移位置的组合

请注意,Modifier一直强调组合的位置不同会出现不同的效果。如果你想实现旋转与拖动功能,那么旋转一定要在前面,否则会出现拖动的时候,实际移动的X轴与Y轴出现相反的问题。

@Composable
fun rotationAndOffset(){
    val rotation = remember { mutableStateOf(0f) }
    val offsetX = remember { mutableStateOf(0f) }
    val offsetY = remember { mutableStateOf(0f) }
    AsyncImage(
        model = if (mIsUrl.value) mUrl.value else mFile.value,
        contentDescription = null,
        modifier = Modifier
            .rotate(rotation.value) //rotate一定要在前面
            .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
    )
}

输入操作(点击、双击、长按、拖动、触控、滚动)

单指操作_单击、按下、双击、长按、触控

    @OptIn(ExperimentalFoundationApi::class)
    @Preview
    @Composable
    fun MyClick() {
        val isEnableClick = remember {
            mutableStateOf(true)
        }

        Column(
            modifier = Modifier
                .width(100.dp)
                .height(150.dp)
                .background(Color.Black)
        ) {
            //方式一 单击监听
            Text(
                text = "A",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.White)
                    .clickable(isEnableClick.value) { //设置点击
                        Log.e("zh", "A单击")
                    }
            )
            //方式二 组合性点击
            Text(
                text = "B",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.White)
                    .combinedClickable( //注意!此Api是实验性的
                        onClick = {
                            Log.e("zh", "B单击")

                        },
                        onDoubleClick = {
                            Log.e("zh", "B双击")

                        },
                        onLongClick = {
                            Log.e("zh", "B长按")

                        }
                    )
            )
            //方式三 输入监听
            Text(
                text = "C",
                color = Color.Black,
                modifier = Modifier
                    .background(Color.White)
                    .pointerInput(Unit) {

                    detectTapGestures(
                        onPress = {
                            Log.e("zh", "C按下")
                        },
                        onDoubleTap = {
                            Log.e("zh", "C双击")
                        },
                        onLongPress = {
                            Log.e("zh", "C长按")
                        },
                        onTap = {
                            Log.e("zh", "C触控")
                        }
                    )
                }
            )
        }
    }

多指操作_缩放、旋转、拖动

双指缩放

    @Preview
    @Composable
    fun MyScale() {
        val scale = remember {
            mutableStateOf(1f)
        }
        Column(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            //多点触控
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .padding(100.dp)
                    .scale(scale.value)
                    .transformable(state = TransformableState { zoomChange, panChange, rotationChange ->
                        scale.value *= zoomChange
                        Log.e("zh", "缩放 ${zoomChange}" )
                        Log.e("zh", "坐标 ${panChange}")
                        Log.e("zh", "旋转 ${rotationChange}" )
                    })
            )
        }
    }

效果图:

双指旋转

    @Preview
    @Composable
    fun MyRotate() {
        val rotate = remember { mutableStateOf(0f) }
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray),
            contentAlignment = Alignment.Center

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .fillMaxSize(0.5f)
                    .rotate(rotate.value) //设置旋转
                    .transformable(state = TransformableState { zoomChange, panChange, rotationChange ->
                        Log.e("zh", "缩放 ${zoomChange}" )
                        Log.e("zh", "坐标 ${panChange}")
                        rotate.value += rotationChange //旋转变化
                        Log.e("zh", "旋转 ${rotationChange}" )
                    })
            )
        }
    }

效果图:

双指拖动

    @Preview
    @Composable
    fun MyOffset() {
        val offsetX = remember { mutableStateOf(0f) }
        val offsetY = remember { mutableStateOf(0f) }
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray),
            contentAlignment = Alignment.Center

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .fillMaxSize(0.5f)
                    .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) } //设置偏移
                    .transformable(state = TransformableState { zoomChange, panChange, rotationChange ->
                        Log.e("zh", "缩放 ${zoomChange}" )
                        Log.e("zh", "坐标 ${panChange}")
                        offsetX.value += panChange.x
                        offsetY.value += panChange.y
                        Log.e("zh", "旋转 ${rotationChange}" )
                    })
            )
        }
    }

效果图:

双指多操作组合

同时拖动、旋转、缩放

    @Preview
    @Composable
    fun MyCombination() {
        val scale  = remember { mutableStateOf(1f) }
        val offsetX = remember { mutableStateOf(0f) }
        val offsetY = remember { mutableStateOf(0f) }
        val rotation = remember { mutableStateOf(0f) }
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray),
            contentAlignment = Alignment.Center

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .fillMaxSize(0.5f)
                    .graphicsLayer{
                        scaleX = scale.value
                        scaleY = scale.value
                        translationX = offsetX.value
                        translationY = offsetY.value
                        rotationZ = rotation.value
                    }
                    .transformable(state = TransformableState { zoomChange, panChange, rotationChange ->
                        scale.value *= zoomChange
                        offsetX.value += panChange.x
                        offsetY.value += panChange.y
                        rotation.value += rotationChange
                    })
            )
        }
    }

效果图:

单指拖动

单方向拖动

如果你发现拖动会超出父类布局,但是你希望不超出父类布局,请参考博客下面的 “让子组件不超出当前父组件范围” 内容

    @Preview
    @Composable
    fun MyImage() {
        var offset = remember { mutableStateOf(0f) }
        Column(
            modifier = Modifier
                .width(200.dp)
                .height(200.dp)
                .background(Color.Gray)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                contentScale = ContentScale.None,
                modifier = Modifier
                    .draggable(state = rememberDraggableState() { //设置拖动
                        offset.value += it
                    }, orientation = Orientation.Vertical) //设置方向 还能设置横向Orientation.Horizontal
                    .offset { IntOffset(0, offset.value.roundToInt()) }
            )
        }
    }

效果图:

双方向拖动

    @Preview
    @Composable
    fun MyImage() {
        val offsetX = remember { mutableStateOf(0f) }
        val offsetY = remember { mutableStateOf(0f) }
        Column(
            modifier = Modifier
                .width(200.dp)
                .height(200.dp)
                .background(color = Color.Gray)

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .offset { IntOffset(offsetX.value.roundToInt(), offsetY.value.roundToInt()) }
                    .pointerInput(Unit) {
                        //检测拖动手势
                        detectDragGestures { change, dragAmount ->
                            Log.e("zh", "MyImage:检测拖动 当前指针输入数据 = ${change} ")
                            Log.e("zh", "MyImage:检测拖动 每次偏移量 = ${dragAmount} ")
                            offsetX.value += dragAmount.x
                            offsetY.value += dragAmount.y
                        }
                    }
            )
        }
    }

效果图:

 

设置可滚动

横竖两个方向的滚动

    @Preview
    @Composable
    fun MyScroll() {
        Column(modifier = Modifier.fillMaxSize()) {
            //Row横向排列滚动
            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .horizontalScroll(rememberScrollState()) //设置横向滚动
            ) {
                for (i in 1..10) {
                    Image(
                        painter = painterResource(id = R.mipmap.ic_logo),
                        contentDescription = null,
                    )
                }
            }
            //Column竖向排列滚动
            Column(
                modifier = Modifier
                    .padding(10.dp)
                    .height(250.dp)
                    .verticalScroll(rememberScrollState()) //设置竖向滚动
            ) {
                for (i in 1..10) {
                    Image(
                        painter = painterResource(id = R.mipmap.ic_logo),
                        contentDescription = null,
                    )
                }
            }
        }

效果图:

监听滚动偏移量、滚动位置与滚动位置跳转操作

    @Preview
    @Composable
    fun MyScroll() {
        val state = rememberScrollState()
        val position = remember {
            derivedStateOf {
                state.value
            }
        }
        val scope = rememberCoroutineScope() //这个是在Composable调用协程的一种方式

        Column() {
            Text(
                text = "豫章故郡,洪都新府。星分翼轸,地接衡庐。" +
                        "襟三江而带五湖,控蛮荆而引瓯越。" +
                        "物华天宝,龙光射牛斗之墟;" +
                        "人杰地灵,徐孺下陈蕃之榻。" +
                        "雄州雾列,俊采星驰。" +
                        "台隍枕夷夏之交,宾主尽东南之美。" +
                        "都督阎公之雅望,棨戟遥临;" +
                        "宇文新州之懿范,襜帷暂驻。" +
                        "十旬休假,胜友如云;" +
                        "千里逢迎,高朋满座。" +
                        "腾蛟起凤,孟学士之词宗;" +
                        "紫电青霜,王将军之武库。" +
                        "家君作宰,路出名区;" +
                        "童子何知,躬逢胜饯。",
                modifier = Modifier
                    .padding(10.dp)
                    .width(100.dp)
                    .height(100.dp)
                    //监听滚动位置
                    .scrollable(state = rememberScrollableState(consumeScrollDelta = {
                        Log.e("zh", "当前滚动偏移量:${it}")
                        it
                    }), orientation = Orientation.Vertical)
                    .verticalScroll(state)
            )

            Text(
                color = Color.Red,
                text = "当前滚动位置${position.value}",
            )

            Button(onClick = {
                scope.launch {
                    state.scrollTo(0)
                }
            }) {
                Text(text = "回到起点")
            }
        }
    }

效果图:

指针输入

指针输入监听,与上面各色方法类似

    @Preview
    @Composable
    fun MyPointerInput() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .align(Alignment.Center)
                    .fillMaxSize(0.5f)
                    .background(color = Color.Green)
                    .pointerInput(Unit) {
                        /**
                         * 下面的只能存在一个手势Gestures方法,多了后续的手势方法会不执行,这边是方便观看全部贴出来
                         */
                        detectDragGestures { change, dragAmount ->
                            Log.e("zh", "检测拖动手势: change = $change \n dragAmount = $dragAmount")
                        }
                        detectDragGesturesAfterLongPress { change, dragAmount ->
                            Log.e("zh", "检测长按后的拖拽手势:  change = $change \n dragAmount = $dragAmount")
                        }
                        detectTapGestures {
                            /**
                             * 检测轻按、双击和长按手势,检测到时分别调用onTap、onDoubleTap和onLongPress。当检测到按压和pressgestuscope时调用onPress。
                             * tryAwaitRelease PressGestureScope。awaitRelease可以用来检测指针何时被释放或手势何时被取消。
                             * 第一个向下的指针和最后一个向上的指针被消耗,
                             * 在长按的情况下,检测到长按后的所有更改都被消耗。
                             */
                            Log.e("zh", "检测触控:  Offset = $it \n")
                        }
                        detectTapGestures(
                            onTap = {
                                Log.e("zh", "单击: Offset = $it \n")
                            },
                            onDoubleTap = {
                                Log.e("zh", "双击: Offset = $it \n")
                            },
                            onLongPress = {
                                Log.e("zh", "长按: Offset = $it \n")
                            },
                            onPress = {
                                Log.e("zh", "按下: Offset = $it \n")
                            })

                        detectTransformGestures { centroid, pan, zoom, rotation ->
                            Log.e("zh", "旋转、平移和缩放的手势检测器:  centroid = $centroid \n pan = $pan \n zoom = $zoom rotation = $rotation")

                        }
                        detectHorizontalDragGestures { change, dragAmount ->
                            Log.e("zh", "检测水平拖动手势:  change = $change \n dragAmount = $dragAmount")
                        }
                        detectVerticalDragGestures { change, dragAmount ->
                            Log.e("zh", "检测垂直拖动手势:  change = $change \n dragAmount = $dragAmount")
                        }
                    }
            )
        }
    }

graphicsLayer 

graphicsLayer可用于对内容应用效果,如缩放、旋转、不透明度、阴影和剪切。当你的层属性由androidx. composition .runtime. state或动画值支持时,最好使用这个版本,因为读取块中的状态只会导致层属性更新,而不会触发重新组合和重新布局。

    @Preview
    @Composable
    fun MyGraphicsLayer() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            Text(
                text = "你好",
                color = Color.Black,
                modifier = Modifier
                    .align(Alignment.Center)
                    .fillMaxSize(0.5f)
                    .background(color = Color.Green)
                    .graphicsLayer{
                        //绘制区域的水平比例尺。默认值为“1”
                        scaleX = 1f
                        //绘制区域的垂直比例尺。默认值为“1”。
                        scaleY = 1f
                        //绘制区域的alpha。设置为“1”以外的值将导致绘制的内容是半透明的,设置为“0”将使其完全不可见。默认值为' 1 ',取值范围为' 0 ' ~ ' 1 '。
                        alpha = 1f
                        //该层相对于其左边界的水平像素偏移量。默认值为' 0 '。
                        translationX = 0f
                        //该层相对于其上边界的垂直像素偏移量。默认值为' 0 '
                        translationY = 0f
                        //以像素为单位设置阴影的仰角。默认值为“0”,且不能为负值
                        shadowElevation = 0f
                        //范围阴影颜色
                        ambientShadowColor = Color(0xFFFF0000)
                        //点阴影颜色
                        spotShadowColor = Color(0xFFFF0000)
                        //X轴旋转角度
                        rotationX = 0f
                        //Y轴旋转角度
                        rotationY= 0f
                        //Z轴旋转角度
                        rotationZ = 0f
                        //观察相机视角距离
                        cameraDistance = 0f
                        //旋转or缩放的中心点
                        transformOrigin =  TransformOrigin.Center
                        transformOrigin =  TransformOrigin(0.1f,0.1f)
                        //形状
                        shape = RectangleShape
                        //设置为' true '将内容剪辑到[shape]。默认值为' false '
                        clip = false
                        //渲染效果,比如下面设置的模糊效果
                        renderEffect = BlurEffect(20f, 0.5f, TileMode.Clamp)
                    }
            )
        }
    }

裁剪

clip的裁剪

    @Preview
    @Composable
    fun MyImage() {
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.White)
                //clip的剪裁是对当前组件的内部内容进行裁剪,这里使用了CircleShape圆形进行裁剪.
                //也可以使用RectangleShape 或者 RoundedCornerShape() 矩形进行裁剪
                .clip(shape = CircleShape)

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier.fillMaxSize()
            )
        }
    }

效果图:

上面是使用常用形状裁剪,这里在举例一个自定义形状的裁剪方式,如下使用path绘制了一个三角形进行裁剪

    /**
     * 裁剪三角形
     */
    @Preview
    @Composable
    fun MyImage() {
        val customShape = object : Shape {
            override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
                val path = Path()
                path.moveTo(size.width/2, 0f)
                path.lineTo(0f,size.height)
                path.lineTo(size.width,size.height)
                path.close()
                val outline = Outline.Generic(path)
                return outline
            }
        }
        Column(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.White)
                .clip(shape = customShape)

        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier.fillMaxSize()
            )
        }
    }

效果图:

让子组件不超出当前父组件范围

clipToBounds 按照指定的边界裁切内容。强制限制当前布局下的子组件不允许超出到外部。另外这个属性与clip属性无关联。

反面例子代码,下面的代码中注释了clipToBounds 

    @Preview
    @Composable
    fun MyImage() {
        var offset  =  remember { mutableStateOf(0f) }
        //必须在套一个布局设置最大范围的父类布局,以展示下面的Image在拖动的时候超出的效果
        Column(modifier = Modifier.fillMaxSize().padding(200.dp)) {
            Column(
                modifier = Modifier
                    .width(200.dp)
                    .height(200.dp)
                    .background(Color.Gray) //这里设置背景色为灰色,以观察布局的大小
//                .clipToBounds()

            ) {
                Image(
                    painter = painterResource(id = R.mipmap.ic_logo),
                    contentDescription = null,
                    contentScale = ContentScale.None,
                    modifier = Modifier
                        .draggable(state = rememberDraggableState() {
                            offset.value += it
                        }, orientation = Orientation.Horizontal)
                        .offset { IntOffset(offset.value.roundToInt(), 0) }
                )
            }
        }
    }

 反面例子效果图,图片在拖动的时候可以超出布局范围:

调用clipToBounds 后的效果图(代码就不重复贴了,直接将clipToBounds 注释取消就行):

模糊

注意!blur这个api需要Android版本12 31 API的设备上才能生效

    @Preview
    @Composable
    fun MyBlur() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .align(Alignment.Center)
                    //注意blur这个api需要Android 12 31 API 才能生效
                    .blur(radius = 10.dp, edgeTreatment = BlurredEdgeTreatment.Rectangle) //设置模糊
            )
        }
    }

效果图:

BlurredEdgeTreatment.Rectangle 是矩形边缘清晰中间模糊

BlurredEdgeTreatment.Unbounded 无边界模糊的效果图:

透明度

    @Preview
    @Composable
    fun MyAlpha() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.Gray)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .align(Alignment.Center)
                    .alpha(0.1f) //设置透明度
            )
        }
    }

效果图:

绘制阴影

注意!shadow这个api需要Android版本10的设备上才能生效

    @Preview
    @Composable
    fun MyShadow() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.White)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .align(Alignment.Center)
                    .shadow( //设置阴影,调用此api需要设备Android版本为10
                        elevation = 50.dp, //隐藏长度
                        shape = RectangleShape, //阴影形状
                        ambientColor = Color.Red, //环境颜色
                        spotColor = Color.Red, //阴影颜色
                        clip = true //是否跟随裁剪形状改变阴影形状
                    )
            )
        }
    }

绘制

drawBehind绘制,将增加的绘制内容显示到组件图层的最下层

    @Preview
    @Composable
    fun MyDrawBehind() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.White)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .align(Alignment.Center)
                    .drawBehind { //绘制到最下面的图层
                        val radius = size.width / 3
                        val centerOffset = Offset(size.width, 0f)
                        drawCircle(
                            color = Color.Red,
                            radius = radius,
                            center = centerOffset,
                            style = Fill
                        )
                    }
            )
        }
    }

在下面的效果图里,可以看到红圈在图片的下层:

drawWithContent 人为控制绘制内容的顺序

    @Preview
    @Composable
    fun MyDrawWithContent() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.White)
        ) {
            Image(
                painter = painterResource(id = R.mipmap.ic_logo),
                contentDescription = null,
                modifier = Modifier
                    .align(Alignment.Center)
                    .drawWithContent {
                        /*
                        drawContent()是理解drawWithContent的关键
                        drawContent这个是这个组件本身需要绘制的内容。
                        控制这个方法的位置你就可以控制绘制的顺序。
                         */
                        drawContent()
                        val radius = size.width / 3
                        val centerOffset = Offset(size.width, 0f)
                        drawCircle(
                            color = Color.Red,
                            radius = radius,
                            center = centerOffset,
                            style = Stroke(25f)
                        )
                    }
            )
        }
    }

 

因为代码中是先绘制了drawContent()然后在绘制圆环,所以下面的效果图圆环在图片的上层:

drawWithCache 提供缓存数据避免重组的绘制方式

    @Preview
    @Composable
    fun MyDrawWithCache() {
        val count = remember { mutableStateOf(0) }
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.White)
        ) {
            Text(
                text = "计数",
                modifier = Modifier
                    .align(Alignment.Center)
                    .clickable { count.value++ }
                    /*
                     * drawWithCache 需要配合 onDrawWithContent 或者 onDrawBehind 使用
                     */
                    .drawWithCache {
                        val x = size.width + 20
                        val y = size.height - 10
                        val paint = Paint()
                        paint.textSize = 50f
                        paint.textAlign = Paint.Align.CENTER
                        Log.e("zh", "触发重组1 在这个代码块里不会发生重组")
                        onDrawWithContent {
                            Log.e("zh", "触发重组2 在这个代码块里发生重组,但是请注意mutableStateOf的数据也要放到这个代码块中")
                            drawIntoCanvas { canvas ->
                                canvas.nativeCanvas.drawText("${count.value}", x, y, paint)
                            }
                            drawContent()
                        }
                    }
            )
        }
    }

效果图:

Painter绘制

绘制图形

    @Preview
    @Composable
    fun MyPaint() {
        Box(
            modifier = Modifier
                .width(100.dp)
                .height(100.dp)
                .background(Color.White)
        ) {
            Text(
                text = "你好",
                modifier = Modifier
                    .align(Alignment.Center)
                    .paint(painter = object : Painter() {
                        override val intrinsicSize: Size
                            get() = Size(50f, 50f)

                        override fun DrawScope.onDraw() {
                            drawLine(
                                color = Color.Red,
                                Offset(0f, 0f),
                                Offset(size.width, size.height),
                                strokeWidth = 10f
                            )
                        }
                    })
            )
        }
    }

效果图:

绘制图像

有一些人会利用这个来绘制组件的背景,的确可以但是个人认为不太妥当...

    @Preview
    @Composable
    fun MyPaint() {
        Box(
            modifier = Modifier
                .width(300.dp)
                .height(300.dp)
                .background(Color.White)
        ) {
            Text(
                text = "你好",
                color = Color.White,
                modifier = Modifier
                    .align(Alignment.Center)
                    .paint(painterResource(id = R.mipmap.ic_logo))
            )
        }
    }

效果图:

组合顺序的影响

 Modifier的方法组合顺序是会影响到组件的实际效果的,这里使用两段代码演示(可以根据下面2代码看到点击范围发生了改变):

代码1:

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好世界你好世界",
            color = Color.Black,
            modifier = Modifier
                .background(Color.Green)
                .size(150.dp)
                .clickable { //点击在前
                    Log.e("zh", "MyGraphicsLayer: ")
                }
                .padding(50.dp) //padding在后
        )
    }

效果图:

代码2:

    @Preview
    @Composable
    fun MyText() {
        Text(
            text = "你好世界你好世界",
            color = Color.Black,
            modifier = Modifier
                .background(Color.Green)
                .size(150.dp)
                .padding(50.dp) //padding在前
                .clickable { //点击在后
                    Log.e("zh", "MyGraphicsLayer: ")
                }
        )
    }

效果图:

多个Modifier组合

下面的代码种,分别创建了backgroundModifier  与 borderModifier, 并且使用then将他们组合 

    @Preview
    @Composable
    fun MyText() {
        val backgroundModifier = Modifier
            .background(Color.Green)
            .fillMaxSize(0.2f)

        val borderModifier = Modifier
            .border(
                width = 1.dp, //设置边框粗细
                color = Color(0xFF000000), //边框颜色
                shape = RoundedCornerShape(5.dp) //切角矩形形状
            )

        Column {
            Text(
                text = "A",
                color = Color.Black,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .then(backgroundModifier)
                    .then(borderModifier)
                    .clickable {
                        Log.e("zh", "点击A")
                    }
            )
            Text(
                text = "B",
                color = Color.Black,
                textAlign = TextAlign.Center,
                modifier = Modifier
                    .then(backgroundModifier)
                    .then(borderModifier)
                    .clickable {
                        Log.e("zh", "点击B")
                    }
            )

        }
    }

end.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

依次对数组中的元素进行逻辑非和异或判断numpy.logical_not()numpy.logical_xor()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 依次对数组中的元素进行逻辑非和异或判断 numpy.logical_not() numpy.logical_xor() [太阳]选择题 下列代码中np.logical_xor(A, B)输出的结果是? import numpy as np A [True, …

苹果新增了28个以AI为重点的职位,希望利用这种AI技术改变iPhone和iPad

🚀 苹果新增了28个以AI为重点的职位,希望利用这种AI技术改变iPhone和iPad 苹果公司限制了使用ChatGPT等外部AI工具,正在开发自主的生成式AI技术。 苹果此前新增了28个以AI为重点的职位,希望利用这种AI技术改变iPhone和iPad。 此…

IntelliJ 导出和导入配置文件

导出: File->Manage IDE Setting->Export Settings…->选择导出目录,可以得到一个 settings.zip 的文件 导入方法: File->Manage IDE Setting->Import Settings…->选择需要导入的文件即可,导入之后重启IDE。

13-2-CSS3-渐变色、2D转换、3D转换

一、渐变色 CSS渐变色(Gradient)是指在元素背景中使用两种或多种不同的颜色进行过渡,超过两个颜色可以形成更为细腻的渐变效果。常见的CSS渐变色有线性渐变和径向渐变。 1. 线性渐变:Linear Gradients 向下/向上/向左/向右/对角…

CyberLink的屏幕录制软件Screen Recorder Deluxe 4.3版本在win10系统的下载与安装配置教程

目录 前言一、Screen Recorder Deluxe安装二、使用配置总结 前言 Screen Recorder Deluxe是由CyberLink公司开发的一款高效的屏幕录制工具,该软件提供了全面而易于使用的功能,可以满足广泛的屏幕录制需求。 Screen Recorder Deluxe的一些主要特点&…

代码随想录算法训练营 Day 46 | 139.单词拆分,关于多重背包,你该了解这些!,背包问题总结篇!

139.单词拆分 讲解链接:代码随想录-139.单词拆分 确定 dp 数组以及下标的含义:dp[i] : 字符串长度为 i 的话,dp[i]为 true,表示可以拆分为一个或多个在字典中出现的单词。 确定递推公式:如果确定 dp[j] 是 true&…

PyTorch-Dataset

Dataset类: 如何获取数据及标签。 Dataloader类:为之后的网络提供不同的数据形式。 1. 数据文件夹表示: from torch.utils.data import Dataset from PIL import Image import osclass MyData(Dataset):def __init__(self, root_dir, label_d…

三十七、雪崩问题、Sentinel、簇点链路、流控模式

1、初识Sentinel 1.1雪崩问题 微服务调用链路中的某个服务故障,引起整个链路中的所有微服务都不可用,这就是雪崩。 解决雪崩问题的常见方式有四种: 超时处理:设定超时时间,请求超过一定时间没有响应就返回错误信息&am…

Rust每日一练(Leetday0006) 三数之和、字母组合、四数之和

目录 16. 最接近的三数之和 3Sum Closest 🌟🌟 17. 电话号码的字母组合 Letter-combinations-of-a-phone-number 🌟🌟 18. 四数之和 4Sum 🌟🌟 🌟 每日一练刷题专栏 🌟 Rust…

肝一肝设计模式【九】-- 享元模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 肝一肝设计模式【五】-- 适配器模式 传送门 肝一肝设计模式【六】-- 装饰器模式 传送门 肝…

java枚举类解读

目录 为什么需要枚举类 枚举类的使用 枚举类的实现 枚举类的属性 自定义枚举类 使用enum定义枚举类 常见enum的使用场景 switch 向枚举中添加新方法 覆盖枚举的方法 Enum类的主要方法 实现接口的枚举类 为什么需要枚举类 类的对象只有有限个,确定的。…

如何将map与对象进行转换

Spring Boot内置了一个强大的JSON转换器Jackson,可以实现将JSON字符串或Map类型的数据转换成Java对象。以下是将Map类型的数据转换成Java对象的示例代码: import com.fasterxml.jackson.databind.ObjectMapper;//定义Java对象 public class User {priva…

oracle客户端的安装教程

文章目录 一、安装前的准备工作 1.1、百度网盘安装包的连接 1.2、百度网盘oracle11g软件包 二、oracle数据库客户端的安装与数据的准备 安装步骤 前言 本文主要讲解oracle客户端的安装与简单使用过程 一、安装前的准备工作 1.1、百度网盘安装包的连接 客户端的软件包 …

STM32 FMC篇-SDRAM(IS42S16400J)

IS42S16400J 这个东西太常见啦,长方形的。不会过多解释,详细请阅读它的数据手册。 IS42S16400J是一种高速同步动态随机存储器(SDRAM),64Mb的存储容量,采用4个bank,每个bank大小为16Mb,总线宽度为16位&…

【CW32开发】00 开发环境搭建和示例代码运行

1.下载MDK 2.下载芯片相关的固件库 我用的是芯片是CW32F030系列,所以下载相应的固件库 下载地址:https://www.whxy.com/support/filelist/13 3.安装固件库 解压下载的文件,并在cw32f030-stdperiph-lib\IdeSupport\MDK路径下安装固件库 …

064:cesium设置点划线材质(material-8)

第064个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置点划线材质,请参考源代码,了解PolylineDashMaterialProperty的应用。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共89行)相关API参考:专栏…

CBFS Vault 2022 for .NET Crack

将多个文件打包到一个 Vault - 一个“文件中的文件系统”,完成每个文件的压缩、透明加密和随机读/写访问。 亮点包括新的日记选项、用于更好地控制和跟踪的新事件,以及一系列核心性能和可用性改进 [了解更多]。 CBFS保险库 在任何地方存储一个完整的文件…

chatgpt赋能Python-pythone怎么下载

Python下载:在SEO中的作用 介绍 Python是一种广泛应用于编程、自动化任务和数据处理的强大语言。在SEO中,Python也扮演了越来越重要的角色。Python有许多库和工具可供下载,这使得它成为了SEO领域中必不可少的一部分。在本文中,我…

人工智能之读懂CNN卷积神经网络

通过往期文章的分享,我们了解了神经网络的结构,一般分为输入层,隐藏层,输出层 TensorFlow神经网络 那什么是卷积神经网络那,这就要我们追溯一下人类识别图像的原理 人类的视觉原理如下:从原始信号摄入开始(瞳孔摄入像素 Pixels),接着做初步处理(大脑皮层某些细胞发现…

chatgpt赋能Python-pythondna匹配

Python DNA匹配:从概念到应用 概述 DNA匹配是生物学中的一个重要领域,它意味着找到两个DNA序列之间的相似性并确定它们之间的关系。Python是一种被广泛用于生物信息学中的编程语言,它提供了一系列强大的库和工具来处理DNA序列数据&#xff…