视频组件更换StandardGSYVideoPlayer

This commit is contained in:
2026-06-04 17:51:19 +08:00
parent 60ecdf140b
commit 88c791a520
6 changed files with 228 additions and 510 deletions

View File

@@ -103,6 +103,8 @@ dependencies {
implementation("com.contrarywind:Android-PickerView:4.1.9") implementation("com.contrarywind:Android-PickerView:4.1.9")
//基于exoPlayer 自定义播放器 JPlayer支持功能 //基于exoPlayer 自定义播放器 JPlayer支持功能
implementation("com.ycjiang:VideoPlayModule-Lite:2.3.61") implementation("com.ycjiang:VideoPlayModule-Lite:2.3.61")
// GSYVideoPlayer
implementation("io.github.carguo:gsyvideoplayer:13.0.0")
} }

View File

@@ -1,39 +1,32 @@
package com.example.defenseapplication.liveVideo package com.example.defenseapplication.liveVideo
import android.content.res.Configuration import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.SeekBar
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import chuangyuan.ycj.videolibrary.listener.VideoInfoListener
import chuangyuan.ycj.videolibrary.video.ExoUserPlayer
import chuangyuan.ycj.videolibrary.video.VideoPlayerManager
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.SimpleExoPlayer
import com.example.defenseapplication.R import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AlarmLogAdapter import com.example.defenseapplication.adapter.AlarmLogAdapter
import com.example.defenseapplication.bean.DeviceAlarmBean import com.example.defenseapplication.bean.DeviceAlarmBean
import com.example.defenseapplication.bean.DeviceAlarmResponse
import com.example.defenseapplication.databinding.AlarmLogActivityBinding import com.example.defenseapplication.databinding.AlarmLogActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork import com.example.defenseapplication.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import kotlinx.coroutines.launch import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import android.os.Handler import com.shuyu.gsyvideoplayer.utils.OrientationUtils
import android.os.Looper import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
import java.text.SimpleDateFormat
import java.util.*
import com.bigkoo.pickerview.builder.TimePickerBuilder import com.bigkoo.pickerview.builder.TimePickerBuilder
import com.bigkoo.pickerview.listener.OnTimeSelectListener import com.bigkoo.pickerview.listener.OnTimeSelectListener
import com.example.defenseapplication.view.MyGSYVideoPlayer
import com.shuyu.gsyvideoplayer.GSYVideoManager
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.*
//告警记录 //告警记录
class AlarmLogActivity : AppCompatActivity() { class AlarmLogActivity : AppCompatActivity() {
@@ -42,151 +35,95 @@ class AlarmLogActivity : AppCompatActivity() {
private var adapter: AlarmLogAdapter? = null private var adapter: AlarmLogAdapter? = null
private val alarmList = mutableListOf<DeviceAlarmBean>() private val alarmList = mutableListOf<DeviceAlarmBean>()
private val originalAlarmList = mutableListOf<DeviceAlarmBean>() private val originalAlarmList = mutableListOf<DeviceAlarmBean>()
private lateinit var exoPlayerManager: ExoUserPlayer
private var isMuted = false
private var isPlaying = false
private var deviceId: String? = null private var deviceId: String? = null
private var isLoading = false private var isLoading = false
private var fromLiveVideo = false private var videoUrl: String? = null
private var isFullScreen = false private var orientationUtils: OrientationUtils? = null
private var isControlsVisible = false
private val hideDelay = 5000L // 5秒自动隐藏 private var isMute = false
private val hideHandler = Handler(Looper.getMainLooper()) private lateinit var audioManager: AudioManager
private val hideRunnable = Runnable { private var isRightMenuVisible = false
hideControls()
}
private var passedVideoUrl: String? = null
private var passedSelectedId: String? = null
private var passedCreateTime: String? = null
private var totalDuration: Long = 0
private val progressHandler = Handler(Looper.getMainLooper())
private val progressRunnable = object : Runnable {
override fun run() {
updateProgress()
progressHandler.postDelayed(this, 1000)
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = AlarmLogActivityBinding.inflate(layoutInflater) binding = AlarmLogActivityBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
deviceId = intent.getStringExtra("device_id") deviceId = intent.getStringExtra("device_id")
fromLiveVideo = intent.getBooleanExtra("from_live_video", false) videoUrl = intent.getStringExtra("video_url")
passedVideoUrl = intent.getStringExtra("video_url")
passedSelectedId = intent.getStringExtra("selected_id")
passedCreateTime = intent.getStringExtra("create_time")
calendar = Calendar.getInstance() calendar = Calendar.getInstance()
updateDateDisplay() updateDateDisplay()
initVideoPlayer() initVideoPlayer()
initRecyclerView() initRecyclerView()
fetchAlarmList() fetchAlarmList()
initListener() initListener()
// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// if (exoPlayerManager.onBackPressed()) {
// return
// }
// finish()
// }
// })
}
private fun dpToPx(dp: Int): Int {
val density = resources.displayMetrics.density
return (dp * density).toInt()
} }
private fun initVideoPlayer() { private fun initVideoPlayer() {
val videoPlayerView = binding.exoPlayContextId val videoUrl = intent.getStringExtra("video_url") ?: "https://media.w3.org/2010/05/sintel/trailer.mp4"
val videoUri = if (fromLiveVideo && !passedVideoUrl.isNullOrEmpty()) { val progressDrawable = ContextCompat.getDrawable(this, R.drawable.progress_video)
passedVideoUrl!! val thumbDrawable = ContextCompat.getDrawable(this, R.drawable.thumb_video)
} else {
"https://media.w3.org/2010/05/sintel/trailer.mp4"
}
exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, videoPlayerView) orientationUtils = OrientationUtils(this, binding.exoPlayContextId)
.setPlayUri(videoUri) orientationUtils?.isEnable = true
.setPosition(0)
.addVideoInfoListener(object : VideoInfoListener { GSYVideoOptionBuilder()
override fun onPlayStart(p0: Long) { .setUrl(videoUrl)
Log.d("AlarmLogActivity", "视频开始播放") .setVideoTitle("")
startProgressUpdate() .setNeedLockFull(true)
showControls() .setCacheWithPlay(false)
resetHideTimer() .setRotateViewAuto(true)
.setLockLand(false)
.setShowFullAnimation(true)
.setNeedShowWifiTip(false)
.setEnlargeImageRes(R.drawable.full_size)
.setBottomShowProgressBarDrawable(progressDrawable, thumbDrawable)
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onPrepared(url: String?, vararg objects: Any?) {
super.onPrepared(url, *objects)
orientationUtils?.isEnable = true
} }
override fun onLoadingChanged() { override fun onQuitFullscreen(url: String?, vararg objects: Any?) {
} super.onQuitFullscreen(url, *objects)
orientationUtils?.backToProtVideo()
override fun onPlayerError(p0: com.google.android.exoplayer2.ExoPlaybackException?) {
Toast.makeText(this@AlarmLogActivity, "播放失败", Toast.LENGTH_SHORT).show()
}
override fun onPlayEnd() {
Log.d("AlarmLogActivity", "播放结束")
stopProgressUpdate()
hideControls()
}
override fun isPlaying(p0: Boolean) {
isPlaying = p0
if (p0) {
startProgressUpdate()
// 视频开始播放时,如果控制栏可见,启动自动隐藏计时器
if (isControlsVisible) {
resetHideTimer()
}
} else {
stopProgressUpdate()
// 视频暂停时,取消自动隐藏计时器,保持控制栏显示
hideHandler.removeCallbacks(hideRunnable)
showControls()
}
}
fun onRepeatModeChanged(p0: Int) {
} }
}) })
.create() .build(binding.exoPlayContextId)
exoPlayerManager.startPlayer() binding.exoPlayContextId.onControlVisibilityChanged = { show ->
Log.e("GSY_TEST", "Activity收到回调 show=$show")
val visible = if (show) View.VISIBLE else View.GONE
setupControllerVisibilityListener() binding.ivDownload.visibility = visible
updateProgress() binding.ivDelete.visibility = visible
} binding.ivMute.visibility = visible
private fun setupControllerVisibilityListener() {
// 移除原来的控制器监听器,改为自定义控制逻辑
}
private fun findPlayerView(view: View): PlayerView? {
if (view is PlayerView) {
return view
} }
if (view is ViewGroup) {
for (i in 0 until view.childCount) { binding.exoPlayContextId.startPlayLogic()
val child = view.getChildAt(i) binding.exoPlayContextId.fullscreenButton.setOnClickListener {
val found = findPlayerView(child) orientationUtils?.resolveByClick()
if (found != null) {
return found binding.exoPlayContextId.startWindowFullscreen(this, true, true)?.let { fullPlayer ->
if (fullPlayer is MyGSYVideoPlayer) {
fullPlayer.onControlVisibilityChanged = { show ->
val visible = if (show) View.VISIBLE else View.GONE
binding.ivDownload.visibility = visible
binding.ivDelete.visibility = visible
binding.ivMute.visibility = visible
}
} }
} }
} }
return null
}
}
private fun initRecyclerView() { private fun initRecyclerView() {
adapter = AlarmLogAdapter(alarmList) { item -> adapter = AlarmLogAdapter(alarmList) { item ->
if (!item.video.isNullOrEmpty()) { if (!item.video.isNullOrEmpty()) {
playVideo(item.video!!) Toast.makeText(this, "该告警记录有视频", Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(this, "该告警记录没有视频", Toast.LENGTH_SHORT).show() Toast.makeText(this, "该告警记录没有视频", Toast.LENGTH_SHORT).show()
} }
@@ -195,68 +132,6 @@ class AlarmLogActivity : AppCompatActivity() {
binding.alarmListRecyclerView.adapter = adapter binding.alarmListRecyclerView.adapter = adapter
} }
private fun playVideo(videoUrl: String) {
exoPlayerManager.onDestroy()
exoPlayerManager = VideoPlayerManager.Builder(VideoPlayerManager.TYPE_PLAY_GESTURE, binding.exoPlayContextId)
.setPlayUri(videoUrl)
.setPosition(0)
.addVideoInfoListener(object : VideoInfoListener {
override fun onPlayStart(p0: Long) {
Log.d("AlarmLogActivity", "视频开始播放")
startProgressUpdate()
showControls()
resetHideTimer()
}
override fun onLoadingChanged() {
}
override fun onPlayerError(p0: com.google.android.exoplayer2.ExoPlaybackException?) {
Toast.makeText(this@AlarmLogActivity, "播放失败", Toast.LENGTH_SHORT).show()
}
override fun onPlayEnd() {
Log.d("AlarmLogActivity", "播放结束")
stopProgressUpdate()
hideControls()
}
override fun isPlaying(p0: Boolean) {
isPlaying = p0
if (p0) {
startProgressUpdate()
// 视频开始播放时,如果控制栏可见,启动自动隐藏计时器
if (isControlsVisible) {
resetHideTimer()
}
} else {
stopProgressUpdate()
// 视频暂停时,取消自动隐藏计时器,保持控制栏显示
hideHandler.removeCallbacks(hideRunnable)
showControls()
}
}
fun onRepeatModeChanged(p0: Int) {
}
})
.create()
exoPlayerManager.startPlayer()
// 保持当前的静音状态
if (isMuted) {
try {
val method = exoPlayerManager.javaClass.getMethod("getPlayer")
val player = method.invoke(exoPlayerManager) as? SimpleExoPlayer
player?.volume = 0f
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun fetchAlarmList() { private fun fetchAlarmList() {
val currentDeviceId = deviceId ?: return val currentDeviceId = deviceId ?: return
@@ -328,226 +203,34 @@ class AlarmLogActivity : AppCompatActivity() {
binding.tvDate.text = "${month}${day}" binding.tvDate.text = "${month}${day}"
} }
private fun showControls() {
isControlsVisible = true
binding.functionBar.visibility = View.VISIBLE
binding.muteBar.visibility = View.VISIBLE
binding.playBar.visibility = View.VISIBLE
binding.progressBar.visibility = View.VISIBLE
// 只有在视频播放时才启动自动隐藏计时器
if (isPlaying) {
resetHideTimer()
}
}
private fun hideControls() {
isControlsVisible = false
binding.functionBar.visibility = View.GONE
binding.muteBar.visibility = View.GONE
binding.playBar.visibility = View.GONE
binding.progressBar.visibility = View.GONE
}
private fun toggleControls() {
if (isControlsVisible) {
hideControls()
} else {
showControls()
}
}
private fun resetHideTimer() {
hideHandler.removeCallbacks(hideRunnable)
// 只有在视频播放时才启动自动隐藏计时器
if (isPlaying) {
hideHandler.postDelayed(hideRunnable, hideDelay)
}
}
private fun initListener() { private fun initListener() {
// 点击视频区域切换控制栏显示/隐藏
binding.exoPlayContextId.setOnClickListener {
toggleControls()
}
// 控制栏上的按钮点击时重置隐藏计时器
binding.ivBack.setOnClickListener {
resetHideTimer()
finish()
}
binding.expandImg.setOnClickListener { binding.expandImg.setOnClickListener {
showDatePicker() showDatePicker()
} }
binding.ivMute.setOnClickListener {
resetHideTimer()
toggleMute()
}
binding.ivDownload.setOnClickListener { binding.ivDownload.setOnClickListener {
resetHideTimer() Toast.makeText(this,"下载视频",Toast.LENGTH_SHORT).show()
downloadVideo()
} }
binding.ivDelete.setOnClickListener { binding.ivDelete.setOnClickListener {
resetHideTimer()
deleteVideo()
} }
binding.ivMute.setOnClickListener {
binding.ivFullScreen.setOnClickListener { isMute = !isMute
resetHideTimer() if (isMute) {
toggleFullScreen() audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0)
} binding.ivMute.setImageResource(
R.drawable.mute
binding.ivPlay.setOnClickListener { )
resetHideTimer()
togglePlay()
}
binding.seekBar.setOnSeekBarChangeListener(object : android.widget.SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) {
if (fromUser) {
resetHideTimer()
exoPlayerManager.seekTo(progress.toLong() * 1000)
}
}
override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) {
resetHideTimer()
}
override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) {
}
})
}
private fun toggleMute() {
isMuted = !isMuted
try {
// 通过反射获取底层的 ExoPlayer 实例
val method = exoPlayerManager.javaClass.getMethod("getPlayer")
val player = method.invoke(exoPlayerManager) as? SimpleExoPlayer
if (isMuted) {
binding.ivMute.setImageResource(R.drawable.mute)
player?.volume = 0f
Toast.makeText(this, R.string.sound_off, Toast.LENGTH_SHORT).show()
} else { } else {
binding.ivMute.setImageResource(R.drawable.un_mute) audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) / 2, 0)
player?.volume = 1f binding.ivMute.setImageResource(
Toast.makeText(this, R.string.sound_on, Toast.LENGTH_SHORT).show() R.drawable.un_mute
)
} }
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "音量控制失败", Toast.LENGTH_SHORT).show()
}
}
private fun downloadVideo() {
Toast.makeText(this, R.string.download, Toast.LENGTH_SHORT).show()
}
private fun deleteVideo() {
Toast.makeText(this, R.string.delete, Toast.LENGTH_SHORT).show()
}
private fun updateProgress() {
val currentPosition = exoPlayerManager.getCurrentPosition()
totalDuration = exoPlayerManager.getDuration()
binding.tvCurrentTime.text = formatTime(currentPosition)
binding.tvTotalTime.text = formatTime(totalDuration)
if (totalDuration > 0) {
val progress = (currentPosition * 100 / totalDuration).toInt()
binding.seekBar.progress = progress
}
}
private fun formatTime(milliseconds: Long): String {
val seconds = (milliseconds / 1000).toInt()
val hours = seconds / 3600
val minutes = (seconds % 3600) / 60
val secs = seconds % 60
return if (hours > 0) {
String.format("%02d:%02d:%02d", hours, minutes, secs)
} else {
String.format("%02d:%02d", minutes, secs)
}
}
private fun startProgressUpdate() {
stopProgressUpdate()
progressHandler.post(progressRunnable)
}
private fun stopProgressUpdate() {
progressHandler.removeCallbacks(progressRunnable)
}
private fun toggleFullScreen() {
isFullScreen = !isFullScreen
if (isFullScreen) {
binding.ivFullScreen.setImageResource(R.drawable.full_screen)
requestedOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
} else {
binding.ivFullScreen.setImageResource(R.drawable.full_size)
requestedOrientation = android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
private fun togglePlay() {
isPlaying = !isPlaying
if (isPlaying) {
binding.ivPlay.setImageResource(R.drawable.zt)
exoPlayerManager.startPlayer()
} else {
binding.ivPlay.setImageResource(R.drawable.start)
exoPlayerManager.onPause()
}
}
override fun onResume() {
super.onResume()
exoPlayerManager.onResume()
}
override fun onPause() {
super.onPause()
exoPlayerManager.onPause()
}
override fun onDestroy() {
super.onDestroy()
exoPlayerManager.onDestroy()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
// 进入横屏(全屏)模式
binding.titleLayout.visibility = View.GONE
binding.alarmListRecyclerView.visibility = View.GONE
val params = binding.exoPlayContextId.layoutParams
params.width = LinearLayout.LayoutParams.MATCH_PARENT
params.height = LinearLayout.LayoutParams.MATCH_PARENT
binding.exoPlayContextId.layoutParams = params
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
// 进入竖屏(非全屏)模式
binding.titleLayout.visibility = View.VISIBLE
binding.alarmListRecyclerView.visibility = View.VISIBLE
val params = binding.exoPlayContextId.layoutParams
params.width = LinearLayout.LayoutParams.MATCH_PARENT
params.height = dpToPx(280)
binding.exoPlayContextId.layoutParams = params
} }
exoPlayerManager.onConfigurationChanged(newConfig)
} }
private fun showDatePicker() { private fun showDatePicker() {
@@ -568,4 +251,21 @@ class AlarmLogActivity : AppCompatActivity() {
.build() .build()
timePicker.show() timePicker.show()
} }
override fun onPause() {
super.onPause()
binding.exoPlayContextId.onVideoPause()
}
override fun onResume() {
super.onResume()
binding.exoPlayContextId.onVideoResume()
}
override fun onDestroy() {
super.onDestroy()
orientationUtils?.releaseListener()
binding.exoPlayContextId.release()
}
} }

