【问题标题】:Add button for full screen video with exo player使用 exo 播放器添加全屏视频按钮
【发布时间】:2016-08-27 16:13:12
【问题描述】:

我正在使用 exoPlayer 将视频流式传输到我的应用程序,到目前为止工作正常。我现在想做的是添加一些额外的功能,例如右下角的按钮以充当“全屏按钮”。

但是有两个问题。首先是ExoPlayer 似乎没有提供任何 Control 类,因此您可以简单地添加一个按钮并覆盖其功能。 我猜我必须在视频顶部显示该按钮,因此我可能必须将两者都包装在FrameLayout 中并为按钮添加重力=底部,还是有其他方法?

第二个问题是:如果用户点击全屏按钮,接下来我该怎么做?添加另一个Fragment 全屏视频视图?但是如何从用户单击按钮时开始播放视频而不是从头开始?我在 exoPlayer 中找不到任何与从特定时间开始相关的内容。

【问题讨论】:

    标签: android exoplayer


    【解决方案1】:

    如果您使用SimpleExoPlayerView,有一种方法可以自定义播放器的视图,尤其是控件的视图。查看SimpleExoPlayerView的文档:

    属性

    当在布局 XML 文件中使用时,可以在 SimpleExoPlayerView 上设置以下属性: ...

    controller_layout_id - 指定要由子 PlaybackControlView 膨胀的布局资源的 id。详情见下文。

    • 对应方法:无

    • 默认值:R.id.exo_playback_control_view

    ...

    所以基本上你可以为控制器提供你自己的布局文件(你可以复制文档中提到的 exo_playback_control_view 布局,这是默认布局,并根据需要进行自定义。请注意,您需要为现有控件提供相同的视图 ID(因此最好实际复制),如 PlaybackControlView 的文档中所述:

    覆盖布局文件

    要在整个应用程序中自定义 PlaybackControlView 的布局,或者仅针对某些配置,您可以在应用程序 res/layout* 目录中定义 exo_playback_control_view.xml 布局文件。这些布局将覆盖 ExoPlayer 库提供的布局,并将被扩充以供PlaybackControlView 使用。视图通过查找以下 id 来识别并绑定其子项:

    • 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() 并将其作为 Intent 额外传递给新的全屏 Activity/Fragment。然后,在加载视频的全屏活动/片段中,提取 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>
    

    【讨论】:

    • 很好的答案,我试图获取 PlaybackControlView,然后使用该视图获取每个按钮,这引发了一个空引用,我尝试了你的答案并且工作正常,而不是使用 PlaybackControlView 我使用了主活动视图以查找按钮。
    【解决方案2】:

    在当前 Activity 之上添加一个新的全屏 Activity 并将播放位置传递给它的解决方案

    other answer 很棒,为您指明了正确的方向,但它只是理论上的,我在编写代码时仍然需要填补一些空白并解决一些问题。我会尝试补充它。

    首先将布局 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>
    

    请注意,您可以使用属性app:controller_layout_id 为不同的PlayerViews 设置不同的布局。如果您不想要播放器按钮,也可以删除它们。这在文档中有所介绍: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>
    

    就是这样!希望对您有所帮助。

    当前活动中的替代解决方案

    上述解决方案效果很好,而且很简单。但是,它需要重新下载您已经下载的视频片段,这会中断播放:/

    我一直在尝试通过关注this directions 来避免这种情况。这是我目前所拥有的:

    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" 设置为您的 Activity。

    【讨论】:

    • 谢谢阿尔伯特。你让我免于做很多工作。非常感谢!!!
    【解决方案3】:

    我为此做了一个 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

    【讨论】:

      猜你喜欢
      • 2014-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      • 2015-05-20
      • 2011-10-01
      相关资源
      最近更新 更多