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

相关推荐
幻雨様5 小时前
UE5多人MOBA+GAS 45、制作冲刺技能
android·ue5
Jerry说前后端7 小时前
Android 数据可视化开发:从技术选型到性能优化
android·信息可视化·性能优化
Meteors.7 小时前
Android约束布局(ConstraintLayout)常用属性
android
alexhilton8 小时前
玩转Shader之学会如何变形画布
android·kotlin·android jetpack
whysqwhw12 小时前
安卓图片性能优化技巧
android
风往哪边走12 小时前
自定义底部筛选弹框
android
Yyyy48213 小时前
MyCAT基础概念
android
Android轮子哥13 小时前
尝试解决 Android 适配最后一公里
android
雨白15 小时前
OkHttp 源码解析:enqueue 非同步流程与 Dispatcher 调度
android
风往哪边走15 小时前
自定义仿日历组件弹框
android