Compose 实现微信首页下拉小程序交互效果

国际惯例先上实现效果图,仿真度百分之99。

微信每个人都使用,首页的交互过程大家应该都很清楚,就不细说了。

我们直接来拆分需求:

  1. 手指下拉过程一个大球逐渐变成三个小球
  2. 滑动一定距离小程序界面逐渐显示出来,继续下拉进入小程序界面
  3. 小程序界面上拉超过一定距离可以回到首页

不要慌我们一个一个来实现,还是很简单的,有手就行。

下拉过程中三个小球变化效果实现

下拉过程中仔细观察小球变化,我们可以得到以下信息,小球的大小从0 逐渐变化到最大,如果继续下拉,大球会生出两个小球,然后变小,直至与小球大小一样,继续下拉三个小球同时隐藏。

先定义几个变量

ini 复制代码
                        val maxRadius = remember { 15f }
                        val minRadius = remember { 10f }
                        val startDistance = remember { 0f }
                        val endDistance = remember { 60f }
                        val openMiniThreshold = remember { 0.25f }

以上变量分别表示大球和小球的半径和小球的位移距离,因为大球和小球其实差别不是很大,所以相差5个px就可以了。

我们用Canvas 来绘制小球。

scss 复制代码
                        Canvas(
                            modifier = Modifier
                                .fillMaxWidth()
                                .height(height)
                                .background(Color(0xffededed))
                        ) {
                            //绘制大球
                            drawCircle(
                                Color.Gray,
                                if (scrollPercent <= openMiniThreshold / 3) lerp(
                                    0f,
                                    maxRadius,
                                    fraction = scrollPercent / (openMiniThreshold / 3)
                                ) else lerp(
                                    maxRadius,
                                    minRadius,
                                    fraction = (scrollPercent.minus(openMiniThreshold / 3)) / ((openMiniThreshold.minus(
                                        openMiniThreshold / 3
                                    )))
                                )
                            )
                            //绘制两个小球
                            if (scrollPercent > openMiniThreshold / 3) {
                                repeat(2) {
                                    translate(
                                        left = lerp(
                                            startDistance,
                                            endDistance,
                                            fraction = (scrollPercent.minus(openMiniThreshold / 3)) / ((openMiniThreshold.minus(
                                                openMiniThreshold / 3
                                            )))
                                        ).withSign((if (it == 0) 1 else -1))
                                    ) {
                                        drawCircle(Color.Gray, minRadius)
                                    }

                                }
                            }

                        }

仔细观察微信下拉我们可以看到其实滑动到屏幕大约四分之一小球就消失了,打开小程序界面的阈值取0.25就差不多了,当到达阈值三分之一处大球就变化到最大了,继续下滑大球变小,小球从大球生出来,小球的移动直接用translate移动滑动画布就行了。

下拉过程显示小程序界面

小球消失后,小程序界面显示出来,这时候小程序界面是缩小的,并且上面有一层和topbar颜色一样的蒙层。这个蒙层透明度的变化我们可以看到大概是从1变化到0。

蒙层我们直接在小程序界面用drawWithContent 来实现

scss 复制代码
                            .drawWithContent {
                                drawContent()
                                drawRect(
                                    size = size,
                                    color = Color(0xffededed).copy(
                                        alpha = lerp(
                                            1f,
                                            0f,
                                            scrollPercent
                                                .minus(openMiniThreshold)
                                                .coerceAtLeast(0f) / (1 - openMiniThreshold).coerceAtLeast(
                                                0.1f
                                            )
                                        )
                                    )
                                )
                            }

小程序界面的缩放在graphicsLayer中操作就行了,缩放的最小值大概为0.8

ini 复制代码
   LazyColumn(modifier = Modifier
            .graphicsLayer {
                scaleX = scrollPercent.coerceAtLeast(0.8f)
                scaleY = scrollPercent.coerceAtLeast(0.8f)
                translationY = offsetY
            }, content = {})

因为首页和小程序界面其实都是用LazyColumn 实现,LazyColumn 用scrollable 来实现滚动的,scrollable支持嵌套滚动,所以我们直接用嵌套滚动NestedScrollConnection来实现这个下拉过程。

kotlin 复制代码
                            override fun onPreScroll(
                                available: Offset,
                                source: NestedScrollSource
                            ): Offset {
                                if (abs(offsetY) <= visibilityThreshold) {
                                    return Offset.Zero
                                }
                                if (source == NestedScrollSource.Drag) {
                                    if (flingAnimator.isRunning) {
                                        scope.launch {
                                            flingAnimator.stop()
                                        }
                                    }
                                    var consumedY = 0f
                                    if (offsetY + available.y < 0) {
                                        consumedY = offsetY + available.y
                                        offsetY = 0f
                                    } else {
                                        offsetY += available.y
                                        consumedY += available.y
                                    }
                                    return Offset(0f, consumedY)
                                }
                                return Offset.Zero

                            }

                            override fun onPostScroll(
                                consumed: Offset,
                                available: Offset,
                                source: NestedScrollSource
                            ): Offset {
                                if (source == NestedScrollSource.Drag) {
                                    if (available != Offset.Zero) {
                                        offsetY = (available.y + offsetY).coerceIn(
                                            0f,
                                            500f
                                        )
                                    }
                                    return Offset(0f, available.y)
                                }
                                return Offset.Zero
                            }

                            override suspend fun onPreFling(available: Velocity): Velocity {
                                if (offsetY != 0f) {
                                    if (flingAnimator.isRunning) {
                                        flingAnimator.stop()
                                    }
                                    flingAnimator = Animatable(offsetY)
                                    flingAnimator.animateTo(
                                        if (scrollPercent > openMiniThreshold) constraints.maxHeight.toFloat() else 0f,
                                        springSpec
                                    ) {
                                        offsetY = value
                                    }.endState.velocity
                                    return Velocity(0f, available.y)
                                }

                                return Velocity(0f, 0f)
                            }

上面代码其实就是一些消费距离的计算和动画的处理,compose 的动画api设计还是很好的,用Animatable 来实现就好了。手指离开屏幕会触发Fling ,然后又会由fling触发一些列的onPreScroll 和 onPostScroll,此时的source = fling,所以我们需要注意的是onPreScroll 和 onPostScroll这里只消费NestedScrollSource.Drag就行了。另外需要注意的是这里滑动的距离都是用像素来代替的,真正做需求就需要用dp了。

小程序界面的上拉也是用嵌套滚动来实现的,代码和原理都大致相同就不贴了。

微信聊天界和小程序界面怎么用compose实现,请点这Jetpack Compose 实战之仿微信UI -实现首页小程序入口(六) - 掘金

相关推荐
深海呐3 小时前
Android AlertDialog圆角背景不生效的问题
android
ljl_jiaLiang3 小时前
android10 系统定制:增加应用使用数据埋点,应用使用时长统计
android·系统定制
花花鱼3 小时前
android 删除系统原有的debug.keystore,系统运行的时候,重新生成新的debug.keystore,来完成App的运行。
android
落落落sss4 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
消失的旧时光-19436 小时前
kotlin的密封类
android·开发语言·kotlin
服装学院的IT男8 小时前
【Android 13源码分析】WindowContainer窗口层级-4-Layer树
android
CCTV果冻爽9 小时前
Android 源码集成可卸载 APP
android
码农明明9 小时前
Android源码分析:从源头分析View事件的传递
android·操作系统·源码阅读
秋月霜风10 小时前
mariadb主从配置步骤
android·adb·mariadb
Python私教11 小时前
Python ORM 框架 SQLModel 快速入门教程
android·java·python