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的声明式思想有了更深的理解:少一些命令,多一些声明,代码会变得更优雅、更可预测。

相关推荐
1***81532 小时前
Swift在服务端开发的可能性探索
开发语言·ios·swift
勇气要爆发2 小时前
第三阶段:ExoPlayer进阶播放器
android·音视频·exoplayer
S***H2832 小时前
Swift在系统级应用中的开发
开发语言·ios·swift
掌心天涯2 小时前
Android Gradle工程引入三方so库的方法
android·jni
J***Q2923 小时前
Kotlin DSL开发技巧
android·开发语言·kotlin
勇气要爆发3 小时前
第二阶段:Android音视频基础
android·音视频
kk哥88993 小时前
iOS进阶1-combine
ios
度熊君4 小时前
深入理解 Kotlin 协程结构化并发
android·程序员
吴Wu涛涛涛涛涛Tao4 小时前
用 Flutter + BLoC 写一个顺手的涂鸦画板(支持撤销 / 重做 / 橡皮擦 / 保存相册)
android·flutter·ios