如何将 Exoplayer 的控件放置在 PlayerView 之外
How to place the controls of an Exoplayer outside of the PlayerView
我正在使用 Exoplayer2 在我的应用程序中显示视频。我需要控件始终可见。我可以通过设置 app:show_timeout="0"
来存档它。但是当控件始终可见时,它们会在 PlayerView 中占用 space。
我想显示 PlayerView 下方的控件,以便我始终可以显示整个视频。
这是我的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MediaPlayerActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:contentDescription="hf_hide_help">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true"
app:rewind_increment="0"
app:fastforward_increment="0"
app:repeat_toggle_modes="none"
app:show_timeout="0"
/>
</RelativeLayout>
这是我的 exo_player_control_view.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:background="#CC000000"
android:orientation="vertical">
<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_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
<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"/>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="26dp"/>
<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>
这是它的样子:
这就是它的样子,当我点击播放器时 - 控件隐藏了。
您的用例不是通用的,ExoPlayer 不支持它。在您的情况下,您需要禁用默认控件并在播放器视图下方添加您自己的自定义控件。构建布局并向其添加操作将非常容易。
更新:(回答 Gyan 关于时间栏的评论)
两种方法:(我解释了这些策略,因为 SO 和其他站点上已经提供了数以千计的代码示例)
1 - 我的建议非常适合我的需要:
覆盖 exoplayer 的默认控件视图(在布局文件夹中具有相同名称的视图就可以完成这项工作)。然后删除您在自定义视图中放下的所有不必要的按钮 但 只需将时间栏保持在覆盖视图中。通过这样做,所有出现/消失的行为都会自动保留时间栏,您将在自己的视图中看到其余的行为。如果您喜欢这种方法,则无需担心处理时间栏的问题。
2- 以防万一您坚持将时间栏放在播放器下方 window,您需要的是水平进度条(请注意,您可以自定义进度视图的外观以匹配您的设计) .然后在 exoplayer 上写一个监听器来记录时间(过去/剩余/当前播放时间)并根据它们计算和更新进度条。请记住,进度条需要可触摸(并且可聚焦),以便用户可以来回拖动它。然后,当用户拖动进度时,您需要重新计算播放时间。 (显然有很多编码和计算,但几乎是简单的事情)。希望对你有效。如果您需要代码示例,请告诉我。
我在视频上有 play/pause 按钮,在视频下方有其他控件,这里是操作方法。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.exoplayer2.ui.StyledPlayerView
android:id="@+id/video_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:auto_show="true"
app:controller_layout_id="@layout/custom_exo_overlay_controller_view"
app:layout_constraintBottom_toTopOf="@id/exoBottomControls"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
app:repeat_toggle_modes="none"
app:resize_mode="fixed_width"
app:surface_type="surface_view"
app:use_controller="true" />
<com.google.android.exoplayer2.ui.StyledPlayerControlView
android:id="@+id/exoBottomControls"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:controller_layout_id="@layout/custom_exo_bottom_controller_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/video_view"
app:show_timeout="0" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里我同时使用 use_controller=true 和 StyledPlayerControlView。
应该出现在视频中的控件应该放在你的 custom_exo_overlay_controller_view 中,其他应该出现在视频下面的视图应该放在 custom_exo_bottom_controller_view.
中
这里是custom_exo_overlay_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="@id/exo_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
和custom_exo_bottom_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@id/exo_controls_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/color_secondary_black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:textColor="@color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/exo_progress"
app:layout_constraintStart_toStartOf="parent"
tools:text="00:50" />
<TextView
android:id="@id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="@color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/exo_progress"
app:layout_constraintEnd_toEndOf="parent"
tools:text="02:50" />
<ImageButton
android:id="@+id/exo_sound_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_sound_on"
app:layout_constraintBottom_toTopOf="@+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="@+id/exo_sound_off"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_sound_off"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_height="52dp"
app:buffered_color="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:played_color="@android:color/transparent"
app:scrubber_drawable="@drawable/ic_scrubber"
app:touch_target_height="52dp"
app:unplayed_color="@android:color/transparent" />
</androidx.constraintlayout.widget.ConstraintLayout>
你也应该像这样分配你的控制器
player = ExoPlayer.Builder(binding.root.context).build().also {
binding.videoView.player = it
binding.exoBottomControls.player = it
val mediaItem = MediaItem.fromUri(video.url)
it.setMediaItem(mediaItem)
it.seekTo(video.currentWindow, video.playbackPosition)
it.prepare()
}
我正在使用 Exoplayer2 在我的应用程序中显示视频。我需要控件始终可见。我可以通过设置 app:show_timeout="0"
来存档它。但是当控件始终可见时,它们会在 PlayerView 中占用 space。
我想显示 PlayerView 下方的控件,以便我始终可以显示整个视频。
这是我的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MediaPlayerActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:contentDescription="hf_hide_help">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/playerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="true"
app:rewind_increment="0"
app:fastforward_increment="0"
app:repeat_toggle_modes="none"
app:show_timeout="0"
/>
</RelativeLayout>
这是我的 exo_player_control_view.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:background="#CC000000"
android:orientation="vertical">
<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_play"
style="@style/ExoMediaButton.Play"/>
<ImageButton android:id="@id/exo_pause"
style="@style/ExoMediaButton.Pause"/>
<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"/>
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="26dp"/>
<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>
这是它的样子:
这就是它的样子,当我点击播放器时 - 控件隐藏了。
您的用例不是通用的,ExoPlayer 不支持它。在您的情况下,您需要禁用默认控件并在播放器视图下方添加您自己的自定义控件。构建布局并向其添加操作将非常容易。
更新:(回答 Gyan 关于时间栏的评论)
两种方法:(我解释了这些策略,因为 SO 和其他站点上已经提供了数以千计的代码示例)
1 - 我的建议非常适合我的需要: 覆盖 exoplayer 的默认控件视图(在布局文件夹中具有相同名称的视图就可以完成这项工作)。然后删除您在自定义视图中放下的所有不必要的按钮 但 只需将时间栏保持在覆盖视图中。通过这样做,所有出现/消失的行为都会自动保留时间栏,您将在自己的视图中看到其余的行为。如果您喜欢这种方法,则无需担心处理时间栏的问题。
2- 以防万一您坚持将时间栏放在播放器下方 window,您需要的是水平进度条(请注意,您可以自定义进度视图的外观以匹配您的设计) .然后在 exoplayer 上写一个监听器来记录时间(过去/剩余/当前播放时间)并根据它们计算和更新进度条。请记住,进度条需要可触摸(并且可聚焦),以便用户可以来回拖动它。然后,当用户拖动进度时,您需要重新计算播放时间。 (显然有很多编码和计算,但几乎是简单的事情)。希望对你有效。如果您需要代码示例,请告诉我。
我在视频上有 play/pause 按钮,在视频下方有其他控件,这里是操作方法。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.exoplayer2.ui.StyledPlayerView
android:id="@+id/video_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_gravity="center"
app:auto_show="true"
app:controller_layout_id="@layout/custom_exo_overlay_controller_view"
app:layout_constraintBottom_toTopOf="@id/exoBottomControls"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0"
app:repeat_toggle_modes="none"
app:resize_mode="fixed_width"
app:surface_type="surface_view"
app:use_controller="true" />
<com.google.android.exoplayer2.ui.StyledPlayerControlView
android:id="@+id/exoBottomControls"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:controller_layout_id="@layout/custom_exo_bottom_controller_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/video_view"
app:show_timeout="0" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里我同时使用 use_controller=true 和 StyledPlayerControlView。
应该出现在视频中的控件应该放在你的 custom_exo_overlay_controller_view 中,其他应该出现在视频下面的视图应该放在 custom_exo_bottom_controller_view.
中这里是custom_exo_overlay_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageButton
android:id="@id/exo_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
和custom_exo_bottom_controller_view.xml
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<View
android:id="@id/exo_controls_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/color_secondary_black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:textColor="@color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/exo_progress"
app:layout_constraintStart_toStartOf="parent"
tools:text="00:50" />
<TextView
android:id="@id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="@color/colorWhite"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/exo_progress"
app:layout_constraintEnd_toEndOf="parent"
tools:text="02:50" />
<ImageButton
android:id="@+id/exo_sound_on"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_sound_on"
app:layout_constraintBottom_toTopOf="@+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="@+id/exo_sound_off"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="32dp"
android:background="@android:color/transparent"
android:src="@drawable/ic_sound_off"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/exo_duration"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.exoplayer2.ui.DefaultTimeBar
android:id="@id/exo_progress"
android:layout_width="0dp"
android:layout_height="52dp"
app:buffered_color="@android:color/transparent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:played_color="@android:color/transparent"
app:scrubber_drawable="@drawable/ic_scrubber"
app:touch_target_height="52dp"
app:unplayed_color="@android:color/transparent" />
</androidx.constraintlayout.widget.ConstraintLayout>
你也应该像这样分配你的控制器
player = ExoPlayer.Builder(binding.root.context).build().also {
binding.videoView.player = it
binding.exoBottomControls.player = it
val mediaItem = MediaItem.fromUri(video.url)
it.setMediaItem(mediaItem)
it.seekTo(video.currentWindow, video.playbackPosition)
it.prepare()
}