视频组件自定义

This commit is contained in:
2026-06-03 17:17:44 +08:00
parent b1aa844ff6
commit 60ecdf140b
17 changed files with 626 additions and 64 deletions

View File

@@ -6,7 +6,10 @@ import android.os.Bundle
import android.util.Log
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 androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
@@ -16,6 +19,7 @@ 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.adapter.AlarmLogAdapter
import com.example.defenseapplication.bean.DeviceAlarmBean
@@ -24,6 +28,8 @@ import com.example.defenseapplication.databinding.AlarmLogActivityBinding
import com.example.defenseapplication.utils.RetrofitNetwork
import com.gyf.immersionbar.ImmersionBar
import kotlinx.coroutines.launch
import android.os.Handler
import android.os.Looper
import java.text.SimpleDateFormat
import java.util.*
import com.bigkoo.pickerview.builder.TimePickerBuilder
@@ -38,23 +44,38 @@ class AlarmLogActivity : AppCompatActivity() {
private val originalAlarmList = mutableListOf<DeviceAlarmBean>()
private lateinit var exoPlayerManager: ExoUserPlayer
private var isMuted = false
private var isVoiceEnabled = false
private var isLightOn = false
private var isPlaying = false
private var deviceId: String? = null
private var isLoading = false
private var fromLiveVideo = false
private var isFullScreen = false
private var isControlsVisible = false
private val hideDelay = 5000L // 5秒自动隐藏
private val hideHandler = Handler(Looper.getMainLooper())
private val hideRunnable = Runnable {
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?) {
super.onCreate(savedInstanceState)
binding = AlarmLogActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
ImmersionBar.with(this)
.statusBarDarkFont(true)
.transparentStatusBar()
.init()
window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
deviceId = intent.getStringExtra("device_id")
fromLiveVideo = intent.getBooleanExtra("from_live_video", false)
@@ -68,14 +89,14 @@ class AlarmLogActivity : AppCompatActivity() {
fetchAlarmList()
initListener()
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (exoPlayerManager.onBackPressed()) {
return
}
finish()
}
})
// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// if (exoPlayerManager.onBackPressed()) {
// return
// }
// finish()
// }
// })
}
private fun dpToPx(dp: Int): Int {
@@ -97,6 +118,9 @@ class AlarmLogActivity : AppCompatActivity() {
.addVideoInfoListener(object : VideoInfoListener {
override fun onPlayStart(p0: Long) {
Log.d("AlarmLogActivity", "视频开始播放")
startProgressUpdate()
showControls()
resetHideTimer()
}
override fun onLoadingChanged() {
@@ -108,9 +132,24 @@ class AlarmLogActivity : AppCompatActivity() {
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) {
@@ -121,25 +160,11 @@ class AlarmLogActivity : AppCompatActivity() {
exoPlayerManager.startPlayer()
setupControllerVisibilityListener()
updateProgress()
}
private fun setupControllerVisibilityListener() {
binding.exoPlayContextId.post {
val playerView = findPlayerView(binding.exoPlayContextId)
playerView?.setControllerVisibilityListener { visibility ->
if (visibility == View.VISIBLE) {
binding.ivBack.visibility = View.VISIBLE
binding.muteBar.visibility = View.VISIBLE
binding.ivDownload.visibility = View.VISIBLE
binding.ivDelete.visibility = View.VISIBLE
} else {
binding.ivBack.visibility = View.GONE
binding.muteBar.visibility = View.GONE
binding.ivDownload.visibility = View.GONE
binding.ivDelete.visibility = View.GONE
}
}
}
// 移除原来的控制器监听器,改为自定义控制逻辑
}
private fun findPlayerView(view: View): PlayerView? {
@@ -169,16 +194,19 @@ class AlarmLogActivity : AppCompatActivity() {
binding.alarmListRecyclerView.layoutManager = LinearLayoutManager(this)
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() {
@@ -190,25 +218,51 @@ class AlarmLogActivity : AppCompatActivity() {
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() {
val currentDeviceId = deviceId ?: return
Log.d("AlarmLogActivity", "=== 开始请求设备告警日志 ===")
Log.d("AlarmLogActivity", "请求参数 - deviceId: $currentDeviceId")
isLoading = true
lifecycleScope.launch {
try {
@@ -218,16 +272,16 @@ class AlarmLogActivity : AppCompatActivity() {
pageNum = "1",
pageSize = "10"
)
Log.d("AlarmLogActivity", "请求完成")
Log.d("AlarmLogActivity", "总条数: ${response.total}, 当前页数据条数: ${response.rows.size}")
val newData = response.rows
originalAlarmList.clear()
originalAlarmList.addAll(newData)
filterAlarmListByDate(calendar)
Log.d("AlarmLogActivity", "已获取 ${newData.size} 条数据")
} catch (e: Exception) {
Log.e("AlarmLogActivity", "请求异常: ${e.message}", e)
} finally {
@@ -236,11 +290,21 @@ class AlarmLogActivity : AppCompatActivity() {
}
}
}
private fun showEmptyView() {
binding.alarmListRecyclerView.visibility = View.GONE
binding.emptyLayout.visibility = View.VISIBLE
}
private fun hideEmptyView() {
binding.alarmListRecyclerView.visibility = View.VISIBLE
binding.emptyLayout.visibility = View.GONE
}
private fun filterAlarmListByDate(selectedDate: Calendar) {
val dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
val selectedDateStr = dateFormat.format(selectedDate.time)
alarmList.clear()
originalAlarmList.forEach { item ->
val itemDateStr = item.createTime.substring(0, 10)
@@ -250,6 +314,12 @@ class AlarmLogActivity : AppCompatActivity() {
}
adapter?.notifyDataSetChanged()
Log.d("AlarmLogActivity", "日期筛选完成,筛选后条数: ${alarmList.size}")
if (alarmList.isEmpty()) {
showEmptyView()
} else {
hideEmptyView()
}
}
private fun updateDateDisplay() {
@@ -258,8 +328,51 @@ class AlarmLogActivity : AppCompatActivity() {
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() {
// 点击视频区域切换控制栏显示/隐藏
binding.exoPlayContextId.setOnClickListener {
toggleControls()
}
// 控制栏上的按钮点击时重置隐藏计时器
binding.ivBack.setOnClickListener {
resetHideTimer()
finish()
}
@@ -268,26 +381,66 @@ class AlarmLogActivity : AppCompatActivity() {
}
binding.ivMute.setOnClickListener {
resetHideTimer()
toggleMute()
}
binding.ivDownload.setOnClickListener {
resetHideTimer()
downloadVideo()
}
binding.ivDelete.setOnClickListener {
resetHideTimer()
deleteVideo()
}
binding.ivFullScreen.setOnClickListener {
resetHideTimer()
toggleFullScreen()
}
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
if (isMuted) {
binding.ivMute.setImageResource(R.drawable.mute)
Toast.makeText(this, R.string.sound_off, Toast.LENGTH_SHORT).show()
} else {
binding.ivMute.setImageResource(R.drawable.voice)
Toast.makeText(this, R.string.sound_on, Toast.LENGTH_SHORT).show()
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 {
binding.ivMute.setImageResource(R.drawable.un_mute)
player?.volume = 1f
Toast.makeText(this, R.string.sound_on, Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
Toast.makeText(this, "音量控制失败", Toast.LENGTH_SHORT).show()
}
}
@@ -299,6 +452,63 @@ class AlarmLogActivity : AppCompatActivity() {
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()

View File

@@ -83,14 +83,14 @@ class LiveVideoActivity : BaseActivity() {
initAlarmRecyclerView()
initListener()
fetchAlarmList()
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (exoPlayerManager.onBackPressed()) {
return
}
finish()
}
})
// onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
// override fun handleOnBackPressed() {
// if (exoPlayerManager.onBackPressed()) {
// return
// }
// finish()
// }
// })
}
private fun initVideoPlayer() {

View File

@@ -230,8 +230,6 @@ interface ApiService {
@Query("deviceId") deviceId: String,
@Query("pageNum") pageNum: String="1",
@Query("pageSize") pageSize: String="10",
@Query("startTime") startTime: String? = null,
@Query("endTime") endTime: String? = null
): DeviceAlarmResponse