View File

@@ -0,0 +1,62 @@
package com.example.defenseapplication.view
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
class MyGSYVideoPlayer @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null
) : StandardGSYVideoPlayer(context, attrs) {
/** 控制栏显示/隐藏监听 */
var onControlVisibilityChanged: ((Boolean) -> Unit)? = null
// 播放中显示控制栏
override fun changeUiToPlayingShow() {
super.changeUiToPlayingShow()
Log.e("GSY_TEST", "通知显示按钮")
onControlVisibilityChanged?.invoke(true)
}
// 暂停中显示控制栏
override fun changeUiToPauseShow() {
super.changeUiToPauseShow()
onControlVisibilityChanged?.invoke(true)
}
// 播放中清除控制栏
override fun changeUiToPlayingClear() {
super.changeUiToPlayingClear()
Log.e("GSY_TEST", "changeUiToPlayingClear")
onControlVisibilityChanged?.invoke(false)
}
// 暂停中清除控制栏
override fun changeUiToPauseClear() {
super.changeUiToPauseClear()
onControlVisibilityChanged?.invoke(false)
}
// 完成播放
override fun changeUiToCompleteClear() {
super.changeUiToCompleteClear()
onControlVisibilityChanged?.invoke(false)
}
override fun hideAllWidget() {
Log.e("GSY_TEST", "通知隐藏按钮")
super.hideAllWidget()
onControlVisibilityChanged?.invoke(false)
}
override fun changeUiToNormal() {
Log.e("GSY_TEST", "changeUiToNormal")
super.changeUiToNormal()
}
override fun changeUiToPreparingShow() {
Log.e("GSY_TEST", "changeUiToPreparingShow")
super.changeUiToPreparingShow()
}
}

