512 lines
17 KiB
Java
512 lines
17 KiB
Java
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.opensource.svgaplayer.SVGACallback;
|
||
import com.opensource.svgaplayer.SVGADrawable;
|
||
import com.opensource.svgaplayer.SVGAImageView;
|
||
import com.opensource.svgaplayer.SVGAParser;
|
||
import com.opensource.svgaplayer.SVGAVideoEntity;
|
||
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.io.InputStream;
|
||
import java.net.URL;
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
import java.util.concurrent.ExecutorService;
|
||
import java.util.concurrent.Executors;
|
||
import java.util.concurrent.locks.ReentrantLock;
|
||
|
||
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<>();
|
||
public ExecutorService queue = Executors.newSingleThreadExecutor();
|
||
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 void setQueue(ExecutorService queue) {
|
||
this.queue = queue;
|
||
}
|
||
|
||
public GiftAnimView(Context context) {
|
||
super(context);
|
||
this.mContext = context;
|
||
init();
|
||
}
|
||
|
||
private void init() {
|
||
isLoadEffect = false;
|
||
|
||
// 初始化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();
|
||
}
|
||
}
|
||
}
|
||
}
|