Files
yusheng-android/BaseModule/src/main/java/com/xscm/moduleutil/widget/GiftAnimView.java

501 lines
17 KiB
Java
Raw Normal View History

2025-10-24 17:55:15 +08:00
package com.xscm.moduleutil.widget;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.blankj.utilcode.util.LogUtils;
import com.tencent.qgame.animplayer.AnimConfig;
import com.tencent.qgame.animplayer.AnimView;
import com.tencent.qgame.animplayer.inter.IAnimListener;
import com.xscm.moduleutil.bean.GiftBean;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReentrantLock;
import lombok.Setter;
2025-10-24 17:55:15 +08:00
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class GiftAnimView extends FrameLayout implements GiftSvgaView.OnAnimationListener {
private AnimView playerMp4View;
private GiftSvgaView svgaView;
private GiftBean playModel;
private boolean isLoadEffect = false;
private boolean isShow = true;//是否开启特效
private ReentrantLock lock = new ReentrantLock();
private List<String> giftArray = new ArrayList<>();
@Setter
public ExecutorService queue;
2025-10-24 17:55:15 +08:00
private Context mContext;
private boolean isOnece;
// 添加带Context参数的构造函数
// 添加带Context和AttributeSet参数的构造函数解决XML inflate问题的关键
public GiftAnimView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
init();
}
// 添加带Context、AttributeSet和 defStyleAttr 参数的构造函数(更完整的实现)
public GiftAnimView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
init();
}
public GiftAnimView(Context context) {
super(context);
this.mContext = context;
init();
}
private void init() {
isLoadEffect = false;
2025-12-08 19:01:09 +08:00
// setBackgroundColor(getResources().getColor(R.color.colorBlack45));
2025-10-24 17:55:15 +08:00
// 初始化SVGA视图
svgaView = new GiftSvgaView(getContext());
addView(svgaView);
playerMp4View = new AnimView(getContext());
addView(playerMp4View);
// 设置布局参数 - 在Android中通常使用LayoutParams
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
playerMp4View.setLoop(1);
playerMp4View.setLayoutParams(params);
playerMp4View.setAnimListener(new IAnimListener() {
@Override
public void onVideoDestroy() {
LogUtils.e("onVideoDestroy");
}
@Override
public void onVideoComplete() {
LogUtils.e("onVideoComplete");
post(() -> {
if (playerMp4View!=null) {
playerMp4View.setVisibility(View.GONE);
if (isOnece){
return;
}
// 通知播放完成
notifyPlaybackComplete();
loadStartSVGAPlayer();
}
});
}
@Override
public void onVideoRender(int i, @Nullable AnimConfig animConfig) {
// LogUtils.e("onVideoRender", i, animConfig);
}
@Override
public void onVideoStart() {
LogUtils.e("onVideoStart");
}
@Override
public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) {
LogUtils.e("onVideoConfigReady", animConfig);
return true;
}
@Override
public void onFailed(int i, @Nullable String s) {
LogUtils.e("onFailed", s);
}
});
svgaView.setDidFinishedDisplay(this);
svgaView.setDidStartAnimation(this);
}
public void displayEffectView1(List<String> stringList){
queue.execute(new Runnable() {
@Override
public void run() {
/// 如果play_image不存在return
if (stringList == null || stringList.isEmpty()) {
return;
}
/// 锁住list
lock.lock();
try {
/// 添加礼物进list
giftArray.addAll(stringList);
/// 如果没有在加载则开始加载
if (!isLoadEffect) {
/// 更改加载状态标记
isLoadEffect = true;
loadStartSVGAPlayer();
}
} finally {
/// 解锁
lock.unlock();
}
}
});
}
/// 礼物特效进来 gift为礼物实体类
public void displayEffectView(final String gift) {
queue.execute(new Runnable() {
@Override
public void run() {
/// 如果play_image不存在return
if (gift == null || gift.isEmpty()) {
return;
}
/// 将play_image链接转为小写
String playImage = gift;
String pathExtension = getFileExtension(playImage).toLowerCase();
/// 判定礼物的后缀是否为svga或者mp4如果非这两种 则return
if (!("svga".equals(pathExtension) || "mp4".equals(pathExtension))) {
return;
}
/// 锁住list
lock.lock();
try {
/// 添加礼物进list
giftArray.add(gift);
/// 如果没有在加载则开始加载
if (!isLoadEffect) {
/// 更改加载状态标记
isLoadEffect = true;
loadStartSVGAPlayer();
}
} finally {
/// 解锁
lock.unlock();
}
}
});
}
public void previewEffectWith(String playImage){
this.isOnece=true;
if (playImage.endsWith("mp4")) {
downloadAndPlay(getContext(), playImage, new DownloadCallback() {
@Override
public void onSuccess(File file) {
post(() -> {
playerMp4View.setVisibility(View.VISIBLE);
svgaView.setVisibility(View.GONE);
playerMp4View.startPlay(file);
});
}
@Override
public void onFailure(Exception e) {
LogUtils.e("MP4下载或播放失败: " + e.getMessage());
// 处理失败情况,继续播放下一个
}
});
} else if (playImage.endsWith("svga")) {
// File file = downloadAndPlay(getContext(), playImage);
post(() -> {
playerMp4View.setVisibility(View.GONE);
svgaView.setVisibility(View.VISIBLE);
svgaView.loadSVGAPlayerWith(playImage, false);
});
}
}
public void openOrCloseEffectViewWith(boolean isShow) {
this.isShow = isShow;
removeSvgaQueueData();
playerMp4View.stopPlay();
playerMp4View.setVisibility(View.GONE);
setVisibility(isShow ? View.VISIBLE : View.GONE);
}
public void stopPlay() {
removeSvgaQueueData();
playerMp4View.stopPlay();
playerMp4View.setVisibility(View.GONE);
}
private void loadStartSVGAPlayer() {
if (!isShow) {
/// isshow 为是否开启特效 如果未开启则return
return;
}
String giftModel = null;
/// list加锁
lock.lock();
try {
/// 如果list长度大于0
if (!giftArray.isEmpty()) {
/// gift的实体类则赋值取list中的第一个数据
giftModel = giftArray.get(0);
/// 移除list的第一条数据
giftArray.remove(0);
isLoadEffect = true;
} else {
isLoadEffect = false;
}
} finally {
/// 解锁
lock.unlock();
}
if (isLoadEffect && giftModel != null && !TextUtils.isEmpty(giftModel)) {
String finalGiftModel = giftModel;
post(new Runnable() {
@Override
public void run() {
String playImage = finalGiftModel;
if (playImage.endsWith("mp4")) {
downloadAndPlay(getContext(), playImage, new DownloadCallback() {
@Override
public void onSuccess(File file) {
post(() -> {
playerMp4View.setVisibility(View.VISIBLE);
svgaView.setVisibility(View.GONE);
playerMp4View.startPlay(file);
});
}
@Override
public void onFailure(Exception e) {
LogUtils.e("MP4下载或播放失败: " + e.getMessage());
// 处理失败情况,继续播放下一个
post(() -> {
lock.lock();
try {
isLoadEffect = false;
} finally {
lock.unlock();
}
loadStartSVGAPlayer();
});
}
});
} else if (playImage.endsWith("svga")) {
// File file = downloadAndPlay(getContext(), playImage);
post(() -> {
playerMp4View.setVisibility(View.GONE);
svgaView.setVisibility(View.VISIBLE);
svgaView.loadSVGAPlayerWith(finalGiftModel, false);
});
} else {
lock.lock();
try {
isLoadEffect = false;
} finally {
lock.unlock();
}
loadStartSVGAPlayer();
// 直接播放缓存文件
}
}
});
}
}
public void downloadAndPlay(Context context, String playImage, DownloadCallback callback) {
String fileName = playImage.substring(playImage.lastIndexOf("/"));
String filePath = context.getCacheDir().getAbsolutePath() + fileName;
LogUtils.e("@@@@@filePath: " + filePath.toString());
File file = new File(filePath);
if (!file.exists()) {
LogUtils.e("无缓存");
// 使用OkHttp进行下载
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(playImage)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
LogUtils.e("MP4下载失败: " + e.toString());
// 在主线程中回调失败
post(() -> {
if (callback != null) {
callback.onFailure(e);
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
try (ResponseBody responseBody = response.body()) {
if (responseBody != null) {
File downloadedFile = new File(filePath);
FileOutputStream fos = new FileOutputStream(downloadedFile);
fos.write(responseBody.bytes());
fos.close();
// 在主线程中回调成功
post(() -> {
if (callback != null) {
callback.onSuccess(downloadedFile);
}
});
} else {
// 在主线程中回调失败
post(() -> {
if (callback != null) {
callback.onFailure(new IOException("Response body is null"));
}
});
}
} catch (Exception e) {
LogUtils.e("MP4文件保存失败: " + e.getMessage());
// 在主线程中回调失败
post(() -> {
if (callback != null) {
callback.onFailure(e);
}
});
}
} else {
LogUtils.e("MP4下载响应失败");
// 在主线程中回调失败
post(() -> {
if (callback != null) {
callback.onFailure(new IOException("Response not successful: " + response.code()));
}
});
}
}
});
} else {
// 文件已存在,直接回调成功
if (callback != null) {
callback.onSuccess(file);
}
}
}
// 添加回调接口
public interface DownloadCallback {
void onSuccess(File file);
void onFailure(Exception e);
}
public void destroyEffectView() {
removeSvgaQueueData();
svgaView.stopEffectSvgaPlay();
playerMp4View.stopPlay();
// 清理监听器
clearPlaybackCompleteListeners();
removeView(playerMp4View);
removeView(svgaView);
svgaView = null;
playerMp4View = null;
playModel = null;
if (queue != null) {
queue.shutdown();
queue = null;
}
}
private void removeSvgaQueueData() {
lock.lock();
try {
giftArray.clear();
isLoadEffect = false;
} finally {
lock.unlock();
}
}
private String getFileExtension(String url) {
if (url != null && url.contains(".")) {
return url.substring(url.lastIndexOf(".") + 1);
}
return "";
}
@Override
public void onStartAnimation(GiftSvgaView view) {
}
@Override
public void onFinishedDisplay(GiftSvgaView view) {
post(() -> {
if (svgaView!=null) {
svgaView.setVisibility(View.GONE);
}
if (isOnece){
return;
}
// 通知播放完成
notifyPlaybackComplete();
loadStartSVGAPlayer();
});
}
// 在 GiftAnimView 类中添加播放完成监听接口
public interface OnPlaybackCompleteListener {
void onPlaybackComplete();
}
// 在 GiftAnimView 类中添加成员变量
private List<OnPlaybackCompleteListener> playbackCompleteListeners = new ArrayList<>();
// 在 GiftAnimView 类中添加监听器管理方法
public void addOnPlaybackCompleteListener(OnPlaybackCompleteListener listener) {
if (listener != null && !playbackCompleteListeners.contains(listener)) {
playbackCompleteListeners.add(listener);
}
}
public void removeOnPlaybackCompleteListener(OnPlaybackCompleteListener listener) {
if (listener != null) {
playbackCompleteListeners.remove(listener);
}
}
public void clearPlaybackCompleteListeners() {
playbackCompleteListeners.clear();
}
// 触发播放完成回调的方法
private void notifyPlaybackComplete() {
for (OnPlaybackCompleteListener listener : new ArrayList<>(playbackCompleteListeners)) {
if (listener != null) {
listener.onPlaybackComplete();
}
}
}
}