From 532f85e9e2bfee4e89e7fa2732b7d971e451016e Mon Sep 17 00:00:00 2001 From: lzl <1239365383@qq.com> Date: Thu, 4 Dec 2025 23:07:36 +0800 Subject: [PATCH] =?UTF-8?q?cpview=20=E5=9B=9E=E9=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../moduleutil/utils/roomview/RoomCPView.java | 380 ++++++------------ 1 file changed, 129 insertions(+), 251 deletions(-) diff --git a/BaseModule/src/main/java/com/xscm/moduleutil/utils/roomview/RoomCPView.java b/BaseModule/src/main/java/com/xscm/moduleutil/utils/roomview/RoomCPView.java index c35f473b..1da3fc98 100644 --- a/BaseModule/src/main/java/com/xscm/moduleutil/utils/roomview/RoomCPView.java +++ b/BaseModule/src/main/java/com/xscm/moduleutil/utils/roomview/RoomCPView.java @@ -1,4 +1,3 @@ - package com.xscm.moduleutil.utils.roomview; import android.content.Context; @@ -29,7 +28,6 @@ import com.tencent.qgame.animplayer.AnimView; import com.tencent.qgame.animplayer.inter.IAnimListener; import com.xscm.moduleutil.R; import com.xscm.moduleutil.utils.ImageUtils; -import com.xscm.moduleutil.widget.CircularImage; import java.io.File; import java.io.FileOutputStream; @@ -53,145 +51,79 @@ import okhttp3.Response; import okhttp3.ResponseBody; /** - * 羽声语音房间CP特效视图 - * 用于显示CP(情侣)相关的动画特效,包括头像、名称和动画效果 - * 支持MP4和SVGA格式的动画文件 + * 项目名称:羽声语音 * 时间:2025/11/24 9:50 + * 用途: */ public class RoomCPView extends FrameLayout { - // ==================== 视图组件 ==================== - /** 动画视图组件 */ + private AnimView anim_cp; - /** CP头像组件 */ - private CircularImage room_cp_head1, room_cp_head2; - - /** CP名称组件 */ - private TextView room_cp_name1, room_cp_name2; - - /** 头像容器 */ - private LinearLayout avatarContainer1; - - /** 头像父容器 */ - private ConstraintLayout avatarsParentContainer; - - // ==================== 状态控制 ==================== - /** 是否正在加载特效 */ private boolean isLoadEffect = false; - - /** 是否开启特效显示 */ - private boolean isShow = true; - - /** 是否仅播放一次 */ + private boolean isShow = true; // 是否开启特效 + private ReentrantLock lock = new ReentrantLock(); + private List animationArray = new ArrayList<>(); + public ExecutorService queue = Executors.newSingleThreadExecutor(); + private Context mContext; private boolean isOnece; - /** 当前播放的动画路径 */ - private String currPlayPath = ""; + private RoundedImageView room_cp_head1; + private RoundedImageView room_cp_head2; + private TextView room_cp_name1, room_cp_name2; + private LinearLayout avatarContainer1; + private ConstraintLayout avatarsParentContainer; - // ==================== 线程安全 ==================== - /** 动画数组锁 */ - private ReentrantLock lock = new ReentrantLock(); - - /** 动画队列锁 */ + // 动画队列和锁机制 + private final Queue animationQueue = new LinkedList<>(); + private boolean isAnimationRunning = false; private final Object animationLock = new Object(); - /** 下载队列锁 */ + // 下载任务缓存 + private final Queue downloadQueue = new LinkedList<>(); + private boolean isDownloadRunning = false; private final Object downloadLock = new Object(); - // ==================== 数据存储 ==================== - /** 动画路径列表 */ - private List animationArray = new ArrayList<>(); + private String currPlayPath = ""; - /** 动画任务队列 */ - private final Queue animationQueue = new LinkedList<>(); - - /** 下载任务队列 */ - private final Queue downloadQueue = new LinkedList<>(); - - // ==================== 状态标志 ==================== - /** 是否有动画正在运行 */ - private boolean isAnimationRunning = false; - - /** 是否有下载任务正在运行 */ - private boolean isDownloadRunning = false; - - // ==================== 线程池 ==================== - /** 任务执行线程池 */ - public ExecutorService queue = Executors.newSingleThreadExecutor(); - - // ==================== 其他 ==================== - /** 上下文对象 */ - private Context mContext; - - /** 播放完成监听器列表 */ - private List playbackCompleteListeners = new ArrayList<>(); - - /** - * 设置线程池 - * @param queue 线程池 - */ public void setQueue(ExecutorService queue) { this.queue = queue; } - /** - * 基本构造函数 - * @param context 上下文 - */ public RoomCPView(@NonNull Context context) { super(context); this.mContext = context; init(); } - /** - * 带属性集的构造函数 - * @param context 上下文 - * @param attrs 属性集 - */ public RoomCPView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); this.mContext = context; init(); } - /** - * 带属性集和样式属性的构造函数 - * @param context 上下文 - * @param attrs 属性集 - * @param defStyleAttr 默认样式属性 - */ public RoomCPView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; init(); } - /** - * 完整构造函数 - * @param context 上下文 - * @param attrs 属性集 - * @param defStyleAttr 默认样式属性 - * @param defStyleRes 默认样式资源 - */ public RoomCPView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); this.mContext = context; init(); } - /** - * 初始化视图和状态 - */ private void init() { isLoadEffect = false; + initView(); + + // 设置动画监听器 setupAnimListener(); } /** * 设置动画监听器 - * 为动画视图设置各种状态回调,包括开始、完成、失败等事件 */ private void setupAnimListener() { if (anim_cp != null) { @@ -217,31 +149,9 @@ public class RoomCPView extends FrameLayout { if (isOnece) { return; } - - // 添加延迟,确保当前动画完全结束 - postDelayed(() -> { - // 使用动画队列锁确保线程安全 - synchronized (animationLock) { - // 重置播放状态 - lock.lock(); - try { - isLoadEffect = false; - // 标记动画已停止运行 - isAnimationRunning = false; - - // 清除播放器缓存 - clearPlayerCache(); - } finally { - lock.unlock(); - } - - // 通知播放完成 - notifyPlaybackComplete(); - } - - // 继续播放下一个动画 - processNextAnimation(); - }, 100); // 添加100毫秒的延迟 + // 通知播放完成 + notifyPlaybackComplete(); + loadStartAnimation(); } }); } @@ -267,7 +177,6 @@ public class RoomCPView extends FrameLayout { // startAvatarFloatAnimation(); }); } -/**************** c361dac4fe23498d925fba2efe376ca8 ****************/ @Override public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) { @@ -283,133 +192,88 @@ public class RoomCPView extends FrameLayout { // 动画失败,隐藏视图 setVisibility(View.GONE); anim_cp.setVisibility(View.GONE); - - // 使用动画队列锁确保线程安全 - synchronized (animationLock) { - // 重置播放状态 - lock.lock(); - try { - isLoadEffect = false; - // 标记动画已停止运行 - isAnimationRunning = false; - } finally { - lock.unlock(); - } - } - // 继续播放下一个 - processNextAnimation(); + loadStartAnimation(); }); } }); } } - /** - * 初始化视图组件 - * 加载布局文件并获取各个视图组件的引用 - */ private void initView() { - // 加载布局文件 LayoutInflater.from(getContext()).inflate(R.layout.room_cp_vip_view, this, true); - - // 获取动画视图组件 anim_cp = findViewById(R.id.anim_cp); - - // 获取头像组件 room_cp_head1 = findViewById(R.id.room_cp_head1); room_cp_head2 = findViewById(R.id.room_cp_head2); - - // 获取名称组件 room_cp_name1 = findViewById(R.id.room_cp_name1); room_cp_name2 = findViewById(R.id.room_cp_name2); - // 获取头像容器 + // 获取头像的父布局 avatarContainer1 = findViewById(R.id.ll_head); - // 获取包含两个头像的父容器 + // 获取包含两个头像的父 LinearLayout avatarsParentContainer = (ConstraintLayout) avatarContainer1.getParent(); - // 注释:初始状态隐藏头像和名称(如需要可取消注释) - // room_cp_head1.setVisibility(View.GONE); - // room_cp_head2.setVisibility(View.GONE); - // room_cp_name1.setVisibility(View.GONE); - // room_cp_name2.setVisibility(View.GONE); +// // 初始状态隐藏头像和名称 +// room_cp_head1.setVisibility(View.GONE); +// room_cp_head2.setVisibility(View.GONE); +// room_cp_name1.setVisibility(View.GONE); +// room_cp_name2.setVisibility(View.GONE); } - /** - * 设置CP信息数据 - * @param room_head1 第一个用户头像URL - * @param room_head2 第二个用户头像URL - * @param room_cp_name1 第一个用户名称 - * @param room_cp_name2 第二个用户名称 - */ public void setCPTextData(String room_head1, String room_head2, String room_cp_name1, String room_cp_name2) { - // 加载用户头像 ImageUtils.loadHead(room_head1, room_cp_head1); ImageUtils.loadHead(room_head2, room_cp_head2); - - // 设置用户名称 this.room_cp_name1.setText(room_cp_name1); this.room_cp_name2.setText(room_cp_name2); } - /** - * 处理并播放队列中的下一个动画 - */ - private void processNextAnimation() { - // 检查是否开启特效 + private void loadStartAnimation() { if (!isShow) { + // isshow 为是否开启特效 如果未开启则return return; } - // 使用动画队列锁确保线程安全 - synchronized (animationLock) { - // 如果已经有动画在运行,则直接返回,等待当前动画完成后再处理下一个 - if (isAnimationRunning) { - return; - } + String animationPath = null; - // 检查动画队列是否为空 - if (animationQueue.isEmpty()) { - // 队列为空,重置状态并释放资源 + // list加锁 + lock.lock(); + try { + // 如果list长度大于0 + if (!animationArray.isEmpty()) { + // 动画路径则赋值,取list中的第一个数据 + animationPath = animationArray.get(0); + // 移除list的第一条数据 + animationArray.remove(0); + isLoadEffect = true; + } else { isLoadEffect = false; + // 队列为空,释放资源但不销毁视图 post(() -> { destroyEffectView(); }); - return; } + } finally { + // 解锁 + lock.unlock(); + } - // 获取并移除队列中的第一个动画任务 - AnimationTask task = animationQueue.poll(); - if (task == null) { - return; - } - - // 设置状态标记 - isLoadEffect = true; - isAnimationRunning = true; - - // 设置CP数据 - setCPTextData(task.room_head1, task.room_head2, task.room_cp_name1, task.room_cp_name2); - - // 确保视图可见 - setVisibility(View.VISIBLE); - - // 处理动画文件 - String animationPath = task.mp4Path; - if (animationPath != null && !TextUtils.isEmpty(animationPath)) { - post(() -> { + if (isLoadEffect && animationPath != null && !TextUtils.isEmpty(animationPath)) { + String finalAnimationPath = animationPath; + post(new Runnable() { + @Override + public void run() { // 处理MP4动画文件(可能是网络URL) - handleMP4File(animationPath, new DownloadCallback() { + handleMP4File(finalAnimationPath, new DownloadCallback() { @Override public void onSuccess(String localPath) { post(() -> { - // 设置当前播放路径 + // 设置MP4动画文件 currPlayPath = localPath; // 启动从底部弹起动画 startBottomUpAnimation(); - // 设置播放次数为1次 + + // 开始播放动画 anim_cp.setLoop(1); }); } @@ -418,52 +282,88 @@ public class RoomCPView extends FrameLayout { public void onError(String error) { LogUtils.e("MP4下载或播放失败: " + error); // 处理失败情况,继续播放下一个 - synchronized (animationLock) { + post(() -> { lock.lock(); try { - // 重置状态标记 isLoadEffect = false; - isAnimationRunning = false; } finally { lock.unlock(); } - } - // 尝试播放下一个动画 - processNextAnimation(); + loadStartAnimation(); + }); } }); - }); - } + } + }); } } /** - * 显示CP特效 - * 设置CP信息并添加动画到队列中播放 - * @param room_head1 第一个用户头像URL - * @param room_head2 第二个用户头像URL - * @param room_name1 第一个用户名称 - * @param room_name2 第二个用户名称 - * @param mp4Path 动画文件路径(支持mp4和svga格式) + * CP特效进来 + * @param room_head1 头像1 + * @param room_head2 头像2 + * @param room_name1 名称1 + * @param room_name2 名称2 + * @param mp4Path MP4动画文件路径 */ public void displayEffectView(String room_head1, String room_head2, String room_name1, String room_name2, String mp4Path) { // 确保视图已初始化 reinitView(); - // 创建动画任务并添加到队列 - AnimationTask task = new AnimationTask(room_head1, room_head2, room_name1, room_name2, mp4Path, null); + // 设置CP数据,但不显示头像 + setCPTextData(room_head1, room_head2, room_name1, room_name2); - // 使用动画队列锁确保线程安全 - synchronized (animationLock) { - animationQueue.offer(task); + // 确保头像初始为隐藏状态 +// room_cp_head1.setVisibility(View.GONE); +// room_cp_head2.setVisibility(View.GONE); +// room_cp_name1.setVisibility(View.GONE); +// room_cp_name2.setVisibility(View.GONE); - // 如果当前没有动画在播放,则开始播放队列中的第一个动画 - if (!isAnimationRunning) { - processNextAnimation(); - } + // 确保视图可见 + setVisibility(View.VISIBLE); + + // 检查队列是否已初始化 + if (queue == null) { + queue = Executors.newSingleThreadExecutor(); } + + queue.execute(new Runnable() { + @Override + public void run() { + // 如果mp4Path不存在return + if (mp4Path == null || mp4Path.isEmpty()) { + return; + } + + // 将mp4Path链接转为小写 + String playImage = mp4Path; + String pathExtension = getFileExtension(playImage).toLowerCase(); + + // 判定礼物的后缀是否为svga或者mp4如果非这两种 则return + if (!("svga".equals(pathExtension) || "mp4".equals(pathExtension))) { + return; + } + + // 锁住list + lock.lock(); + try { + // 添加动画数据进list + animationArray.add(playImage); + + // 如果没有在加载则开始加载 + if (!isLoadEffect) { + // 更改加载状态标记 + isLoadEffect = true; + loadStartAnimation(); + } + } finally { + // 解锁 + lock.unlock(); + } + } + }); } @@ -520,6 +420,7 @@ public class RoomCPView extends FrameLayout { } // 添加成员变量 + private List playbackCompleteListeners = new ArrayList<>(); // 添加监听器管理方法 public void addOnPlaybackCompleteListener(OnPlaybackCompleteListener listener) { @@ -757,16 +658,12 @@ public class RoomCPView extends FrameLayout { setVisibility(View.GONE); // 清空动画队列 - synchronized (animationLock) { - lock.lock(); - try { - animationQueue.clear(); - isLoadEffect = false; - // 重置动画运行状态 - isAnimationRunning = false; - } finally { - lock.unlock(); - } + lock.lock(); + try { + animationArray.clear(); + isLoadEffect = false; + } finally { + lock.unlock(); } } @@ -966,19 +863,6 @@ public class RoomCPView extends FrameLayout { } } - /** - * 清除播放器缓存 - * 清除当前播放的视频缓存,但不删除已下载的文件 - */ - private void clearPlayerCache() { - if (anim_cp != null) { - // 停止当前播放 - anim_cp.stopPlay(); - // 重置当前播放路径 - currPlayPath = ""; - } - } - /** * 重新初始化视图,以便再次播放 */ @@ -998,7 +882,6 @@ public class RoomCPView extends FrameLayout { // 重置状态 isLoadEffect = false; - isAnimationRunning = false; isShow = true; } @@ -1026,11 +909,6 @@ public class RoomCPView extends FrameLayout { queue = null; } - // 清空动画队列 - synchronized (animationLock) { - animationQueue.clear(); - } - // 隐藏并移除整个视图 setVisibility(View.GONE); if (getParent() != null) {