持续创作,加速成长!这是我参与「日新计划 6 月更文挑战」的第2天,点击查看活动详情

效果

废话不说,先上图!

所对应代码大致为:

val physicsConfig = PhysicsConfig()
PhysicsLayout(modifier = modifier, physicsLayoutState = physicsLayoutState, boundSize = boundSize.toFloat()) {
    RandomColorBox(modifier = Modifier
        .size(40.dp)
        .physics(physicsConfig, initialX = 300f, initialY = 500f))
    // This one has a circle shape
    // so you need to modify it with not only a `clip()` Modifier to make it "looks like" a circle
    // but also a `physics(physicsConfig.copy(shape = PhysicsShape.CIRCLE)` Modifier to create a circle Body
    RandomColorBox(modifier = Modifier
        .clip(CircleShape)
        .size(50.dp)
        .physics(physicsConfig.copy(shape = PhysicsShape.CIRCLE), 300f, 1000f))
    RandomColorBox(modifier = Modifier
        .size(60.dp)
        .physics(physicsConfig))
    var checked by remember {
        mutableStateOf(false)
    }
    Checkbox(...)
    Card(...) {
        ...
    }
}

这之中的PhysicsLayout就是我实现的物理布局

如需体验android下载安装上图的其他功能,可以到Github仓库下载demo;完整源码亦可见仓库

这是怎么实现的?

正如标题所言,这是一个自定义布局。关于这方面,我软件工程专业已经写了5篇文章详细描述了,感兴趣的同学可以点击我的头像查看。本android的drawable类文所用到的无非就是那里的初始化电脑知识加上JBox2d而已,看完之后你也写的出来

JBox2d

JBox2D是开源的2D物理引擎,能够根据开发人员初始化电脑时出现问题未进行更改设定的参数,如重力、密度、摩擦系数和弹性系数等,自动进行2D刚体物理运动的模拟。

参考

