前言
在之前的文章中,我们通过传统View实现过环形菜单
但是本篇的重点就不是效果了,我们重点放在布局上。
简介
Compose为什么能快速开发UI,除了Kotlin语法糖等加持之外,其Modifier功能也非常强大,但是在开发的过程中,也会遇到让人比较难以理解的行为,比如其Modifier.layout有一定的局限性,无法获取到所有Child Node的相关信息。
本篇,我们这里会实现一种环形菜单,也会分析一下MeasurePolicy相关的用法和设计思想,也会简单介绍下官方的一些方法。
关于布局方式
相较于传统的View布局,Compose UI的布局和测量是一起的,传统的View是measure和layout存在一定的隔离,即所有的view都测量完成,才会进行真正的layout。但有时,需要进行强行关联,比如在实现Flow布局时,传统的ViewGroup需要做一些缓存信息来服务layout。而compose UI是边测量边布局,使得measure和layout隔离程度减少,显然应该有一定的其他方面的想法,具体是什么呢,继续往下看。
那么,如果想要在Compose实现布局怎么实现呢?
其实,Compose 官方给出了很多实现方式
扩展Modifier属性
这种方式,通过扩展Modifer属性实现布局,但是仅仅对Compose自身有用,对child Node无效。
1 | java复制代码fun Modifier.firstBaselineToTop( |
使用方式如下,下面是直接影响Text的布局
1 | java复制代码@Preview |
Layout扩展
下面是官方网站的一套代码,我们可以进行参考,这种方式可以约束到child Node,实际上本篇内容也可以使用这种方式实现,但是我们的主题是MeasurePolicy,因此就没用这种方式
1 | java复制代码@Composable |
MeasurePolicy
MeasurePolicy 字面意思是测量策略,在使用Compose时会作为参数传入Layout,但是如果将其理解为测量显然是不正确的,因为MeasurePolicy 不仅仅可以测量,还能实现布局,该方法名称还是有一定的误导性质的。
1 | java复制代码@UiComposable |
我们来看看MeasurePolicy在Box组件中的用法,下面代码中我添加了一些注释,方便理解
1 | java复制代码internal fun boxMeasurePolicy(alignment: Alignment, propagateMinConstraints: Boolean) = |
代码实现很复杂,但是为什么Compose UI种都往往会使用MeasurePolicy呢,主要原因是通过减少对Compose 组件的修改,实现更多的UI表现。这点理念其实很像recyclerView的LayoutManager。
当然,这个设计思想其实都是为了减少测量
下面是《Jetpack Compose 博物馆》的总结
composable 被调用时会将自身包含的UI元素添加到UI树中并在屏幕上被渲染出来。每个 UI 元素都有一个父元素,可能会包含零至多个子元素。每个元素都有一个相对其父元素的内部位置和尺寸。
每个元素都会被要求根据父元素的约束来进行自我测量(类似传统 View 中的 MeasureSpec ),约束中包含了父元素允许子元素的最大宽度与高度和最小宽度与高度,当父元素想要强制子元素宽高为固定值时,其对应的最大值与最小值就是相同的。
对于一些包含多个子元素的UI元素,需要测量每一个子元素从而确定当前UI元素自身的大小。并且在每个子元素自我测量后,当前UI元素可以根据其所需要的宽度与高度进行在自己内部进行放置
结合代码,我们从其中就能看出,MeasurePolicy是一个重要的环节
1 | java复制代码val measurePolicy = rememberBoxMeasurePolicy(contentAlignment, propagateMinConstraints) |
好了,以上是对Compose UI的一些理解,下面我们进入本篇的主题环节。
实现环形菜单
如何实现环形菜单呢?
本篇是使用MeasurePolicy去实现,但是这种往往需要我们自定义一个Compose组件,在Compose UI中,组件无法被继承,显然我们需要参考一些其他实现,这里我们选择使用Box的实现,将其代码复制为CircleBox类Compose组件。
我们这里将其核心的逻辑改在一下
1 | java复制代码internal fun boxMeasurePolicy(alignment: Alignment, propagateMinConstraints: Boolean) = |
通过以上代码就实现了环形布局
当然,使用起来也很简单,我们只需要将菜单Item加入到CircleBox中即可
1 | java复制代码class CircleMenuActivity : ComponentActivity() { |
总结
以上就是本篇的核心内容,在这篇文章中我们可以了解到MeasurePolicy的用法和设计思想。目前而言,Compose UI有很多超前的设计。有很多大家喜欢的轮子官方都给造好了,所以我们可以放更多精力在状态控制和ViewModel上,提升开发效率。
说到提升开发效率,google的程序员理论上和大家一样,都是面向老板编程,因此,尽早入局Compose UI或者Flutter显然是必要的。
遗留问题
本篇我们实现了环形菜单,但是我们没有实现菜单的旋转功能,主要是旋转部分依赖事件,需要将事件处理引入的结果传递给MeasurePolicy,这部分理论上也不难,大家可以自己实现哦。
本篇源码
本篇我们无法继承Box,而是复制了Box,对其进行了改写,主要代码如下
1 | java复制代码@Composable |
以上是完整的代码实现,下面demo地址
附:
Github源码
本文转载自: 掘金