Android 直播间动画动画队列实现

在直播软件中 需要用到各种各样的动画,大量用户赠送礼物和加入房间会产生很多的动画执行.根据不同的动画又要又要有不同的播放逻辑,这个时候就需要容器具根据不同的需求,如排序,插队,顺序,倒叙等场景去执行动画.

1:需求

  • 先进先出/先进后出 --->链表
  • 插队
  • 优先级执行 --->PriorityQueue 队列
  • 容器最大容量

2:需求分析

整理上诉需求,List 处理起来明显很处理,所以就需要用到链表和队列来处理这部分需求

  • LinkList 双向链表 插入和删除快,指定位置获取快的特点 适合先进先出和插队的操作
  • PriorityQueue 是一种基于优先级排序的队列

3:实现

3.1 FIFO/LIFO 和插队需求

需求:

  • 先进先出/先进后出
  • 插队(优先取出)
  • 容量限制(超出容量会移除后边的数据,将新数据添加进去)

实现代码:

kotlin 复制代码
/**
 * 直播动画队列
 * 动画队列顺序执行,支持 FIFO 和 LIFO 动态切换  默认先进先出
 * 可设置最大容量,超过后自动移除旧任务
 * maxQueueSize  最大容量  超出容量 会自动移除最晚执行的顺序
 *
 */
class AnimationFIFOAndLIFOQueueManager(private val maxQueueSize: Int = 100) {

    enum class Mode {
        FIFO, // 先进先出
        LIFO  // 先进后出
    }

    private val animationQueue: LinkedList<() -> Unit> = LinkedList()
    private var isRunning = false
    private var mode = Mode.FIFO

    /**
     * 设置队列模式
     */
    @Synchronized
    fun setMode(newMode: Mode) {
        mode = newMode
    }

    /**
     * 添加动画到队列,如果队列超过最大长度则移除旧动画
     */
    @Synchronized
    fun enqueue(animation: () -> Unit) {
        // 达到最大容量,移除"最老"的任务
        if (animationQueue.size >= maxQueueSize) {
            when (mode) {
                Mode.FIFO -> animationQueue.pollFirst() // FIFO 模式,移除队头(最旧)
                Mode.LIFO -> animationQueue.pollLast()  // LIFO 模式,移除队尾(最旧)
            }
        }

        // 添加新任务
        when (mode) {
            Mode.FIFO -> animationQueue.offerLast(animation)  // 队尾入队
            Mode.LIFO -> animationQueue.offerFirst(animation) // 队头入队
        }
        runNext()
    }

    /**
     * 插队:无论模式,始终插入队头
     */
    @Synchronized
    fun enqueueAtFront(animation: () -> Unit) {
        if (animationQueue.size >= maxQueueSize) {
            animationQueue.pollLast() // 插队时默认移除最尾的(最不紧急)
        }
        animationQueue.offerFirst(animation)
        runNext()
    }

    @Synchronized
    private fun runNext() {
        if (isRunning || animationQueue.isEmpty()) return
        val next = animationQueue.pollFirst() ?: return
        isRunning = true
        try {
            next()
        } catch (e: Exception) {
            isRunning = false
            runNext()
        }
    }

    /**
     * 动画完成时调用
     */
    @Synchronized
    fun onAnimationEnd() {
        isRunning = false
        runNext()
    }

    @Synchronized
    fun clear() {
        animationQueue.clear()
        isRunning = false
    }

    fun isBusy(): Boolean = isRunning
}

调用:

✅ 1. 初始化队列(默认 FIFO)

ini 复制代码
val animationQueue = AnimationFIFOAndLIFOQueueManager()

✅ 2. 添加动画任务(enqueue)

scss 复制代码
animationQueue.enqueue {
    // 这里写你的动画逻辑
    println("动画 A 开始")

    // 假设动画是异步的,比如 500ms 后结束,结束后手动回调:
    Handler(Looper.getMainLooper()).postDelayed({
        println("动画 A 结束")
        animationQueue.onAnimationEnd() // 通知队列继续下一个
    }, 500)
}

✅ 3. 插队(优先执行)

scss 复制代码
animationQueue.enqueueAtFront {
    println("插队动画 B 开始")
    Handler(Looper.getMainLooper()).postDelayed({
        println("插队动画 B 结束")
        animationQueue.onAnimationEnd()
    }, 300)
}

✅ 4. 切换 LIFO 模式(后进先出)

scss 复制代码
animationQueue.setMode(AnimationFIFOAndLIFOQueueManager.Mode.LIFO)

✅ 5. 清空队列 / 判断是否执行中

scss 复制代码
animationQueue.clear()         // 清除所有未执行动画
val busy = animationQueue.isBusy() // 判断当前是否正在执行中

