在xml文件中经过各种特点来描述View,在Compose中经过Modifier润饰符来界说UI组件的款式。

在Compose中,每个基础UI组件都有一个Modifier参数,经过界说Modifier来修改组件的款式。

一、常用的润饰符

API 阐明
align 设置组件在父容器中的对齐办法。
alpha 设置组件的透明度。
aspectRatio 设置组件的宽高比。
background 设置组件的布景款式。
border 设置组件的边框款式。
clickable 设置组件可点击。
clip 设置组件的裁剪效果。
clipToBounds 设置组件是否裁剪超出鸿沟的内容。
fillMaxHeight 将组件的高度设置为最大可用空间。
fillMaxSize 将组件的尺寸设置为最大可用空间。
fillMaxWidth 将组件的宽度设置为最大可用空间。
focusRequester 设置组件的焦点恳求器。
height 设置组件的固定高度。
indication 设置组件的接触反应效果。
layout 设置组件的自界说布局规则。
offset 设置组件的偏移量。
padding 设置组件的内边距。
pointerInput 设置组件的指针输入处理。
requiredHeight 设置组件的最小高度。
requiredSize 设置组件的最小尺寸。
requiredWidth 设置组件的最小宽度。
rotate 设置组件的旋转视点。
scale 设置组件的缩放比例。
shadow 设置组件的阴影效果。
size 设置组件的尺寸。
swipeable 设置组件可滑动。
testTag 为组件设置测试标签。
weight 设置组件在父容器中的权重。
width/height 设置组件的固定宽高度。
zIndex 设置组件的堆叠次序。
draggable 获取组件单向的拖拽的偏移量

二、常用润饰符用法示例

1、Modifier.size

Image(
    painter = painterResource(id = R.mipmap.rabit),
    contentDescription = "图片描述",
    modifier = Modifier
        //.size(150.dp)       <--------这个也行,下面是重载办法
        .size(width = 150.dp, height = 150.dp)
        .clip(CircleShape)
)

宽高150dp的圆角图片就完成了

Jetpack Compose(二)-Modifier修饰符

假如想要设置宽度为屏幕宽度,高度为300dp,应该怎么写?
办法一:

Box(
    modifier = Modifier
        .fillMaxWidth()
        .height(300.dp)
        .background(Color.Blue)
) { }

办法二:

import androidx.compose.ui.platform.LocalConfiguration
//获取屏幕宽度的dp值
val screenWidth = LocalConfiguration.current.screenWidthDp.dp
Box(
    modifier = Modifier
        .size(height = 300.dp, width = screenWidth)
        .background(Color.Blue)
) { }

2、Modifier.background

backgroud润饰符用来为被润饰组件增加布景色。布景色支持设置color的纯色布景,也能够运用brush设置渐变色布景。

Row {
    //Box1
    Box(
        modifier = Modifier
            .size(150.dp)
            .background(color = Color.Blue)             <--------纯色布景
    ) {
        Text(text = "纯色", Modifier.align(Alignment.Center), color = Color.White)
    }
    // 增加水平间距
    Spacer(modifier = Modifier.width(50.dp))
    //Box2
    Box(
        modifier = Modifier
            .size(150.dp)
            .background(              <--------渐变色布景
                brush = Brush.horizontalGradient(    //创立Brush水平方向的线性渐变色
                    listOf(
                        Color.Cyan,
                        Color.Blue,
                        Color.Green
                    )
                )
            )
    ) {
        Text(text = "渐变色", Modifier.align(Alignment.Center), color = Color.White)
    }
}

运转在手机上的效果

Jetpack Compose(二)-Modifier修饰符

xml中View的background特点能够设置图片格式的布景,Compose的background润饰符只能设置色彩布景,图片布景需求运用其他组件完成。下面是一些示例:

办法一:

Box(
    modifier = Modifier
        .fillMaxWidth()
        .height(300.dp)
        .paint(painterResource(id = R.mipmap.rabit), contentScale = ContentScale.Fit)   <----布景图
) { }

办法二:

