当前位置:   article > 正文

Jetpack Compose实战教程(四)

Jetpack Compose实战教程(四)

Jetpack Compose实战教程(四)

第四章 如何在Compose UI中使用ConstraintLayout



一、前言

hello,各位看官,距离上次更新compose ui教程的时间已经过去了一年,是的,整整一年。主要还是工作的原因抽不出太多时间来写教程,同时因为工作的原因,也还是优先使用自己熟练的命令式UI来进行编码,所谓实践出真知,那么没有实际使用,也就不会来更新教程。

大家在使用命令式UI的时候,就有这种需求,当一个页面布局足够复杂的时候,使用常规的LinearLayout 或RelativeLayout等布局很难实现需求,或者就是需要嵌套很多层,而因为命令式UI的设计问题,当嵌套层级过多的时候,就会引发绘制效率低下的问题,此时ConstraintLayout的出现,就能满足我们实现复杂布局而不用过多嵌套的问题。

前几章我们介绍了官方自带的compose库中,所自带的Row、Column、Box布局,因为compose ui的特性,即使再多的嵌套,也不会引发绘制效率低下的问题,但如果我们有一个复杂的布局,仍需要嵌套多层,此时是不利于代码可读性的,考虑到这点,官方还是研发了ConstraintLayout,截止本文发布,目前可用的稳定版本仍然还是22年5月20日发布的1.0.1版本,最新的alpha版本是1.1.0-alpha13,但已经有大半年都没更新了,原本想等着1.1.0版本发布,看是否有新特性再来介绍的,看来一时半会等不到了

二、本章目标

了解如何在compose ui中使用约束布局,以及常见问题

友情提醒,如果各位看官有不懂的代码可以先看一下之前的章节,循序渐进,如果还是有不懂的,可以给我留言

三、开始编码

3.1 引入ConstraintLayout

compose版本的ConstraintLayout 和 命令式UI一样,都不在自带的android库中,需要额外引入,目前稳定版本是1.0.1,需要在gradle文件中引入:

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
  • 1

3.2 设置id

在命令式UI中,我们需要给在ConstraintLayout 中有相互作用的控件设置id,然后通过app:layout_constraint 来设置左上右下的具体位置,在compose中一样适用

BaseTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
            	ConstraintLayout{
            		val ivMask = createRef() //这里就是创建一个ID的意思,用于标记
            		Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
                                        Modifier
                                            .constrainAs(ivMask) { //这里应用上这个ID
                                            //下面这行对应xml中的app:layout_constraintTop_toTopOf="parent"
                                            //但很友好的,官方还将margin的参数也在这里一并传入了,可以点进去看一下
                                            //其实它有3个参数,第二个是margin,第三个是当布局隐藏时的margin
                                                top.linkTo(parent.top, 26.dp) 
                                                start.linkTo(parent.start)
                                                end.linkTo(parent.end)
                                            }
                                            .width(356.5.dp)
                                            .height(49.dp))
                      
                      //有了上面这个ID之后,接下来如果有布局需要根据上面的图片进行位置的调整,就好办了
                      Text(
                                        text = getString(R.string.start_device_hint),
                                        //当如果你这个控件不会被别的控件所依赖,就可以直接在这里写createRef()
                                        Modifier.constrainAs(createRef()) { 
                                        //等同于app:layout_constraintTop_toTopOf="@id/iv_mask"
                                            top.linkTo(ivMask.top)
                                            bottom.linkTo(ivMask.bottom)
                                            start.linkTo(ivMask.start)
                                            end.linkTo(ivMask.end)
                                        }, color = Color.White, fontSize = 14.sp, textAlign = TextAlign.Center
                                    )
            	}
            }
            }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

3.3设置多个ID

假设上文还有更多的布局,下一个布局也需要依赖Text的位置,那么就需要给Text也设置一个ID,当然你可以再多写一行代码,如:

val tv_text = createRef()
  • 1

然后将这个tv_text应用到上文的Text中去,但同时你也可以这么写:

val (ivMask,tv_text) = createRefs()
  • 1

这样你就可以创建多个ID,用逗号分隔即可

四、最常见的问题

请特别留意,由于compose ui中的ConstraintLayout仍处于发展阶段,在代码编译阶段,是有一些错误没法提醒给开发者,并且运行后出现报错也很难找到对应的代码的,比如:
如果你的代码如下:

BaseTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                ConstraintLayout{
                    val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
                    Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
                        Modifier
                            .constrainAs(ivMask) {
                                top.linkTo(parent.top, 26.dp)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }
                            .width(356.5.dp)
                            .height(49.dp))

                    Text(text = getString(R.string.start_device_hint),
                        Modifier.constrainAs(tv_text) {
                            top.linkTo(ivMask.top)
                            bottom.linkTo(ivMask.bottom)
                            start.linkTo(ivMask.start)
                            end.linkTo(ivMask.end)
                        }, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
                    )

                    ConstraintLayout{
                        Text(text = "测试内容1",
                            Modifier.constrainAs(tv_text2) { 
                                top.linkTo(tv_text.bottom)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }, color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
                        )

                        Text(text = "哈哈哈哈",
                            Modifier.constrainAs(tv_text3) {
                                top.linkTo(tv_text2.top)
                                start.linkTo(tv_text2.start)
                                end.linkTo(tv_text2.end)
                            }, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
                        )
                    }
                }

            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

代码编译不会报错,乍一看也没什么问题,但因为tv_text2 和 tv_text3 在内部作用域ConstraintLayout下,它们是无法访问到上层作用域的ID的,那么tv_text2设置的top.linkTo(tv_text.bottom) 会失效,就会默认从屏幕左上角开始绘制, 而设置的start.linkTo(parent.start) 和end.linkTo(parent.end) 则因为父布局ConstraintLayout 未设置宽度,则会默认使用有效宽度(等同于命令式UI的android:layout_width=“wrap_content”),如果想要文字居中,那么有两种办法,先看第一种:

BaseTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                ConstraintLayout{
                    val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
                    Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
                        Modifier
                            .constrainAs(ivMask) {
                                top.linkTo(parent.top, 26.dp)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }
                            .width(356.5.dp)
                            .height(49.dp))

                    Text(text = getString(R.string.start_device_hint),
                        Modifier.constrainAs(tv_text) {
                            top.linkTo(ivMask.top)
                            bottom.linkTo(ivMask.bottom)
                            start.linkTo(ivMask.start)
                            end.linkTo(ivMask.end)
                        }, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
                    )

                    ConstraintLayout{
                        Text(text = getString(R.string.start_device_hint),
                            Modifier.constrainAs(tv_text2) {
                                top.linkTo(tv_text.bottom)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }.fillMaxWidth(), color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
                            //上面新增了.fillMaxWidth(),将文字的宽度全屏,但请留意,只有这个是没法让文字居中的
                            //是因为最早我们就写了textAlign = TextAlign.Center,所以当Text的宽度是全屏时,就居中了
                        )

                        Text(text = "测试内容",
                            Modifier.constrainAs(tv_text3) {
                                top.linkTo(tv_text2.top)
                                start.linkTo(tv_text2.start)
                                end.linkTo(tv_text2.end)
                            }, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
                        )
                    }
                }

            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

如果上述代码中tv_text2只设置了fillMaxWidth(),而没有设置textAlign=TextAlign.Center,那么文字不会居中,但tv_text3则因为tv_text2的宽度是全屏,而设置的start.linkTo(tv_text2.start)和end.linkTo(tv_text2.end)让其文字居中

方案二:

BaseTheme {
            Surface(
                modifier = Modifier.fillMaxSize(),
                color = MaterialTheme.colorScheme.background
            ) {
                ConstraintLayout{
                    val (ivMask,tv_text,tv_text2,tv_text3) = createRefs()
                    Image(painter = painterResource(id = R.mipmap.ic_waiting_device_mask), contentDescription = null,
                        Modifier
                            .constrainAs(ivMask) {
                                top.linkTo(parent.top, 26.dp)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }
                            .width(356.5.dp)
                            .height(49.dp))

                    Text(text = getString(R.string.start_device_hint),
                        Modifier.constrainAs(tv_text) {
                            top.linkTo(ivMask.top)
                            bottom.linkTo(ivMask.bottom)
                            start.linkTo(ivMask.start)
                            end.linkTo(ivMask.end)
                        }, color = Color.Blue, fontSize = 14.sp, textAlign = TextAlign.Center
                    )

 					//让父布局的宽度全屏,那么下面的Text因为设置的start.linkTo(parent.start) 和
 					//end.linkTo(parent.end) 从而居中显示
                    ConstraintLayout(Modifier.fillMaxWidth()){
                        Text(text = getString(R.string.start_device_hint),
                            Modifier.constrainAs(tv_text2) {
                                top.linkTo(tv_text.bottom)
                                start.linkTo(parent.start)
                                end.linkTo(parent.end)
                            }, color = Color.Black, fontSize = 14.sp, textAlign = TextAlign.Center
                        )

                        Text(text = "测试内容",
                            Modifier.constrainAs(tv_text3) {
                                top.linkTo(tv_text2.top)
                                start.linkTo(tv_text2.start)
                                end.linkTo(tv_text2.end)
                            }, color = Color.Red, fontSize = 14.sp, textAlign = TextAlign.Center
                        )
                    }
                }

            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/917617
推荐阅读
相关标签
  

闽ICP备14008679号