3.2 优先级队列

需求:

  • 按优先级执行
  • 定制容器大小

代码实现

kotlin 复制代码
package com.qianrun.voice.common.utils.animate

import java.util.PriorityQueue

/**
 * 动画队列 等级顺序执行
 * maxSize  队列 最大容量  超过了 移除优先级最低的数据
 * priorityDescending  true:priority越大越先执行;false:priority越小越先执行
 */
class AnimationPriorityQueueManager(  private val maxSize: Int = 100 ,var priorityDescending: Boolean=true ) {
    private var sequenceCounter = 0L
    private val animationQueue: PriorityQueue<AnimationTask> = PriorityQueue()
    private var isRunning = false


    // 添加动画到队列尾部
    @Synchronized
    fun enqueue(priority: Int = 0, animation: () -> Unit) {
        val task = AnimationTask(priority, sequenceCounter++,priorityDescending, animation)
        // 加入新任务前,如果队列满了,先移除一个任务
        if (animationQueue.size >= maxSize) {
            // 移除"最旧"的任务,或者"最低优先级"的任务,依据你想要的策略
            // 这里简单移除优先级最低的(队尾元素)
            animationQueue.poll()
        }
        animationQueue.offer(task)
        runNext()
    }

    @Synchronized
    private fun runNext() {
        if (isRunning || animationQueue.isEmpty()) return

        val next = animationQueue.poll() ?: return

        isRunning = true
        try {
            next.action()
        } catch (e: Exception) {
            isRunning = false
            runNext()
        }
    }


    // 动画完成时回调此方法
    @Synchronized
    fun onAnimationEnd() {
        isRunning = false
        runNext()
    }

    // 清空队列
    @Synchronized
    fun clear() {
        animationQueue.clear()
        isRunning = false
    }

    // 当前是否有动画在运行
    fun isBusy(): Boolean = isRunning
}

调用:

✅ 1. 初始化队列

ini 复制代码
val animationQueue = AnimationPriorityQueueManager(
    maxSize = 50,
    priorityDescending = true // 优先级越大越先执行
)

✅ 2. 添加动画任务(enqueue)

scss 复制代码
animationQueue.enqueue(priority = 10) {
    println("🎬 动画 A(优先级10)开始")
    Handler(Looper.getMainLooper()).postDelayed({
        println("✅ 动画 A 结束")
        animationQueue.onAnimationEnd() // 动画结束必须调用
    }, 500)
}

animationQueue.enqueue(priority = 50) {
    println("🎬 动画 B(优先级50)开始")
    Handler(Looper.getMainLooper()).postDelayed({
        println("✅ 动画 B 结束")
        animationQueue.onAnimationEnd()
    }, 300)
}

会先执行 B(priority=50),再执行 A(priority=10)


✅ 3. 可选:清空/判断是否执行中

scss 复制代码
animationQueue.clear()             // 清空所有任务
val isRunning = animationQueue.isBusy() // 判断是否正在执行动画

✅ 4. 示例封装函数(更清爽)

你可以写个辅助函数来封装动画任务:

kotlin 复制代码
fun enqueueDemoAnimation(name: String, priority: Int, duration: Long) {
    animationQueue.enqueue(priority = priority) {
        println("🎞️ 动画 [$name] 开始(优先级=$priority)")
        Handler(Looper.getMainLooper()).postDelayed({
            println("✅ 动画 [$name] 结束")
            animationQueue.onAnimationEnd()
        }, duration)
    }
}

总结

简单实现了动画的各种队列执行,特此记录下.如有启发 ,欢迎copy

相关推荐
BAGAE6 分钟前
使用 Flutter 在 Windows 平台开发 Android 应用
android·大数据·数据结构·windows·python·flutter
风舞雪凌月9 分钟前
【教程】Android(AOSP)Framework开发/ROM定制快速教程
android·framework·开发·aosp·rom
喜欢踢足球的老罗10 小时前
自动化模型管理:MediaPipe Android SDK 中的模型文件下载与加载机制
android·运维·自动化
AgilityBaby12 小时前
Untiy打包安卓踩坑
android·笔记·学习·unity·游戏引擎
硬件学长森哥13 小时前
Android音视频多媒体开源框架基础大全
android·图像处理·音视频
二流小码农14 小时前
鸿蒙开发:CodeGenie万能卡片生成
android·ios·harmonyos
月山知了14 小时前
Android有的命令不需要root权限,有的命令需要root权限是如何实现的
android
科技道人15 小时前
Android 实体键盘 设置默认布局
android·实体键盘·设置默认键盘语言
SHUIPING_YANG15 小时前
tp3.1临时连接指定数据库,切片分类in查询,带过滤需要的数据
android·数据库