Jetpack Compose中的Layouts使用

一:简介

您将学习如何使用Compose的最高层次的UI抽象,Material Design,以及低级的可组合工具,如Layout,它允许您测量并在屏幕上放置元素。

二:Modifier

1:设置文本的透明度

CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
            Text("3 minutes ago", style = MaterialTheme.typography.body2)
        }

2:指明Surface等控件的大小

 modifier = Modifier.size(50.dp)

3:Modifiers 和 XML attribute的区别

Modifiers和XML attribute类似,但是Modifiers具有作用域的类型安全检查,一些无效的modifiers不能在一些layouts上使用

4:设置一个modifier在形参的一号位

If you're creating your own composable, consider having a modifier as a parameter, default it to Modifier (i.e. empty modifier that doesn't do anything) and apply it to the root composable of your function. In this case:

@Composable
fun PhotographerCard(modifier: Modifier = Modifier) {
    Row(modifier) { ... }
}

Note: By convention, the modifier is specified as the first optional parameter of a function. This enables you to specify a modifier on a composable without having to name all parameters.

5:Modifier的顺序有影响

看一个例子:

Surface (color = if (color_state.value == 1) Color.Green else Color.Blue){
            Column(modifier = Modifier
                .padding(5.dp)
                .clickable { color_state.value = -color_state.value }
                .align(Alignment.CenterVertically)) {

                ...
                }
            }
        }

column先在周围创建了空隙padding,再创建了点击事件clickable,这个时候clickable在padding之间是无效的。但是,clickable放在padding前面就可以把padding的范围也包括进去。

6:背景的圆角

想要把背景设置成圆角,看来有两种方案:

1:
Row(modifier = Modifier
        .padding(8.dp)
        .clip(RoundedCornerShape(4.dp))
        .background(MaterialTheme.colors.surface)
        .padding(16.dp)) {
...
}

2:
Row(modifier = Modifier
        .padding(8.dp)
        .background(MaterialTheme.colors.surface, Shapes.medium)
        .padding(16.dp)) {
...
}

第一种先裁剪,在设置背景;第二种设置一个裁剪后的背景。

三:Scaffold

scaffold是一个布局结构的模板,具有很多slot(插槽),可以放置自己的组件上去。比如说,topBar, bottomBar...

Scaffold(topBar = { MyBar() }, bottomBar = {...}) { padding ->
        BodyContent(Modifier.padding(padding))
    }

它的最后一个参数是一个lambda,自己定义放置什么内容,不过,它会传递一个padding给我们,而我们需要把这个padding应用到我们内容content的根modifier上:

@Composable
fun BodyContent(modifier: Modifier = Modifier) {
    Column(modifier = modifier) {
        Text(text = "Hi there!")
        Text(text = "Thanks for going through the Layouts codelab")
    }
}

四:TopAppBar

在上一个代码中,我们设置了scaffold的topBar为一个TopAppBar的实例:

TopAppBar(title = { Text(text = "Here Title") },
        actions = {
            IconButton(onClick = { Log.d("MainActivity", "i click the button") }) {
                Icon(imageVector = Icons.Filled.Favorite, contentDescription = null)
            }
        })

TopAppBar也是一个具有很多slot的composable function。

@Composable
fun TopAppBar(
    title: @Composable () -> Unit, 设置中间的文字
    modifier: Modifier = Modifier,
    navigationIcon: @Composable (() -> Unit)? = null,
    actions: @Composable RowScope.() -> Unit = {}, 
    backgroundColor: Color = MaterialTheme.colors.primarySurface,
    contentColor: Color = contentColorFor(backgroundColor),
    elevation: Dp = AppBarDefaults.TopAppBarElevation
) {
...
}

具体的parameter的含义查看文档可知。

(底部的导航可使用BottomNavigation)

bottomBar = {
        var selectedItem by remember { mutableStateOf(0) }
        val items = listOf("Songs", "Artists", "Playlists")
        BottomNavigation {
            items.forEachIndexed { index, item ->
                BottomNavigationItem(
                    icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
                    label = { Text(item) },
                    selected = selectedItem == index,
                    onClick = { selectedItem = index }
                )
            }
        }
    }

五:操作列表

可以使用Column或Row来轻松地展示list,但也有lazy的版本only composes and lays out the currently visible items.

普通的column无法scroll滚动:

//val scrollState = rememberScrollState()
    Column(/*modifier = Modifier.verticalScroll(scrollState)*/) {
        repeat(1000){
            Text("this is No $it")
        }
    }

只有加上被注释掉的那部分才可以滚动。

但是上面的代码也有缺点,就是屏幕外不可见的元素也会一次性加载出来,当item很多的时候,是一个性能隐患。这时候考虑使用LazyColumn。

note:LazyColumn in Jetpack Compose is the equivalent of RecyclerView in Android Views.

LazyColumn() {
        items(100){
            Text(text = "No $it")
        }
    }

用items表示多个项,用item表示一个项。有多个重载,可以接受list作为items的参数,这里是接受一个size。

六:展示图片

Image(
            painter = rememberImagePainter(
                data = "https://developer.android.com/images/brand/Android_Robot.png"
            ),
            contentDescription = "Android Logo",
            modifier = Modifier.size(50.dp)
        )

rememberImagePainter是Coil包中一个很好用的展示图片的函数。

这是这个项目的github主页:https://github.com/coil-kt/coil

 七:创建自定义布局 custom layouts:

  1. 在view的体系中, 创建custom layout需要继承ViewGroup, 在Compose中只需要使用Layout函数。

八:测量控件

对于一个可组合对象,你可以要求它的intrinsicWidthintrinsicHeight:

  • (min|max)IntrinsicWidth:给定这个高度,你可以正确绘制内容的最小/最大宽度是多少
  • (min|max)IntrinsicHeight:给定这个宽度,你可以正确地绘制内容的最小/最大高度是多少
@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier.height(IntrinsicSize.Min)) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )

        Divider(color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp))
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),
            text = text2
        )
    }
}

@Preview
@Composable
fun TwoTextsPreview() {
    LayoutsCodelabTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

Row的minIntrinsicHeight将是其子行的最大minIntrinsicHeight。Divider的minIntrinsicHeight为0,因为如果没有给定约束,它不会占用空间;Text的minIntrinsicHeight将是给定特定宽度的文本的高度。因此,Row的高度约束将是text的最大minIntrinsicHeight。然后,Divider将其高度扩展到由Row给出的高度约束。

猜你喜欢

转载自blog.csdn.net/qq_39312146/article/details/130663867