@Composable
fun ShowImage() {
    // 获取屏幕的参数
    val density = LocalDensity.current.density      //屏幕密度
    val screenWidthDp = LocalConfiguration.current.screenWidthDp.dp   //屏幕宽度的dp值
    val screenHeightDp = LocalConfiguration.current.screenHeightDp.dp  //屏幕高度的dp值
    val screenWidthPx = LocalConfiguration.current.screenWidthDp.dp.value * density  //屏幕宽度的px值
    val screenHeightPx = LocalConfiguration.current.screenHeightDp.dp.value * density //屏幕高度的px值
    //将资源图片转化为ImageBitmap
    val option = BitmapFactory.Options().apply {
        inPreferredConfig = Bitmap.Config.ARGB_8888
    }
    val imageBitmap =
        BitmapFactory.decodeResource(LocalContext.current.resources, R.mipmap.rabit, option)
            .asImageBitmap()
    Box(
        modifier = Modifier
            .background(Color.Green)
            .width(screenWidthDp)
            .height(screenHeightDp) // 设置Box组件宽高为屏幕的宽高
            .drawBehind {
                drawImage(        <-------制作布景
                    imageBitmap,
                    srcOffset = IntOffset.Zero,
                    srcSize = IntSize(imageBitmap.width, imageBitmap.height),   //制作的图片巨细
                    dstOffset = IntOffset.Zero,
                    dstSize = IntSize(     //图片宽度为屏幕宽度,高度为屏幕高度的一半
                        screenWidthPx.toInt(),             <---------留意这儿简略错
                        (screenHeightPx / 2F).toInt()      <---------留意这儿简略错
                    )
                )
            }
    ) { }
}

UI上显现的效果,图片被拉伸占满了一半的屏幕

Jetpack Compose(二)-Modifier修饰符

Compose中提供了获取dp的办法,也把Dp作为参数传递,所以有些当地dp和px的运用简略混杂。

3、Modifier.fillMaxSize

Modifier.fillMaxSize为占满父布局,占满父布局宽高度还有fillMaxWidthfillMaxHeight

4、Modifier.border、Modifier.padding

border用来为被润饰组件增加边框。边框能够指定色彩、粗细,以及经过Shape指定形状,比方圆角矩形等。padding用来为被润饰组件增加间隙。

@Composable
fun ShowImage() {
    val avatarSize = 200.dp  //头像的尺寸
    Box(
        modifier = Modifier
            .border(
            2.dp,     //边框宽度
            Color.Blue,   //边框色彩
            shape = RoundedCornerShape(avatarSize / 2))  //边框圆角,也能够给50表明50%
    ) {
        Image(
            painter = painterResource(id = R.mipmap.rabit2),
            contentDescription = null,
            modifier = Modifier
                .size(avatarSize)
                .padding(2.dp)     //向外加一个padding的宽度,不遮挡头像
                .clip(CircleShape)
        )
    }
}

UI效果

Jetpack Compose(二)-Modifier修饰符

留意padding()办法不是覆盖联系,而是叠加联系,看下面的简略示例:

//用手机运转,试试修改下面的数字就能在手机上直接预览,而不用从头运转
Box(
    modifier = Modifier
        .background(Color.Black)
        .padding(50.dp)
        .border(10.dp, Color.Blue, shape = RoundedCornerShape(10.dp))
        .padding(50.dp)
) {}

UI效果

Jetpack Compose(二)-Modifier修饰符

同时要留意padding对background的影响。

相对于传统布局有Margin和Padding之分,Compose中只要padding这一种润饰符,概念愈加简洁。

5、Modifier.offset

组件偏移

Box(
    modifier = Modifier
        .size(150.dp)
        .background(Color.Black)   //布景黑色
        .offset(15.dp,25.dp)    //默许在原点,这儿设置一定的偏移量
        .background(Color.Green)   //布景绿色
) {}

UI效果

Jetpack Compose(二)-Modifier修饰符

Modifier调用次序会影响最终UI出现的效果,先运用background设置布景,再运用offset润饰符偏移,再运用background制作布景,会发现出现的成果或许跟咱们想象中不一样。

6、Modifier.align

设置组件在父容器中的对齐办法。

Column(
    modifier = Modifier
        .background(Color.Green)
        .fillMaxWidth()
        .height(300.dp),
    verticalArrangement = Arrangement.Center    //父容器指定子组件的对齐办法为垂直方向上居中
) {
    Text(
        text = "Hello Android",
        style = TextStyle(fontSize = 18.sp, color = Color.Black),
        modifier = Modifier.align(Alignment.CenterHorizontally)  //子组件设置在父容器中的对齐办法为水平居中
    )
}

Tips: Compose中没有相似TextView的gravity="center"的特点,假如想让文本居中只能包一层。

Box(
    modifier = Modifier.size(200.dp),
    contentAlignment = Alignment.Center
) {
    Text(text = "Hello Android")
}

