Kuikly基础之音频播放与资源管理:青蛙叫声实现

在上一篇文章中,我们成功地让青蛙对我们的点击做出了视觉上的响应------一个生动的缩放动画。现在,我们的应用不再是静态的了,但似乎还缺点什么。

没错,就是声音!交互不仅仅是视觉上的,听觉上的反馈同样重要。如果每次点击青蛙,它都能发出一声清脆的"呱",那应用的趣味性和沉浸感无疑会更上一层楼。

今天,我们就来完成这个任务:为我们的"单身青蛙"应用加上音效,让它在被点击时,能真实地"叫"出来。同时,我们也会探讨在Kuikly中如何进行基础的音频资源管理。

初步计划

在动手之前,我们自然是先去翻了翻Kuikly的文档,想找找有没有现成的音频播放组件,比如一个叫<Audio>的家伙。结果发现,似乎并没有这样一个专门为播放短音效而生的轮子。

不过,没有"直路",我们可以"绕路"走嘛。Kuikly提供了一个功能强大的<Video>组件。既然视频能包含声音,我们是不是可以利用它来只播放声音呢?这个想法听起来不错。

我们的计划是:准备一个音频文件(就是我们之前准备好的frog_sound.mp3),然后把它"伪装"成一个视频源交给<Video>组件。为了不让它在界面上"碍眼",我们再把它设置成一个1x1像素的、完全透明的"隐形"播放器。

实战

好了,理论有了,开干!

我们最初的想法很简单直接:既然拿到了<Video>组件的引用(ViewRef),那在点击事件里直接调用它的play()方法不就行了?就像这样:

kotlin 复制代码
// 理想中的代码...
ctx.soundPlayerRef.view?.play() 

然而,现实很快给了我们一拳,编译器无情地报错:Unresolved reference: play。好吧,看来此路不通。这种命令式的、直接操作视图的方式,似乎并不符合Kuikly的"口味"。

这次碰壁让我们冷静下来,重新思考Kuikly的设计哲学------声明式UI。我们不应该去"命令"一个组件去播放,而应该"声明"它的状态是"正在播放"。

于是,新的方案诞生了:

  1. 定义一个observable的状态变量 soundPlayControl,用来控制播放行为(播放/暂停)。
  2. <Video>组件的playControl属性绑定到这个状态上。
  3. 当用户点击青蛙时,我们不再调用方法,而是去更新soundPlayControl的状态为VideoPlayControl.PLAY
  4. 监听<Video>playStateDidChanged事件,当播放结束(状态变为PlayState.PLAY_END)时,再将soundPlayControl的状态改回VideoPlayControl.PAUSE,为下一次播放做准备。

这个思路听起来就"地道"多了!它完美地融入了整个应用的响应式数据流。

最终代码

说干就干,我们立刻对FrogMainPage.kt进行了改造,最终的代码如下:

kotlin 复制代码
// ... 省略部分import ...
import com.tencent.kuikly.core.base.ViewRef
import com.tencent.kuikly.core.views.PlayState
import com.tencent.kuikly.core.views.Video
import com.tencent.kuikly.core.views.VideoPlayControl
import com.tencent.kuikly.core.views.VideoView

// ...

internal class FrogMainPage : BasePager() {

    // ... 省略其他状态变量 ...
    lateinit var soundPlayerRef: ViewRef<VideoView>
    // 新增一个控制音频播放的状态
    private var soundPlayControl: VideoPlayControl by observable(VideoPlayControl.PAUSE)

    override fun body(): ViewBuilder {
        val ctx = this
        return {
            // ... 省略大部分UI布局 ...
            
                        // 主青蛙按钮
                        View {
                            // ... 省略部分 attr ...
                            event {
                                 click {
                                     // ... 省略其他点击事件逻辑 ...
                                     
                                     // 更新状态,触发音频播放
                                     this@FrogMainPage.soundPlayControl = VideoPlayControl.PLAY
                                 }
                             }
                        }
            
            // ... 省略部分UI布局 ...

                // "隐形"的音频播放器
                Video {
                    ref {
                        ctx.soundPlayerRef = it
                    }
                    attr {
                        src("media/frog_sound.mp3")
                        width(1f)
                        height(1f)
                        opacity(0f)
                        // 将播放行为和我们的状态绑定
                        playControl(this@FrogMainPage.soundPlayControl)
                    }
                    event {
                        // 监听播放状态
                        playStateDidChanged { state, _ ->
                            // 播放结束后,重置状态以便下次播放
                            if (state == PlayState.PLAY_END) {
                                this@FrogMainPage.soundPlayControl = VideoPlayControl.PAUSE
                            }
                        }
                    }
                }
                
            // ... 省略底部功能区 ...
        }
    }
    
    // ... 省略生命周期方法 ...
}

代码解释:

  1. soundPlayControl状态 :我们新增了一个observable变量,它的类型是VideoPlayControl,默认值为PAUSE
  2. 状态更新触发播放 :在青蛙的click事件中,我们不再调用任何方法,而是简单地将soundPlayControl的值改为VideoPlayControl.PLAY
  3. 属性绑定<Video>组件的playControl属性直接绑定了soundPlayControl状态。当状态改变时,Kuikly框架会自动更新组件,从而控制视频(音频)的播放或暂停。
  4. 播放后重置状态 :我们监听playStateDidChanged事件。在探索中我们发现,当播放完成时,状态会变为PlayState.PLAY_END。此时,我们把soundPlayControl重新设置为PAUSE,这样就完成了一个播放循环,并且可以让青蛙被连续点击发声。

结语

现在,重新编译运行,再次点击那只青蛙。伴随着熟悉的缩放动画,一声清脆的"呱"终于响了起来!虽然中间走了点小弯路,但我们最终还是用一种更"Kuikly"的方式,让我们的应用"活"了起来。这次的经历也让我们对Kuikly的声明式思想有了更深的理解:少一些命令,多一些声明,代码会变得更优雅、更可预测。

相关推荐
一只大侠的侠29 分钟前
Flutter开源鸿蒙跨平台训练营 Day 5Flutter开发鸿蒙电商应用
flutter·开源·harmonyos
lxysbly1 小时前
md模拟器安卓版带金手指2026
android
不爱吃糖的程序媛1 小时前
Capacitor:跨平台Web原生应用开发利器,现已全面适配鸿蒙
前端·华为·harmonyos
儿歌八万首1 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节
一只大侠的侠2 小时前
Flutter开源鸿蒙跨平台训练营 Day6ArkUI框架实战
flutter·开源·harmonyos
一只大侠的侠3 小时前
Flutter开源鸿蒙跨平台训练营 Day 4实现流畅的下拉刷新与上拉加载效果
flutter·开源·harmonyos
早點睡3903 小时前
高级进阶 ReactNative for Harmony 项目鸿蒙化三方库集成实战:react-native-drag-sort
react native·react.js·harmonyos
果粒蹬i3 小时前
【HarmonyOS】DAY9:利用React Native开发底部 Tab 开发实战:从问题定位到最佳实践
华为·harmonyos
消失的旧时光-19434 小时前
从 Kotlin 到 Dart:为什么 sealed 是处理「多种返回结果」的最佳方式?
android·开发语言·flutter·架构·kotlin·sealed
Jinkxs4 小时前
Gradle - 与Groovy/Kotlin DSL对比 构建脚本语言选择指南
android·开发语言·kotlin