From 8e6bada617dc0b4dbb442deb784b521155ac5a4f Mon Sep 17 00:00:00 2001 From: gaozl Date: Wed, 10 Dec 2025 10:51:30 +0800 Subject: [PATCH] fix: ci --- app/build.gradle | 31 +- app/src/main/AndroidManifest.xml | 1 + .../activity/program/MyPresentation.java | 108 +++--- .../activity/program/ViewScreenSaver.java | 132 +++----- .../container/view/CustomerVideoView.java | 316 +++++++++++++++++- playerview/build.gradle | 6 +- 6 files changed, 424 insertions(+), 170 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6ecdfb9..e44abb6 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 30 - buildToolsVersion "30.0.3" + buildToolsVersion "35.0.1" buildFeatures { dataBinding = true } @@ -12,10 +12,11 @@ android { minSdkVersion 24 targetSdkVersion 30 versionCode 6 - versionName "V2.0.8.5" + versionName "V2.0.8.6" // 2.0.8.2 修改网络连接证书设置、定时开关机设置 // 2.0.8.3 获取mac修改 // V2.0.8.5 同屏优化 + // 2.0.8.6 视频播放器切换 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles 'consumer-rules.pro' @@ -92,19 +93,19 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation files('libs\\ymeeting.jar')//亿晟 + implementation files('libs/ymeeting.jar')//亿晟 implementation project(path: ':playerview') - implementation files('libs\\zckjAPI-2.2.jar')//卓策 - implementation files('libs\\jna-min.jar') - implementation files('libs\\toolbox_kit.jar')//灰度主板 罗湖寻车机 - implementation files('libs\\shockman.jar')//指路机 + implementation files('libs/zckjAPI-2.2.jar')//卓策 + implementation files('libs/jna-min.jar') + implementation files('libs/toolbox_kit.jar')//灰度主板 罗湖寻车机 + implementation files('libs/shockman.jar')//指路机 implementation files('libs/signway.jar') //欣威视通 - implementation files('libs\\sdkapi.jar') //Bv-3588 - implementation files('libs\\smdt.jar') //视美泰 - implementation files('libs\\DUI-lite-SDK-for-Android-3.5.0-466a4c77.jar') //思必驰 + implementation files('libs/sdkapi.jar') //Bv-3588 + implementation files('libs/smdt.jar') //视美泰 + implementation files('libs/DUI-lite-SDK-for-Android-3.5.0-466a4c77.jar') //思必驰 testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.okhttp3:okhttp:4.9.3' @@ -117,8 +118,8 @@ dependencies { implementation 'jp.wasabeef:glide-transformations:3.0.1' annotationProcessor 'com.alibaba:arouter-compiler:1.2.2' implementation 'org.java-websocket:Java-WebSocket:1.5.1' - implementation 'com.google.android.exoplayer:exoplayer:2.11.4' - + //视频播放 + implementation 'com.google.android.exoplayer:exoplayer:2.15.1' implementation 'com.yanzhenjie.andserver:api:2.0.4' annotationProcessor 'com.yanzhenjie.andserver:processor:2.0.4' implementation files('libs/java-unrar-1.7.0-8.jar') // rar解压 @@ -128,7 +129,7 @@ dependencies { implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0' implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1' implementation 'org.bouncycastle:bcpkix-jdk15on:1.59' - implementation 'com.android.support:support-v4:30.4.1' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' //加载json动图 implementation 'com.airbnb.android:lottie:4.2.2' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8daa3bc..05b11ef 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,6 +57,7 @@ android:largeHeap="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" + android:hardwareAccelerated="true" android:sharedUserId="android.uid.system" android:usesCleartextTraffic="true" android:theme="@style/AppTheme"> diff --git a/app/src/main/java/qianmu/container/activity/program/MyPresentation.java b/app/src/main/java/qianmu/container/activity/program/MyPresentation.java index 721b18c..a06a5e2 100644 --- a/app/src/main/java/qianmu/container/activity/program/MyPresentation.java +++ b/app/src/main/java/qianmu/container/activity/program/MyPresentation.java @@ -36,6 +36,7 @@ import com.airbnb.lottie.LottieAnimationView; import com.alibaba.android.arouter.launcher.ARouter; import com.bumptech.glide.Glide; import com.dl7.player.media.IjkPlayerView; +import com.google.android.exoplayer2.ExoPlayer; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.loopj.android.http.AsyncHttpClient; @@ -2661,46 +2662,17 @@ class MyPresentation extends Presentation { layoutVideo2.bringToFront(); handler.sendEmptyMessage(TYPE_UPDATE_VIDEO0); videoImg2.setVisibility(View.VISIBLE); - videoView2.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + videoView2.setOnPreparedListener(new CustomerVideoView.OnPreparedListener() { @Override - public void onPrepared(MediaPlayer mp) { - mp.setOnInfoListener(new MediaPlayer.OnInfoListener() { - @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) - videoView2.setBackgroundColor(Color.TRANSPARENT); - videoImg2.setVisibility(View.GONE); - return true; - - } - }); - - //视频准备完毕,可以进行播放 - Log.e("(双面屏)TAG","准备好视频,开始播放"); - if( isvideoPlay0 ){ - handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); - if(components.getConfig().isPlayAudio()){ - mp.setVolume(15f,15f); - }else { - mp.setVolume(0f,0f); - } - mp.start(); - - }else{ - Log.e("(双面屏)TAG","预加载播放暂停"); - mp.start(); - mp.setVolume(0f,0f); - //暂停播放 - handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); - handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY0,5000); - } + public void onPrepared() { + videoStartPlay(videoView2, videoImg2, videoComponents0); } }); //视频播放发送错误时回调 - videoView2.setOnErrorListener(new MediaPlayer.OnErrorListener() { + videoView2.setOnErrorListener(new CustomerVideoView.OnErrorListener() { @Override - public boolean onError(MediaPlayer mp, int what, int extra) { + public boolean onError() { //视频播放失败 return true; } @@ -2730,54 +2702,52 @@ class MyPresentation extends Presentation { videoComponents1 = components; handler.sendEmptyMessage(TYPE_UPDATE_VIDEO1); - videoView3.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + videoView3.setOnPreparedListener(new CustomerVideoView.OnPreparedListener() { @Override - public void onPrepared(MediaPlayer mp) { - mp.setOnInfoListener(new MediaPlayer.OnInfoListener() { - @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) - videoView3.setBackgroundColor(Color.TRANSPARENT); - videoImg3.setVisibility(View.GONE); - return true; - - } - }); - - //视频准备完毕,可以进行播放 - Log.e("(双面屏)TAG","准备好视频,开始播放"); - if( isvideoPlay1 ){ - handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); - if(components.getConfig().isPlayAudio()){ - mp.setVolume(15f,15f); - }else { - mp.setVolume(0f,0f); - } - mp.start(); - - }else { - Log.e("(双面屏)TAG","预加载播放暂停"); - mp.start(); - mp.setVolume(0f,0f); - //暂停播放 - handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); - handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY1,5000); - } + public void onPrepared() { + videoStartPlay(videoView3, videoImg3, videoComponents1); } }); //视频播放发送错误时回调 - videoView3.setOnErrorListener(new MediaPlayer.OnErrorListener() { + videoView3.setOnErrorListener(new CustomerVideoView.OnErrorListener() { @Override - public boolean onError(MediaPlayer mp, int what, int extra) { + public boolean onError() { //视频播放失败 - return true; } }); } } + private void videoStartPlay(CustomerVideoView vView, ImageView imgView, NewProgramBean.components components){ + vView.setOnInfoListener(() -> { + // if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) + vView.setBackgroundColor(Color.TRANSPARENT); + imgView.setVisibility(View.GONE); + }); + + //视频准备完毕,可以进行播放 + Log.e("(双面屏)TAG","准备好视频,开始播放"); + if( isvideoPlay0 ){ + handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); + if(components.getConfig().isPlayAudio()){ + vView.setVolume(15f); + }else { + vView.setVolume(0f); + } + vView.start(); + + }else{ + Log.e("(双面屏)TAG","预加载播放暂停"); + vView.start(); + vView.setVolume(0f); + //暂停播放 + handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); + handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY0,5000); + } + + } /** * 更新video * */ diff --git a/app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java b/app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java index 0e97a01..8684234 100644 --- a/app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java +++ b/app/src/main/java/qianmu/container/activity/program/ViewScreenSaver.java @@ -543,7 +543,7 @@ public class ViewScreenSaver extends ViewBase { } if(!isNextVideo){ - Log.e("TAG","预览设置下个视频"); + Log.e("TAG: ","预览设置下个视频"); isNextVideo=true; if(components.getMaterials().get(0).getDuration()>0){ binding.videoView0.setVideoPath(localPath); @@ -551,7 +551,6 @@ public class ViewScreenSaver extends ViewBase { //图片 Drawable drawable = new BitmapDrawable(localPath); binding.videoView0.setBackground(drawable); - } } }else { @@ -576,7 +575,7 @@ public class ViewScreenSaver extends ViewBase { } if(!isNextVideo){ - Log.e("TAG","预览设置下个视频"); + Log.e("TAG: ","预览设置下个视频"); isNextVideo=true; if(components.getMaterials().get(0).getDuration()>0){ binding.videoView0.setVideoPath(localPath); @@ -2740,7 +2739,6 @@ public class ViewScreenSaver extends ViewBase { if(components == null ||components.getMaterials()==null || components.getMaterials().size() <= 0){ return; } - RelativeLayout.LayoutParams lParams = new RelativeLayout.LayoutParams(components.getWidth(), components.getHeight()); lParams.setMargins(components.getOffsetX(), components.getOffsetY(), 0, 0); if(position==0){ @@ -2755,54 +2753,22 @@ public class ViewScreenSaver extends ViewBase { layoutParams.height = components.getHeight(); layoutParams.width = components.getWidth(); videoComponents0 = components; - binding.layoutVideo0.setVisibility(View.VISIBLE); binding.layoutVideo0.bringToFront(); handler.sendEmptyMessage(TYPE_UPDATE_VIDEO0); - binding.videoImg0.setVisibility(View.VISIBLE); - binding.videoView0.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + binding.videoView0.setOnPreparedListener(new CustomerVideoView.OnPreparedListener() { @Override - public void onPrepared(MediaPlayer mp) { - mp.setOnInfoListener(new MediaPlayer.OnInfoListener() { - @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) - binding.videoView0.setBackgroundColor(Color.TRANSPARENT); - binding.videoImg0.setVisibility(View.GONE); - return true; - - } - }); - - //视频准备完毕,可以进行播放 - Log.e("TAG","准备好视频,开始播放"); - if( isvideoPlay0 ){ - handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); - if(components.getConfig().isPlayAudio()){ - mp.setVolume(15f,15f); - }else { - mp.setVolume(0f,0f); - } - mp.start(); - - }else{ - Log.e("TAG","预加载播放暂停"); - mp.start(); - mp.setVolume(0f,0f); - //暂停播放 - handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); - handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY0,5000); - } + public void onPrepared() { + videoStartPlay(binding.videoView0, binding.videoImg0, videoComponents0, isvideoPlay0); } }); // 视频播放完成时的操作 - binding.videoView0.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + binding.videoView0.setOnCompletionListener(new CustomerVideoView.OnCompletionListener() { @Override - public void onCompletion(MediaPlayer mp) { + public void onCompletion() { if(!LocSocCliManager.getLocalSocketState()){ - LoggerUtil.e("TAG:: ","socket未连接,播放下一个"); // 视频播放完成时的操作 handler.sendEmptyMessage(TYPE_UPDATE_VIDEO0); } @@ -2810,9 +2776,9 @@ public class ViewScreenSaver extends ViewBase { }); //视频播放发送错误时回调 - binding.videoView0.setOnErrorListener(new MediaPlayer.OnErrorListener() { + binding.videoView0.setOnErrorListener(new CustomerVideoView.OnErrorListener() { @Override - public boolean onError(MediaPlayer mp, int what, int extra) { + public boolean onError() { //视频播放失败 return true; } @@ -2830,7 +2796,6 @@ public class ViewScreenSaver extends ViewBase { binding.videoView1.seekTo(0); binding.videoView1.setZOrderOnTop(true); binding.videoView1.setZOrderMediaOverlay(true); - binding.videoView1.setVisibility(View.VISIBLE); binding.layoutVideo1.setVisibility(View.VISIBLE); binding.layoutVideo1.bringToFront(); @@ -2839,46 +2804,17 @@ public class ViewScreenSaver extends ViewBase { videoComponents1 = components; handler.sendEmptyMessage(TYPE_UPDATE_VIDEO1); - binding.videoView1.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + binding.videoView1.setOnPreparedListener(new CustomerVideoView.OnPreparedListener() { @Override - public void onPrepared(MediaPlayer mp) { - mp.setOnInfoListener(new MediaPlayer.OnInfoListener() { - @Override - public boolean onInfo(MediaPlayer mp, int what, int extra) { - if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) - binding.videoView1.setBackgroundColor(Color.TRANSPARENT); - binding.videoImg1.setVisibility(View.GONE); - return true; - - } - }); - - //视频准备完毕,可以进行播放 - Log.e("TAG","准备好视频,开始播放"); - if( isvideoPlay1 ){ - handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); - if(components.getConfig().isPlayAudio()){ - mp.setVolume(15f,15f); - }else { - mp.setVolume(0f,0f); - } - mp.start(); - - }else { - Log.e("TAG","预加载播放暂停"); - mp.start(); - mp.setVolume(0f,0f); - //暂停播放 - handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); - handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY1,5000); - } + public void onPrepared() { + videoStartPlay(binding.videoView1, binding.videoImg1, videoComponents1,isvideoPlay1); } }); // 视频播放完成时的操作 - binding.videoView1.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + binding.videoView1.setOnCompletionListener(new CustomerVideoView.OnCompletionListener() { @Override - public void onCompletion(MediaPlayer mp) { + public void onCompletion() { if(!LocSocCliManager.getLocalSocketState()){ // 视频播放完成时的操作 handler.sendEmptyMessage(TYPE_UPDATE_VIDEO1); @@ -2886,9 +2822,9 @@ public class ViewScreenSaver extends ViewBase { } }); //视频播放发送错误时回调 - binding.videoView1.setOnErrorListener(new MediaPlayer.OnErrorListener() { + binding.videoView1.setOnErrorListener(new CustomerVideoView.OnErrorListener() { @Override - public boolean onError(MediaPlayer mp, int what, int extra) { + public boolean onError() { //视频播放失败 return true; } @@ -2896,6 +2832,41 @@ public class ViewScreenSaver extends ViewBase { } } + private void videoStartPlay(CustomerVideoView vView, ImageView imgView, NewProgramBean.components components, Boolean isPlay){ + vView.setOnInfoListener(new CustomerVideoView.OnInfoListener() { + @Override + public void onInfo() { + //vView.clearBackground(); + imgView.setVisibility(View.GONE); + } + }); + + //视频准备完毕,可以进行播放 + if( isPlay ){ + handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); + handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); + if(components.getConfig().isPlayAudio()){ + vView.setVolume(15f); + }else { + vView.setVolume(0f); + } + vView.start(); + }else { + vView.start(); + vView.setVolume(0f); + //暂停播放 + if(vView == binding.videoView0){ + handler.removeMessages(TYPE_STOP_VIDEO_PLAY0); + handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY0,5000); + }else{ + handler.removeMessages(TYPE_STOP_VIDEO_PLAY1); + handler.sendEmptyMessageDelayed(TYPE_STOP_VIDEO_PLAY1,5000); + } + + } + + } + /** * 更新video * */ @@ -2929,7 +2900,6 @@ public class ViewScreenSaver extends ViewBase { if(materials0.getDuration()>0){ //视频 videoView.setVideoPath(localPath); - }else { //图片 if(TYPE_UPDATE_VIDE==TYPE_UPDATE_VIDEO0){ diff --git a/app/src/main/java/qianmu/container/view/CustomerVideoView.java b/app/src/main/java/qianmu/container/view/CustomerVideoView.java index 8bc2bcc..6b188c6 100644 --- a/app/src/main/java/qianmu/container/view/CustomerVideoView.java +++ b/app/src/main/java/qianmu/container/view/CustomerVideoView.java @@ -1,35 +1,347 @@ package qianmu.container.view; import android.content.Context; +import android.graphics.Color; +import android.graphics.PixelFormat; +import android.graphics.drawable.Drawable; +import android.net.Uri; import android.util.AttributeSet; +import android.util.Log; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.widget.FrameLayout; import android.widget.VideoView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.android.exoplayer2.PlaybackException; +import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.Player; +import com.google.android.exoplayer2.SimpleExoPlayer; +import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; +import com.google.android.exoplayer2.ui.StyledPlayerView; + /** * Created by Android Studio. * User: linzhibin * Date: 2021/5/19 * Time: 13:59 */ - public class CustomerVideoView extends VideoView { + public class CustomerVideoView extends FrameLayout { + + private StyledPlayerView playerView; + private ExoPlayer player; + private OnCompletionListener onCompletionListener; + private OnErrorListener onErrorListener; + private OnPreparedListener onPreparedListener; + private OnInfoListener onInfoListener; + + // 监听器接口,保持与 VideoView 兼容 + public interface OnCompletionListener { + void onCompletion(); + } + + public interface OnErrorListener { + boolean onError(); + } + + public interface OnPreparedListener { + void onPrepared(); + } + + public interface OnInfoListener { + void onInfo(); + } + public CustomerVideoView(Context context) { super(context); + init(context); } public CustomerVideoView(Context context, AttributeSet attrs) { super(context, attrs); + init(context); } public CustomerVideoView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + // 创建 StyledPlayerView + playerView = new StyledPlayerView(context); + addView(playerView); + // 初始化 ExoPlayer + initializePlayer(); + // 设置背景透明 + playerView.setBackgroundColor(Color.TRANSPARENT); + playerView.setShutterBackgroundColor(Color.TRANSPARENT); + + // 3. 设置使用透明背景 + playerView.setUseArtwork(false); // 如果不需要 artwork + playerView.setDefaultArtwork(null); // 清除默认 artwork + + // 4. 设置 SurfaceView 透明 + View videoSurfaceView = playerView.getVideoSurfaceView(); + if (videoSurfaceView != null) { + videoSurfaceView.setBackgroundColor(Color.TRANSPARENT); + + // 如果使用 SurfaceView,设置格式支持透明 + if (videoSurfaceView instanceof SurfaceView) { + SurfaceView surfaceView = (SurfaceView) videoSurfaceView; + surfaceView.setZOrderOnTop(true); // 必须设置为 true 才能使透明生效 + surfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT); + }else if (videoSurfaceView instanceof TextureView) { + // TextureView 默认支持透明 + videoSurfaceView.setBackgroundColor(Color.TRANSPARENT); + } + } + } + + private void initializePlayer() { + // 创建 ExoPlayer 实例 + player = new SimpleExoPlayer.Builder(getContext()) + .setTrackSelector(new DefaultTrackSelector(getContext())) + .build(); + player.setPlayWhenReady(false); + // 设置播放器到 PlayerView + playerView.setPlayer(player); + setupPlayerListeners(); + // 默认设置(可以根据需要调整) + playerView.setUseController(false); + // 默认设置 Surface 在底部 + setZOrderOnTop(false); + setZOrderMediaOverlay(false); + } + + private void setupPlayerListeners() { + if (player == null) return; + // 播放完成监听 + player.addListener(new Player.Listener() { + @Override + public void onPlaybackStateChanged(int playbackState) { + if (playbackState == Player.STATE_ENDED) { + if (onCompletionListener != null) { + onCompletionListener.onCompletion(); + } + } + if(playbackState == Player.STATE_READY){ + // 视频准备就绪 + if (onPreparedListener != null) { + onPreparedListener.onPrepared(); + } + if (playerView != null) { + playerView.setVisibility(VISIBLE); + playerView.setAlpha(0); + } + } + } + + @Override + public void onPlayerError(@NonNull PlaybackException error) { + if (onErrorListener != null) { + onErrorListener.onError(); + } + } + @Override + public void onRenderedFirstFrame() { + // 视频开始渲染第一帧 + if (onInfoListener != null) { + onInfoListener.onInfo(); + onInfoListener=null; + } + if(playerView != null){ + playerView.setAlpha(1); + } + } + }); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // 其实就是在这里做了一些处理。 + // 保持原有的测量逻辑 int width = getDefaultSize(0, widthMeasureSpec); int height = getDefaultSize(0, heightMeasureSpec); setMeasuredDimension(width, height); + + // 让 StyledPlayerView 填满整个父布局 + if (playerView != null) { + playerView.measure( + MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) + ); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // 布局 StyledPlayerView 使其填满整个控件 + if (playerView != null) { + playerView.layout(0, 0, getWidth(), getHeight()); + } + } + + // ==================== 实现 VideoView 的常用方法 ==================== + + // 1. setBackground - 设置背景 + @Override + public void setBackground(Drawable background) { + super.setBackground(background); + // 如果需要设置 PlayerView 的背景 + if (playerView != null) { + playerView.setVisibility(GONE); + } + } + + public void clearBackground(){ + super.setBackground(null); + } + + @Override + public void setBackgroundColor(int color) { + super.setBackgroundColor(color); + + } + + @Override + public void setBackgroundResource(int resid) { + super.setBackgroundResource(resid); + if (playerView != null) { + playerView.setVisibility(GONE); + } + } + + // 2. setZOrderOnTop - 设置 Surface 是否在最顶层 + public void setZOrderOnTop(boolean onTop) { + if (playerView != null) { + // 获取 PlayerView 内部的 SurfaceView + View videoSurfaceView = playerView.getVideoSurfaceView(); + if (videoSurfaceView instanceof SurfaceView) { + SurfaceView surfaceView = (SurfaceView) videoSurfaceView; + surfaceView.setZOrderOnTop(onTop); + } + } + } + + // 3. setZOrderMediaOverlay - 设置 Surface 作为媒体叠加层 + public void setZOrderMediaOverlay(boolean isMediaOverlay) { + if (playerView != null) { + View videoSurfaceView = playerView.getVideoSurfaceView(); + if (videoSurfaceView instanceof SurfaceView) { + SurfaceView surfaceView = (SurfaceView) videoSurfaceView; + surfaceView.setZOrderMediaOverlay(isMediaOverlay); + } + } + } + + // 4. setVisibility - 设置可见性 + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (playerView != null) { + playerView.setVisibility(visibility); + } + } + + // 5. 视频控制相关方法 + public void setVideoPath(String path) { + if (player != null && path != null) { + MediaItem mediaItem = MediaItem.fromUri(path); + player.setMediaItem(mediaItem); + player.prepare(); + } + } + + + public void start() { + if (player != null) { + player.play(); + } + } + + public void pause() { + if (player != null) { + player.pause(); + } + } + + public void stopPlayback() { + if (player != null) { + player.stop(); + } + } + + public void seekTo(int position) { + if (player != null) { + player.seekTo(position); + } + } + + public boolean isPlaying() { + return player != null && player.isPlaying(); + } + + // 6. 监听器设置 + public void setOnCompletionListener(OnCompletionListener listener) { + this.onCompletionListener = listener; + } + + public void setOnPreparedListener(OnPreparedListener listener) { + this.onPreparedListener = listener; + } + + public void setOnInfoListener(OnInfoListener listener) { + this.onInfoListener = listener; + } + + public void setOnErrorListener(OnErrorListener listener) { + this.onErrorListener = listener; + } + + // 可选:添加与 ExoPlayer 兼容的错误监听器 + public void setOnExoPlayerErrorListener(Player.Listener errorListener) { + if (player != null && errorListener != null) { + player.addListener(errorListener); + } + } + + public void release() { + if (player != null) { + player.release(); + player = null; + } + if (playerView != null) { + playerView.setPlayer(null); + } + onCompletionListener = null; + onErrorListener = null; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + release(); + } + + // 重置播放器 + public void reset() { + if (player != null) { + player.stop(); + player.clearMediaItems(); + } + } + + // 设置音量 + public void setVolume(float volume) { + if (player != null) { + player.setVolume(volume); + } } } diff --git a/playerview/build.gradle b/playerview/build.gradle index 49b7829..2ac3192 100644 --- a/playerview/build.gradle +++ b/playerview/build.gradle @@ -3,12 +3,12 @@ apply plugin: 'com.github.dcendents.android-maven' group='com.github.Rukey7' android { - compileSdkVersion 30 + compileSdkVersion 34 buildToolsVersion "30.0.3" defaultConfig { minSdkVersion 22 - targetSdkVersion 24 + targetSdkVersion 34 versionCode 4 versionName "1.0.3" @@ -29,7 +29,7 @@ dependencies { androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - implementation 'com.android.support:appcompat-v7:24.2.1' + implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'