Добавить кнопку для полноэкранного видео с exo player

Я использую exoPlayer для потоковой передачи видео в свое приложение и пока работает нормально. Что я хотел бы сделать сейчас, так это добавить некоторые дополнительные функции, такие как кнопка в правом нижнем углу, которая действует как «полноэкранная кнопка».

Но есть две проблемы. Во-первых, ExoPlayer, по-видимому, не предоставляет никакого класса Control, поэтому вы можете просто добавить кнопку и переопределить ее функциональность. Я предполагаю, что мне нужно отобразить эту кнопку поверх видео, поэтому мне, возможно, придется обернуть обе в FrameLayout и добавить гравитацию = дно для кнопки или есть другой способ?

Вторая проблема: если пользователь нажимает кнопку полноэкранного режима, что мне делать дальше? Добавить еще Fragment с просмотром видео в полноэкранном режиме? Но как я могу запустить видео с того момента, когда пользователь нажал кнопку, а не запустить его с самого начала? Я не могу найти в exoPlayer ничего относительно запуска с определенного времени.


person Mes    schedule 02.05.2016    source источник


Ответы (3)


Если вы используете SimpleExoPlayerView, есть способ настроить вид Player, особенно вид Control. Проверьте документацию 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>
person dud3rino    schedule 22.12.2016
comment
Отличный ответ, я пытался получить PlaybackControlView, а затем с помощью этого представления получить каждую кнопку, и это выдало нулевую ссылку, я попытался с вашим ответом и работал нормально, вместо использования PlaybackControlView я использовал основное представление активности, чтобы найти кнопки. - person Kevin Ramirez Zavalza; 30.07.2018

Решение с новой полноэкранной активностью поверх текущей, передавая ей позицию воспроизведения

другой ответ отличный и указывает вам правильное направление, но он скорее теоретический, и мне все же пришлось заполнить некоторые пробелы и решить несколько вещей при написании кода. попробую дополнить.

Начните с копирования макета 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, используя атрибут app:controller_layout_id. Вы также можете удалить кнопки плеера, если они вам не нужны. Это описано в документации: https://exoplayer.dev/ui-components.html#overriding-layout-files

Как только у вас появится полноэкранная кнопка, установите для нее 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" в свою активность в манифесте.

person Albert Vila Calvo    schedule 03.10.2019
comment
спасибо Альберт. Вы избавили меня от большого количества работы. Большое спасибо!!! - person Pemba Tamang; 07.12.2019

Для этого я сделал функцию расширения 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

person Jéwôm'    schedule 29.02.2020