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