Добавить кнопку для полноэкранного видео с Exo Player
Я использую exoPlayer для потоковой передачи видео в мое приложение и пока работает нормально. Теперь я хотел бы добавить некоторые дополнительные функции, такие как кнопка в правом нижнем углу, чтобы она работала как "кнопка полного экрана".
Но есть две проблемы. Во-первых, это ExoPlayer
Похоже, он не предоставляет никакого класса Control, поэтому вы можете просто добавить кнопку и переопределить ее функциональность. Я предполагаю, что мне нужно отобразить эту кнопку в верхней части видео, чтобы мне пришлось обернуть оба в FrameLayout
и добавить гравитацию = основание для кнопки или есть другой способ?
Вторая проблема: если пользователь нажимает кнопку полноэкранного режима, что мне делать дальше? Добавить еще Fragment
с просмотром видео на весь экран? Но как я могу начать видео с того места, где он был, когда пользователь нажал на кнопку, а не запустить его с самого начала? Я не могу найти в exoPlayer ничего относительно начала с определенного времени.
4 ответа
Если вы используете SimpleExoPlayerView
Есть способ настроить вид проигрывателя, особенно вид элемента управления. Проверьте документацию SimpleExoPlayerView
:
Атрибуты
Следующие атрибуты могут быть установлены на
SimpleExoPlayerView
при использовании в файле макета XML:...controller_layout_id - Определяет идентификатор ресурса макета, который будет раздуваться дочерним элементом
PlaybackControlView
, Подробности смотрите ниже.
Соответствующий метод: нет
По умолчанию: R.id.exo_playback_control_view
...
Таким образом, в основном вы можете предоставить свой собственный файл макета для контроллера (вы можете скопировать макет exo_playback_control_view, упомянутый в документации, который является стандартным, и настроить его по своему усмотрению. Обратите внимание, что вам нужно будет предоставить те же идентификаторы вида для существующие элементы управления (так что лучше всего скопировать это), как указано в документах PlaybackControlView
:
Переопределение файла макета
Чтобы настроить макет
PlaybackControlView
во всем приложении или только для определенных конфигураций вы можете определять файлы макетов exo_playback_control_view.xml в каталогах res/layout* вашего приложения. Эти макеты будут иметь приоритет над макетом, предоставляемым библиотекой ExoPlayer, и будут раздуты для использованияPlaybackControlView
, Представление идентифицирует и связывает своих потомков, ища следующие идентификаторы:
exo_play - кнопка воспроизведения.
exo_pause - кнопка паузы.
exo_ffwd - кнопка быстрой перемотки вперед.
exo_rew - кнопка перемотки.
exo_prev - кнопка предыдущего трека.
exo_next - кнопка следующего трека.
exo_position - текстовое представление, отображающее текущую позицию воспроизведения.
exo_duration - текстовое представление, отображающее текущую продолжительность мультимедиа.
exo_progress - строка поиска, которая обновляется во время воспроизведения и позволяет искать.
Все дочерние представления являются необязательными и поэтому могут быть опущены, если не требуются, однако, если они определены, они должны иметь ожидаемый тип.
Ниже приведен индивидуальный макет с полноэкранной кнопкой. Вы получаете ссылку на кнопку view.findViewById(R.id.exo_fullscreen_button)
и приложить OnClickListener
на кнопку. внутри onClick()
Вы можете начать работу в полноэкранном режиме (вы можете определить ее в полноэкранном режиме в AndroidManifest.xml или программно) или показать другой фрагмент, который имеет SimpleExoPlayerView
занимая весь экран. Что касается вашего второго пункта, вы можете получить позицию воспроизведения следующим образом: playbackPosition = player.getCurrentPosition()
и передайте его новому полноэкранному действию / фрагменту как дополнительное намерение. Затем в этом полноэкранном разделе "Деятельность / фрагмент" вы загружаете видео и извлекаете playbackPosition
стоимость и вызов:
player.seekTo(playbackPosition);
player.setPlayWhenReady(true);
Вот файл макета элемента управления:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="4dp"
android:orientation="horizontal">
<ImageButton android:id="@id/exo_prev"
style="@style/ExoMediaButton.Previous"/>
<ImageButton android:id="@id/exo_rew"
style="@style/ExoMediaButton.Rewind"/>
<ImageButton android:id="@id/exo_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
<ImageButton android:id="@id/exo_ffwd"
style="@style/ExoMediaButton.FastForward"/>
<ImageButton android:id="@id/exo_next"
style="@style/ExoMediaButton.Next"/>
// This is the custom button
<ImageButton
android:id="@+id/exo_fullscreen_button"
style="@style/ExoMediaButton"
android:src="@drawable/ic_fullscreen"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView android:id="@id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
<SeekBar android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="32dp"
android:focusable="false"
style="?android:attr/progressBarStyleHorizontal"/>
<TextView android:id="@id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textStyle="bold"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:includeFontPadding="false"
android:textColor="#FFBEBEBE"/>
</LinearLayout>
</LinearLayout>
Решение с новым полноэкранным действием поверх текущего, передавая ему позицию воспроизведения
Другой ответ велик, и указывает на вас в правильном направлении, но это скорее теоретическая, и я все еще должен был заполнить некоторые пробелы и решить несколько вещей при написании кода. Постараюсь его дополнить.
Начните с копирования макета exo_playback_control_view.xml
из библиотеки ExoPlayer в res/layout
. Ссылка на файл: https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/res/layout/exo_playback_control_view.xml
Измените макет, чтобы добавить полноэкранную кнопку, которая может быть примерно такой:
<FrameLayout
android:id="@+id/exo_fullscreen_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end">
<ImageView
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:src="@drawable/ic_fullscreen_expand"
android:focusable="true" />
</FrameLayout>
Обратите внимание, что у вас могут быть разные макеты для разных PlayerView
s с помощью атрибута app:controller_layout_id
. Вы также можете удалить кнопки проигрывателя, если они вам не нужны. Это описано в документации: https://exoplayer.dev/ui-components.html.
Когда у вас есть кнопка полноэкранного режима, установите OnClickListener
в теме:
findViewById<View>(R.id.exo_fullscreen_button).setOnClickListener {
player.playWhenReady = false // pause current video if it's playing
startActivity(
FullScreenVideoActivity.newIntent(
context,
videoUrl,
player.currentPosition
)
)
}
Добавить FullScreenVideoActivity
:
private const val EXTRA_VIDEO_URL = "EXTRA_VIDEO_URL"
private const val EXTRA_PLAYBACK_POSITION_MS = "EXTRA_PLAYBACK_POSITION_MS"
private const val STATE_PLAYBACK_POSITION_MS = "STATE_PLAYBACK_POSITION_MS"
class FullScreenVideoActivity : AppCompatActivity() {
companion object {
fun newIntent(packageContext: Context, videoUrl: String, playbackPositionMs: Long): Intent {
val intent =
Intent(packageContext, FullScreenVideoActivity::class.java)
intent.putExtra(EXTRA_VIDEO_URL, videoUrl)
intent.putExtra(EXTRA_PLAYBACK_POSITION_MS, playbackPositionMs)
return intent
}
}
private lateinit var player: SimpleExoPlayer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_full_screen_video)
val videoUrl = intent.getStringExtra(EXTRA_VIDEO_URL)
var playbackPositionMs = intent.getLongExtra(EXTRA_PLAYBACK_POSITION_MS, 0)
if (savedInstanceState != null) {
// The user rotated the screen
playbackPositionMs = savedInstanceState.getLong(STATE_PLAYBACK_POSITION_MS)
}
findViewById<View>(R.id.exo_fullscreen_button).setOnClickListener {
finish()
}
val playerView: PlayerView = findViewById(R.id.player_view)
player = ExoPlayerFactory.newSimpleInstance(this)
val userAgent = Util.getUserAgent(this, getString(R.string.app_name))
val dataSourceFactory = DefaultDataSourceFactory(this, userAgent)
val mediaSource: MediaSource =
ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(videoUrl))
player.prepare(mediaSource)
player.seekTo(playbackPositionMs)
player.playWhenReady = true
playerView.player = player
}
override fun onPause() {
super.onPause()
player.playWhenReady = false
}
override fun onDestroy() {
super.onDestroy()
player.release()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putLong(STATE_PLAYBACK_POSITION_MS, player.currentPosition)
}
}
Добавьте активность в манифест:
<activity
android:name=".ui.FullScreenVideoActivity"
android:screenOrientation="landscape" <-- this is optional
android:theme="@style/AppTheme.NoActionBar.FullScreen" />
Наконец добавьте тему в styles.xml
:
<style name="AppTheme.NoActionBar.FullScreen">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
Вот и все! Надеюсь, это поможет.
Альтернативное решение в текущей деятельности
Вышеупомянутое решение отлично работает и тривиально. Однако это требует повторной загрузки фрагментов уже загруженного вами видео, что прерывает воспроизведение: /
Я пытался избежать этого, следуя этим указаниям. Вот что у меня есть на данный момент:
activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
val fullScreenPlayerView = PlayerView(context)
val dialog = object : Dialog(context!!, android.R.style.Theme_Black_NoTitleBar_Fullscreen) {
override fun onBackPressed() {
activity!!.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
PlayerView.switchTargetView(player, fullScreenPlayerView, playerView)
super.onBackPressed()
}
}
dialog.addContentView(
fullScreenPlayerView,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
dialog.show()
PlayerView.switchTargetView(player, playerView, fullScreenPlayerView)
Обратите внимание, что для этого вам нужно установить android:configChanges="orientation|screenSize|layoutDirection"
к вашей деятельности в манифесте.
Теперь вы можете отобразить полноэкранную кнопку, установив прослушиватель кликов наStyledPlayerView
:
videoPlayer.setFullscreenButtonClickListener {}
Где вид:
<com.google.android.exoplayer2.ui.StyledPlayerView/>
Я сделал для этого функцию расширения kotlin. Вам нужно два Exoplayer, и расширение будет переключаться между двумя игроками (полноэкранный и нормальный).
@SuppressLint("SourceLockedOrientationActivity")
fun SimpleExoPlayer.preparePlayer(playerView: PlayerView, playerViewFullscreen: PlayerView) {
(playerView.context as AppCompatActivity).apply {
val fullScreenButton: ImageView = playerView.findViewById(R.id.exo_fullscreen_icon)
val normalScreenButton: ImageView = playerViewFullscreen.findViewById(R.id.exo_fullscreen_icon)
fullScreenButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_fullscreen_open))
normalScreenButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_fullscreen_close))
fullScreenButton.setOnClickListener {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
supportActionBar?.hide()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
playerView.visibility = View.GONE
playerViewFullscreen.visibility = View.VISIBLE
PlayerView.switchTargetView(this@preparePlayer, playerView, playerViewFullscreen)
}
normalScreenButton.setOnClickListener {
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
supportActionBar?.show()
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
normalScreenButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_fullscreen_close))
playerView.visibility = View.VISIBLE
playerViewFullscreen.visibility = View.GONE
PlayerView.switchTargetView(this@preparePlayer, playerViewFullscreen, playerView)
}
playerView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIXED_HEIGHT
playerView.player = this@preparePlayer
}
}
fun SimpleExoPlayer.setSource(context: Context, url: String){
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(context, Util.getUserAgent(context, "app"))
val videoSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse(url))
this.prepare(videoSource)
}
Например, с этим макетом:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context=".MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerViewFullscreen"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Вы можете использовать это так:
class MainActivity : AppCompatActivity() {
private val player: SimpleExoPlayer by lazy { SimpleExoPlayer.Builder(applicationContext).build() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
player.preparePlayer(playerView, playerViewFullscreen)
player.setSource(applicationContext, "http://html5videoformatconverter.com/data/images/happyfit2.mp4")
player.playWhenReady = true
}
public override fun onPause() {
super.onPause()
player.playWhenReady = false
}
public override fun onDestroy() {
player.release()
super.onDestroy()
}
}
Если вы предпочитаете не копировать этот код, вы можете добавить эту библиотеку. То же самое: https://github.com/Norulab/android-exoplayer-fullscreen