本布局参考自Jawnnypoo运动/PhysicsLayout: Andro初始化电脑时出现问题id layout that simulates physics using JBox2D (giandroid/harmonyosthub.com),源码其中部分代码也来自于那里,在此表示诚挚的感谢!(源码编辑器 不过我进行了大量的修改,以使原先用于 View的代码被用于Compose

实现

android是什么手机牌子

首先,我们先来想一个事情源码编辑器下载:现在每个物体其初始化失败是怎么解决实都有自己的位置、大小、形状这些参数,那么父布局怎么获得这些值呢?如果你读过我的深入Jetpack Compose——布局原理与自定义布局(四)ParentData,估计可以想到:这是利用ParentData传递的。所以咱们先写个自定义的ParentData

class PhysicsParentData(
    var physicsConfig: PhysicsConfig = PhysicsConfig(),
    var initialX: Float = 0f,
    var initialY: Float = 0f,
    var width: Int = 0,
    var height: Int = 0
)

PhysicsConfig代表基本的物理配置,我们先不细究,其android平板电脑价格余的就是初位置和宽高了

有了Parent初始化电脑Dataandroid是什么系统那是不是也得有对应的修饰符和作用域软件工程啊,所以android下载咱们写一写

interface PhysicsLayoutScope {
    @Stable
    fun Modifier.physics(physicsConfig: PhysicsConfig, initialX : Float = 0f, initialY : Float = 0f) : Modifier
}
internal object PhysicsLayoutScopeInstance : PhysicsLayoutScope {
    @Stable
    override fun Modifier.physics(
        physicsConfig: PhysicsConfig,
        initialX: Float,
        initialY: Float
    ): Modifier = this.then(PhysicsParentData(physicsConfig, initialX, initialY))
}

上面的代码都很简单,属于是自定义Modifier的基本操初始化电脑时出现问题未进行更改作了,如果你看不懂可以先了解了解再来

使用

现在Modifier的定义差不多了,接下来就是使用了。其实总结下软件工程师来就是这个过程

  1. 初始化各个软件技术专业物体源码交易平台和世界

  2. 用代码不断初始化电脑时出现问题未进行更改模拟一下各个物体的运动过程

  3. 在Layout过程中获取位置并正确摆放出来

咱们分别来看*(下面的内容只是我的思路,有些地方可能不太优雅,如果您有更好的想法欢迎android平板电脑价格指出!)*

整体来说,应该有一些代码专门负责物理模拟的过程,这一部分在代码中为Physics类,软件技术专业它负责进行具体的物理世界创造进行物理模拟等过程。此处不赘述。

初始软件商店下载

考虑到各子微件的具体信息要到Layout才能读取到,所以似乎只能在这里初始化;但是Layout又会反复进行,而初始化应该只进行一次。所以用个变量软件商店来控制吧

var initialized by remember {
    mutableStateOf(false)
}

然后第一次Layout时读取各ParentData并存起来

val placeables = measurables.mapIndexed { index,  measurable ->
    val physicsParentData = (measurable.parentData as? PhysicsParentData) ?: PhysicsParentData()
    if (!initialized){
        parentDataList.add(index, physicsParentData)
    }
    measurable.measure(childConstraints)
}

然后运动会作文开个副作用,在所有物体信息初始化好后创建世界并创建Body(在JBox2d中代表刚体的类)

// 初始化世界
LaunchedEffect(initialized){
    if (!initialized) return@LaunchedEffect
    physics.createWorld { body, i ->
        parentDataList[i].body = body
    }
}

其中createWord方法负责创建Body并在每个Body创建完后回调

不断模拟

模拟的工android是什么系统作交给JBox2d,我们要做的就是不断就行。所以wh运动ile循环吧

LaunchedEffect(key1 = Unit){
    while (true){
        delay(16)
        physics.step() // 模拟 16ms
    }
}
读取并正确放置

这个就很简单了,在Layout方法里layout()中读一下各个Body的位置并place就行

不过这里注意,因为Body有旋转角度,所以在place的时候需要使用placeWithLayer,该方法签名如下:

fun Placeable.placeWithLayer(
    position: IntOffset,
    zIndex: Float = 0f,
    layerBlock: GraphicsLayerScope.() -> Unit = DefaultLayerBlock
)

其中第三个参数layerBlock就提供了缩放源码编辑器下载、选择等方法。软件技术专业具体代码是:软件

layout(constraints.maxWidth, constraints.maxHeight){
    placeables.forEachIndexed { i, placeable: Placeable ->
        val x = physics.metersToPixels(parentDataList[i].x).toInt() - placeable.width / 2
        val y = physics.metersToPixels(parentDataList[i].y).toInt() - placeable.height / 2
        placeable.placeWithLayer(IntOffset(x,y), zIndex = 0f, layerBlock = {
             rotationZ = parentDataList[i].rotation
        })
    }
}

上面的metersToPixels用于将物理世源码时代界的坐标映射到现软件工程

完工!

后续

其实目前来看,代码里还有些地方感觉不大对劲,比如,为了触发Layout过程,我实际使用了一个并无任何用处的state。因为在我的尝试里,只要layout块里不出现state的变化,它就不会重新触发(这点当然符合Comp运动健康ose的感觉Android喽);我想不到什么好点子,只好这么处理了。如果大家有什运动的好处么好想法,欢迎探讨和PR

如果你好奇有什么用……额,我也不知道有什么实际用源码处。我就是觉得很好玩儿,很早之前就想做了,最近下定决心,android平板电脑价格两天完成,感觉效果还不错。

如果你对Compose完整项目源码之家感兴趣,欢迎看看我的开源项目Fu源码编辑器nnySaltyFish/FunnyTranslatio软件商店n: 基于Jetpack Compose开发的翻译软件软件测试支持多引擎、插件化~

本文代码:FunnySaltyFish/JetpackComposePhysicsLayout: Jetpack Compose custom layout that simulat软件商店下载es ph源码之家ysics using JBox2D ,欢迎Star运动