Jetpack Compose 自定义布局+物理引擎 = ?
效果
废话不说,先上图!
所对应代码大致为:
1 | val physicsConfig = PhysicsConfig() |
这之中的PhysicsLayout
就是我实现的物理布局
了
如需体验上图的其他功能,可以到Github仓库下载demo;完整源亦可见仓库
这是怎么实现的?
正如标题所言,这是一个自定义布局。关于这方面,我已经写了5篇文章详细描述了,感兴趣的同学可以点击我的头像查看。本文所用到的无非就是那里的知识加上JBox2d
而已,看完之后你也写的出来
JBox2d
JBox2D是开源的2D物理引擎,能够根据开发人员设定的参数,如重力、密度、摩擦系数和弹性系数等,自动进行2D刚体物理运动的模拟。
参考
本布局参考自Jawnnypoo/PhysicsLayout: Android layout that simulates physics using JBox2D (github.com),其中部分代码也来自于那里,在此表示诚挚的感谢!( 不过我进行了大量的修改,以使原先用于 View
的代码被用于Compose
)
实现
定义
首先,我们先来想一个事情:现在每个物体其实都有自己的位置、大小、形状这些参数,那么父布局怎么获得这些值呢?如果你读过我的深入Jetpack Compose——布局原理与自定义布局(四)ParentData,估计可以想到:这是利用ParentData
传递的。所以咱们先写个自定义的ParentData
吧
1 | class PhysicsParentData( |
PhysicsConfig
代表基本的物理配置,我们先不细究,其余的就是初位置和宽高了。
有了ParentData
,那是不是也得有对应的修饰符和作用域啊,所以咱们写一写
1 | interface PhysicsLayoutScope { |
上面的代码都很简单,属于是自定义Modifier
的基本操作了,如果你看不懂可以先了解了解再来
使用
现在Modifier
的定义差不多了,接下来就是使用了。其实总结下来就是这个过程
初始化各个物体和世界
用代码不断模拟一下各个物体的运动过程
在Layout过程中获取位置并正确摆放出来
咱们分别来看(下面的内容只是我的思路,有些地方可能不太优雅,如果您有更好的想法欢迎指出!)
整体来说,应该有一些代码专门负责物理模拟的过程,这一部分在代码中为Physics
类,它负责进行具体的物理世界创造
、进行物理模拟
等过程。此处不赘述。
初始化
考虑到各子微件的具体信息要到Layout
才能读取到,所以似乎只能在这里初始化;但是Layout
又会反复进行,而初始化应该只进行一次。所以用个变量来控制吧
1 | var initialized by remember { |
然后第一次Layout
时读取各ParentData
并存起来
1 | val placeables = measurables.mapIndexed { index, measurable -> |
然后开个副作用,在所有物体信息初始化好后创建世界并创建Body(在JBox2d
中代表刚体
的类)
1 | // 初始化世界 |
其中createWord
方法负责创建Body
并在每个Body
创建完后回调
不断模拟
模拟的工作交给JBox2d
,我们要做的就是不断
就行。所以while
循环吧
1 | LaunchedEffect(key1 = Unit){ |
读取并正确放置
这个就很简单了,在Layout
方法里layout()
中读一下各个Body
的位置并place
就行
不过这里注意,因为Body
有旋转角度,所以在place
的时候需要使用placeWithLayer
,该方法签名如下:
1 | fun Placeable.placeWithLayer( |
其中第三个参数layerBlock
就提供了缩放、选择
等方法。具体代码是:
1 | layout(constraints.maxWidth, constraints.maxHeight){ |
上面的metersToPixels
用于将物理世界的坐标映射到现实
完工!
后续
其实目前来看,代码里还有些地方感觉不大对劲,比如,为了触发Layout
过程,我实际使用了一个并无任何用处的state
。因为在我的尝试里,只要layout
块里不出现state
的变化,它就不会重新触发(这点当然符合Compose的感觉喽);我想不到什么好点子,只好这么处理了。如果大家有什么好想法,欢迎探讨和PR
如果你好奇有什么用……额,我也不知道有什么实际用处。我就是觉得很好玩儿,很早之前就想做了,最近下定决心,两天完成,感觉效果还不错。
如果你对Compose完整项目感兴趣,欢迎看看我的开源项目FunnySaltyFish/FunnyTranslation: 基于Jetpack Compose开发的翻译软件,支持多引擎、插件化~
本文代码:FunnySaltyFish/JetpackComposePhysicsLayout: Jetpack Compose custom layout that simulates physics using JBox2D ,欢迎Star!