7、Modifier.aspectRatio

用于设置组件的宽高比。做图片适配就很简略,比方咱们想让图片宽度占满屏幕的宽度,宽高比为1:1,那么就有了如下的代码:

Image(
    painter = painterResource(id = R.mipmap.rabit2),
    contentDescription = null,
    modifier = Modifier
        .fillMaxWidth()    //宽度为屏幕宽度
        .aspectRatio(1 / 1F)   //宽高比1:1
)

UI效果

Jetpack Compose(二)-Modifier修饰符

8、Modifier.clickable

组件的点击事情

Box(
    modifier = Modifier.clickable {
        //点击事情
    }
) { }

9、Modifier.clip

用于指定对组件进行取舍的形状。Modifier.clip 承受一个 Shape 参数,表明要取舍的形状。Compose 提供了多种内置的 Shape 类型,如 RoundedCornerShape(圆角矩形)CutCornerShape(裁剪角矩形)CircleShape(圆形)等。

Modifier
    .clip(RoundedCornerShape(15.dp))   //圆角
    .clip(RoundedCornerShape(50))   //圆形,50为百分百
    .clip(CircleShape)   //上面的方便变量
    .clip(CutCornerShape(topStart = 5.dp, topEnd = 5.dp, bottomStart = 2.dp, bottomEnd = 2.dp))  //指定每个角切开圆角

10、Modifier.focusRequester

用于在可交互的组件上恳求焦点。一般,这个润饰符用于处理键盘焦点和键盘输入的交互。能够经过创立一个 FocusRequester 目标来运用 Modifier.focusRequester,然后将其传递给需求恳求焦点的组件上。

下面是一个有点复杂的比如,进入页面TextField(相当于View中的EditText)显现光标并弹出软键盘,点击按钮,去掉光标并躲藏软键盘。代码如下:

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun Test() {
    //输入框输入的内容
    var text by remember { mutableStateOf("") }
    //切换焦点
    val focusRequester = remember { FocusRequester() }
    //焦点办理
    val focusManager = LocalFocusManager.current
    //键盘控制器
    val keyboardController = LocalSoftwareKeyboardController.current
    Column(modifier = Modifier.fillMaxSize()) {
        //TextField:相当于View中的EditText
        TextField(
            value = text,
            onValueChange = { text = it },
            modifier = Modifier
                .focusRequester(focusRequester) //获取焦点
                .fillMaxWidth(),
            keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),
            keyboardActions = KeyboardActions(
                onDone = {
                    //点击键盘上承认按钮时躲藏键盘
                    keyboardController?.hide()
                }
            ),
            label = { Text(text = "进入页面获取焦点并弹出软键盘") }
        )
        //按钮
        Button(
            onClick = {
                //铲除光标
                focusManager.clearFocus()
                //躲藏软键盘
                keyboardController?.hide() //键盘上点击承认按钮时躲藏键盘
            }, modifier = Modifier.padding(16.dp)
        ) {
            Text("点击按钮铲除焦点并躲藏软键盘")
        }
        //界面重组异步回调
        LaunchedEffect(key1 = Unit) {
            focusRequester.requestFocus() //初次进入和重组页面恳求焦点
            keyboardController?.show() //初次进入页面弹出键盘,留意必须先获取焦点才干弹出键盘成功
        }
    }
}

UI效果如下

Jetpack Compose(二)-Modifier修饰符

11、Modifier.indication

用于指定组件(Clickable, Focusable, SemanticsPropertyProvider 等)的交互指示器。

交互指示器是一个视觉效果,当用户与组件进行交互(点击、获取焦点等)时,能够显现在组件周围,以提供反应和视觉指示。

看下面的一个比如:

Box(
    modifier = Modifier
        .fillMaxSize()
        .background(Color.Blue)
        .clickable {  }
        .indication(
            interactionSource = remember { MutableInteractionSource() }, //创立一个交互源,用于盯梢用户与组件的交互
            indication = rememberRipple()   //用于界说组件的交互指示器效果
        )
) {}

rememberRipple()为系统提供的波纹指示器。点一下有水波纹效果,这个大家应该不生疏。

12、Modifier.layout

Modifier.layout 是一个很强壮的 Compose API,它能够以更精确的办法控制布局和方位。经过 Modifier.layout,能够指定组件的方位和巨细,以及在父容器中的摆放办法。

Modifier.layout 承受一个 lambda 表达式,该表达式包含二个参数,其中measurable包含了组件的测量信息。经过 Measurable,能够获取组件的实践巨细并设置新的布局束缚。