View File

@@ -0,0 +1,31 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景 -->
<item android:id="@android:id/background">
<shape>
<corners android:radius="2dp"/>
<solid android:color="#333333"/>
</shape>
</item>
<!-- 缓冲 -->
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="2dp"/>
<solid android:color="#888888"/>
</shape>
</clip>
</item>
<!-- 播放进度 -->
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="2dp"/>
<solid android:color="#09C2BD"/>
</shape>
</clip>
</item>
</layer-list>

View File

@@ -0,0 +1,11 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="12dp"
android:height="12dp"/>
<solid android:color="#09C2BD"/>
<corners android:radius="6dp"/>
</shape>

View File

@@ -2,10 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main" android:id="@+id/main"
android:layout_marginTop="30dp"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical"> android:orientation="vertical">
<!-- 视频播放区域和悬浮按钮 --> <!-- 视频播放区域和悬浮按钮 -->
@@ -14,137 +12,51 @@
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<!-- 视频播放区域 --> <!-- 视频播放区域 -->
<chuangyuan.ycj.videolibrary.widget.VideoPlayerView <com.example.defenseapplication.view.MyGSYVideoPlayer
android:layout_marginTop="40dp"
android:id="@+id/exoPlayContextId" android:id="@+id/exoPlayContextId"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="280dp" android:layout_height="280dp"/>
android:background="@android:color/transparent"
app:use_controller="false"/>
<!-- 自定义顶部栏 - 返回、下载、删除在同一行 --> <!-- 右上侧按钮层 -->
<LinearLayout <LinearLayout
android:id="@+id/functionBar" android:layout_marginTop="50dp"
android:layout_width="match_parent" android:id="@+id/rightMenu"
android:layout_height="44dp" android:layout_width="wrap_content"
android:layout_gravity="top" android:layout_height="wrap_content"
android:background="@android:color/transparent" android:layout_gravity="end"
android:gravity="center_vertical" android:layout_marginEnd="15dp"
android:visibility="visible"> android:orientation="horizontal">
<ImageView
android:id="@+id/ivBack"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="8dp"
android:src="@drawable/back_white"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"/>
<ImageView <ImageView
android:id="@+id/ivDownload" android:id="@+id/ivDownload"
android:layout_width="40dp" android:layout_width="24dp"
android:layout_height="40dp" android:layout_height="24dp"
android:padding="8dp"
android:src="@drawable/download"/> android:src="@drawable/download"/>
<ImageView <ImageView
android:id="@+id/ivDelete" android:id="@+id/ivDelete"
android:layout_width="40dp" android:layout_width="24dp"
android:layout_height="40dp" android:layout_height="24dp"
android:layout_marginStart="12dp" android:layout_marginStart="20dp"
android:padding="8dp"
android:src="@drawable/delete"/> android:src="@drawable/delete"/>
</LinearLayout> </LinearLayout>
<!-- 右下侧按钮层 -->
<!-- 右下角按钮区域 - 静音、全屏按钮 -->
<LinearLayout <LinearLayout
android:id="@+id/muteBar" android:id="@+id/rightDow"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="end|bottom"
android:layout_marginEnd="6dp" android:layout_marginEnd="15dp">
android:orientation="vertical"
android:gravity="center">
<ImageView <ImageView
android:id="@+id/ivMute" android:id="@+id/ivMute"
android:layout_width="40dp" android:layout_width="24dp"
android:layout_height="40dp" android:layout_height="24dp"
android:padding="8dp" android:layout_marginBottom="60dp"
android:src="@drawable/un_mute"/> android:src="@drawable/un_mute"/>
<ImageView
android:id="@+id/ivFullScreen"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/full_size"/>
</LinearLayout>
<!-- 左下角播放按钮 -->
<LinearLayout
android:id="@+id/playBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start">
<ImageView
android:id="@+id/ivPlay"
android:layout_width="48dp"
android:layout_height="48dp"
android:padding="8dp"
android:src="@drawable/zt"/>
</LinearLayout>
<!-- 底部进度条和时间区域 -->
<LinearLayout
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="44dp"
android:layout_gravity="bottom"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:paddingLeft="70dp"
android:paddingRight="70dp">
<!-- 当前时间 -->
<TextView
android:id="@+id/tvCurrentTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textColor="#FFFFFF"
android:textSize="14sp"/>
<!-- 进度条 -->
<SeekBar
android:id="@+id/seekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/custom_seekbar"
android:thumb="@drawable/custom_thumb"/>
<!-- 总时长 -->
<TextView
android:id="@+id/tvTotalTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textColor="#FFFFFF"
android:textSize="14sp"/>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>