看效果
vap动画
github 地址: https://github.com/Tencent/vap
代码
// 腾讯 vap 动画
implementation "io.github.tencent:vap:2.0.17"
package com.example.appvap
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.util.Log
import com.example.appvap.databinding.ActivityPlayDemoBinding
import com.example.appvap.tool.FileUtil
import com.tencent.qgame.animplayer.inter.IFetchResource
import com.tencent.qgame.animplayer.mix.Resource
import com.tencent.qgame.animplayer.util.ScaleType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Random
class PlayDemoActivity : AppCompatActivity() {
companion object{
val TAG = "PlayDemoActivity"
}
private lateinit var mContext: Context
private lateinit var binding : ActivityPlayDemoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mContext = this@PlayDemoActivity
binding = ActivityPlayDemoBinding.inflate(layoutInflater)
setContentView(binding.root)
loadFile()
initView()
}
private fun initView(){
// 居中(根据父布局按比例居中并裁剪)
binding.animView.setScaleType(ScaleType.CENTER_CROP)
binding.btnPlay.setOnClickListener {
setListener()
play(videoInfo)
}
}
private fun play(videoInfo: VideoInfo) {
// 播放前强烈建议检查文件的md5是否有改变
// 因为下载或文件存储过程中会出现文件损坏,导致无法播放
CoroutineScope(Dispatchers.IO).async {
val file = File(dir + "/" + videoInfo.fileName)
val md5 = FileUtil.getFileMD5(file)
withContext(Dispatchers.Main){
if (videoInfo.md5 == md5) {
// 开始播放动画文件
binding.animView.startPlay(file)
} else {
Log.e(TAG, "md5 is not match, error md5=$md5")
}
}
}
}
private var head1Img = true
private fun setListener(){
/**
* 注册资源获取类
*/
binding.animView.setFetchResource(object : IFetchResource {
/**
* 获取图片资源
* 无论图片是否获取成功都必须回调 result 否则会无限等待资源
*/
override fun fetchImage(resource: Resource, result: (Bitmap?) -> Unit) {
/**
* srcTag是素材中的一个标记,在制作素材时定义
* 解析时由业务读取tag决定需要播放的内容是什么
* 比如:一个素材里需要显示多个头像,则需要定义多个不同的tag,表示不同位置,需要显示不同的头像,文字类似
*/
val srcTag = resource.tag
if (srcTag.isNotEmpty()) {
val drawableId = if (head1Img) R.drawable.head1 else R.drawable.head2
head1Img = !head1Img
val options = BitmapFactory.Options()
options.inScaled = false
result(BitmapFactory.decodeResource(resources, drawableId, options))
} else {
result(null)
}
}
/**
* 获取文字资源
*/
override fun fetchText(resource: Resource, result: (String?) -> Unit) {
val str = "恭喜 No.${1000 + Random().nextInt(8999)}用户 升神"
val srcTag = resource.tag
if (srcTag.isNotEmpty()) { // 此tag是已经写入到动画配置中的tag
result(str)
} else {
result(null)
}
}
/**
* 播放完毕后的资源回收
*/
override fun releaseResource(resources: List<Resource>) {
resources.forEach {
it.bitmap?.recycle()
}
}
})
}
// 视频信息
data class VideoInfo(val fileName: String, val md5: String)
private val videoInfo = VideoInfo("vapx.mp4", "f981e0f094ead842ad5ae99f1ffaa1a1")
private val dir by lazy {
// 存放在sdcard应用缓存文件中
getExternalFilesDir(null)?.absolutePath ?: Environment.getExternalStorageDirectory().path
}
private fun loadFile() {
CoroutineScope(Dispatchers.IO).async {
val files = Array(1) {
videoInfo.fileName
}
FileUtil.copyAssetsToStorage(mContext, dir, files) {
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".PlayDemoActivity">
<com.tencent.qgame.animplayer.AnimView
android:id="@+id/animView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<Button
android:id="@+id/btnPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"/>
<Button
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/btnPlay"
android:text="停止"/>
</androidx.constraintlayout.widget.ConstraintLayout>