val screenWidthDp = LocalConfiguration.current.screenWidthDp.dp   //屏幕宽度的dp值
val screenHeightDp = LocalConfiguration.current.screenHeightDp.dp  //屏幕高度的dp值
Box(
    modifier = Modifier
        .size(screenWidthDp / 2, screenHeightDp / 4)
        .background(Color.LightGray)
) {
    Text(
        text = "Hello Android!",
        style = TextStyle(fontSize = 18.sp, color = Color.White),
        modifier = Modifier
            .layout { measurable, constraints ->
                // 获取组件的实践巨细
                val placeable = measurable.measure(constraints)
                // 设置新的布局束缚
                layout(placeable.width, placeable.height) {
                    //指定组件在父布局中的方位x,y坐标
                    placeable.placeRelative(100, 100)
                }
            }
            .background(Color.Blue)
    )
}

UI效果

Jetpack Compose(二)-Modifier修饰符

13、Modifier.draggable

单方向上的拖动手势。Google官网有个横向拖拽的比如(网址)

var offsetX by remember { mutableStateOf(0f) }
Text(
    modifier = Modifier
        .fillMaxSize()
        .offset { IntOffset(offsetX.roundToInt(), 0) }
        .draggable(
            orientation = Orientation.Horizontal,    //横向
            state = rememberDraggableState { delta ->
                offsetX += delta   //一向重置偏移量的值
            }
        ),
    text = "Drag me!"
)

14、Modifier.pointerInput

用于监听组件的输入性事情,啥叫输入性事情,就是组件监听到的本身单击,双击,长按,拖拽等事情。detectTapGestures监听点击类事情,detectDragGestures监听拖拽类事情。

var tapped by remember { mutableStateOf(false) }
Box(
    modifier = Modifier
        .size(200.dp)
        .background(if (tapped) Color.Blue else Color.Green)
        .pointerInput(Unit) {
            //监测接触事情
            detectTapGestures(onTap = { offset -> //接触改变布景色
                tapped = !tapped
            })
            //监测拖拽事情
            detectDragGestures { pointerInputChange, offset ->
            }
        }
)

接触前

Jetpack Compose(二)-Modifier修饰符

接触后

Jetpack Compose(二)-Modifier修饰符

detectTapGesturesdetectDragGestures的Api如下:

//按压事情
suspend fun PointerInputScope.detectTapGestures(
    onDoubleTap: ((Offset) -> Unit)? = null,    //双击
    onLongPress: ((Offset) -> Unit)? = null,    //长按
    onPress: suspend PressGestureScope.(Offset) -> Unit = NoPressGesture,    //按压
    onTap: ((Offset) -> Unit)? = null     //单击 
) = coroutineScope {...}
//拖拽事情
suspend fun PointerInputScope.detectDragGestures(
    onDragStart: (Offset) -> Unit = { },   //开始拖拽
    onDragEnd: () -> Unit = { },     //结束拖拽
    onDragCancel: () -> Unit = { },    //撤销拖拽
    onDrag: (change: PointerInputChange, dragAmount: Offset) -> Unit   //正在拖拽
) {...}

Google官网有个跟手拖拽的比如(网址)

Box(modifier = Modifier.fillMaxSize()) {
    var offsetX by remember { mutableStateOf(0f) }
    var offsetY by remember { mutableStateOf(0f) }
    Box(
        Modifier
            .offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
            .background(Color.Blue)
            .size(50.dp)
            .pointerInput(Unit) {
                detectDragGestures { change, dragAmount ->
                    change.consume()
                    offsetX += dragAmount.x   //修改偏移的x,y的量
                    offsetY += dragAmount.y
                }
            }
    )
}

Gif效果图

15、Modifier.swiping

可滑动润饰符允许拖动元素,当释放时,这些元素一般会朝着一个方向中界说的两个或多个锚点移动。一个常见的用法是完成一个“滑动到闭幕”模式。

官网上有一个比如,我在这个比如上修改了一点代码,让它能在手机屏幕的宽度上左右滑动,也是为了进一步理解参数。

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Greeting() {
    val screenWidthDp = LocalConfiguration.current.screenWidthDp.dp
    val squareSize = 48.dp
    val swipeableState = rememberSwipeableState(0)
    val screenWidthPx = with(LocalDensity.current) { screenWidthDp.toPx() }  //核算像素值
    val squareSizePx = with(LocalDensity.current) { squareSize.toPx() }   //核算像素值
    val anchors = mapOf(0f to 0, screenWidthPx - squareSizePx to 1)   //界说二个锚点,滑块的最左最右的x坐标点
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .swipeable(
                state = swipeableState,
                anchors = anchors,
                thresholds = { _, _ -> FractionalThreshold(0.3f) },   //0.3为阈值,意思是不超越这个阈值就回弹,超越就滑向界说的下一个锚点
                orientation = Orientation.Horizontal
            )
            .background(Color.LightGray)
    ) {
        Box(
            Modifier
                .offset { IntOffset(swipeableState.offset.value.roundToInt(), 0) }
                .size(squareSize)
                .background(Color.DarkGray)
        )
    }
}

看GIF格式的UI图

16、Modifier.requiredSize、Modifier.requiredHeight、Modifier.requiredWidth

required系列的Api表明必要的尺寸,中国风尺寸是强制性的,假如内容超越会被切断,不会自动适应扩展。

Box(
    modifier = Modifier
        .requiredHeight(38.dp)
        .fillMaxWidth()
        .background(Color.Green)
) {
    Text(text = "测试超出高度".repeat(20))
}

UI效果

Jetpack Compose(二)-Modifier修饰符

17、Modifier.rotate、Modifier.scale

rotate旋转,scale缩放

var rotationAngle by remember { mutableStateOf(0f) }
var scale by remember { mutableStateOf(1f) }
Column(
    horizontalAlignment = Alignment.CenterHorizontally,
    verticalArrangement = Arrangement.Center,
    modifier = Modifier
        .rotate(rotationAngle)   //旋转
        .scale(scale)           //缩放
        .clickable {       //点击按钮旋转45度,缩小1倍
            rotationAngle += 45f
            scale /= 2
        }
) {
    Image(
        painter = painterResource(id = R.mipmap.rabit2),
        contentDescription = null,
        modifier = Modifier
            .fillMaxWidth()
            .aspectRatio(1 / 1F)
    )
}

点击一次后的UI效果

Jetpack Compose(二)-Modifier修饰符

18、Modifier.shadow

用于为 Compose 中的组件增加阴影效果。

Column(
    modifier = Modifier
        .fillMaxSize()
        .padding(20.dp)
) {
    Box(
        modifier = Modifier
            .size(200.dp)
            .shadow(5.dp, shape = RoundedCornerShape(16.dp))   //阴影效果
            .background(Color.White),    //需求增加布景,不增加布景阴影效果会有问题
        contentAlignment = Alignment.Center
    ) {
        Text(
            "Hello Android!",
            style = TextStyle(fontSize = 16.sp, color = Color.Black)
        )
    }
}

增加布景的UI效果(图一)和不增加布景的UI效果(图二)

Jetpack Compose(二)-Modifier修饰符

Jetpack Compose(二)-Modifier修饰符

19、Modifier.weight

子组件在父组件的权重

Row(
    modifier = Modifier
        .fillMaxWidth()
        .height(100.dp)
) {
    Box(
        modifier = Modifier
            .background(Color.Blue)
            .fillMaxHeight()
            .weight(2f)
    ) {}
    Box(
        modifier = Modifier
            .background(Color.Green)
            .fillMaxHeight()
            .weight(1f)
    ) {}
}

UI效果图

Jetpack Compose(二)-Modifier修饰符

20、Modifier.zIndex

设置组件的层级次序,较大值的组件会制作在较小值的组件之上。下面的代码假如没有设置zIndexButton2应该制作在Button1之上,可是因为设置了zIndexButton1制作在了Button2之上。

Box(
    modifier = Modifier.fillMaxWidth()
) {
    Button(
        onClick = { },
        modifier = Modifier
            .zIndex(2f)
    ) {
        Text("Button1")
    }
    Button(
        onClick = { },
        modifier = Modifier
            .zIndex(1f)
    ) {
        Text("Button2")
    }
}

UI效果

Jetpack Compose(二)-Modifier修饰符

三、效果域限制Modifier润饰符

Compose充分发挥了Kotlin的语法特性,让某些Modifier润饰符只能在特定效果域中运用,有利于类型安全地调用它们。所谓的“效果域”,在Kotlin中就是一个带有Receiver的代码块。例如Box组件参数中的conent就是一个Reciever类型为BoxScope的代码块,因此其子组件都处于BoxScope效果域中。

@Composable
inline fun Box(
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.TopStart,
    propagateMinConstraints: Boolean = false,
    content: @Composable BoxScope.() -> Unit   //BoxScope效果域
) {...}

需求留意Reciever类型默许能够跨层级拜访。例如下面的比如中,bScope{...}处于aScope{...}内部,能够在bScope{...}中拜访到属于aScope{...}的办法methodFromAScope()

aScope{
    bScope{
        methodFromAScope()   //aScope效果域的办法
    }
}

ComposeDSL中,一般只需求调用当时效果域的办法,像上面这样的Receiver跨级拜访会成为写代码时的“噪声”,加大犯错的概率。Compose考虑到了这个问题,能够经过@LayoutScopeMarker注解来躲避Receiver的跨级拜访。常用组件Receivier效果域类型均已运用@LayoutScopeMarker注解进行了声明。

//例如Column的效果域ColumnScope
Column() {...}
@LayoutScopeMarker     <------注解
@Immutable
@JvmDefaultWithCompatibility
interface ColumnScope {...}

四、Modifier完成原理浅析

Modifier会因为调用次序不同而产生出不同的Modifier链,Compose会依照Modifier链来次序完成页面测量布局与烘托。那么Modifier链是怎么被构建并解析的呢?

从源码中咱们发现Modifier实践是一个接口。它有三个详细完成,分别是一个Modifier伴生目标,Modifier. Element以及CombinedModifier

@Suppress("ModifierFactoryExtensionFunction")
@Stable
@JvmDefaultWithCompatibility
interface Modifier {
    fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
    fun <R> foldOut(initial: R, operation: (Element, R) -> R): R
    fun any(predicate: (Element) -> Boolean): Boolean
    fun all(predicate: (Element) -> Boolean): Boolean
    infix fun then(other: Modifier): Modifier =
        if (other === Modifier) this else CombinedModifier(this, other)
    /**
     * A single element contained within a [Modifier] chain.
     */
    @JvmDefaultWithCompatibility
    interface Element : Modifier {...}    //Modifier. Element
    ...略...
    // The companion object implements `Modifier` so that it may be used as the start of a
    // modifier extension factory expression.   //Modifier伴生目标,调用的起点
    companion object : Modifier {
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial
        override fun any(predicate: (Element) -> Boolean): Boolean = false
        override fun all(predicate: (Element) -> Boolean): Boolean = true
        override infix fun then(other: Modifier): Modifier = other
        override fun toString() = "Modifier"
    }
}
class CombinedModifier(      //CombinedModifier
    internal val outer: Modifier,
    internal val inner: Modifier
) : Modifier {...}

Modifier伴生目标是咱们对Modifier润饰符进行链式调用的起点,即Modifier.xxx()中最初的那个ModifierCombinedModifier用于衔接Modifier链中的每个Modifier目标。Modifier. Element代表详细的润饰符。当咱们运用Modifier.xxx()时,其内部实践上会创立一个Modifier实例。以size为例,其内部会创立SizeModifier实例,并运用then进行衔接。

@Stable
fun Modifier.size(size: Dp) = this.then(
    //创立SizeModifier目标
    SizeModifier(...)
)
//衔接不同的Modifier
infix fun then(other: Modifier): Modifier =
    if (other === Modifier) this else CombinedModifier(this, other)

咱们创立的各种Modifier本质上都是一个Modifier. Element。像LayoutModifier这类直接承继自Modifier. Element的接口。

@JvmDefaultWithCompatibility
interface LayoutModifier : Modifier.Element {...}

其他的还有DrawModifierFocusEventModifierPointerInputModifier等等,正是经过它们的组合形成了咱们的UI界面要素。

Compose在制作UI时,会遍历Modifier链获取装备信息。Compose运用foldOut()foldIn()遍历Modifier链,链上的一切节点被“折叠”成一个成果后,传入视图树用于烘托。

fun <R> foldIn(initial: R, operation: (R, Element) -> R): R
fun <R> foldOut(initial: R, operation: (Element, R) -> R): R

foldInfoldOut的办法相同:initial是折叠核算的初始值,operation是详细核算办法。Element参数表明当时遍历到的Modifier,返回值也是R类型,表明本轮核算的成果,会作为下一轮R类型参数传入。folInfoldOut的遍历次序有所不同,foldIn()代表从正向遍历,而foldOut是反向遍历。

学习笔记

作为初学者,不免有疏漏或错误,欢迎批评指正。文中部分内容参考了以下资料:
Jetpack Compose博物馆

实体书 Jetpack Compose从入门到实战