Adding a fullscreen button to your video player is a crucial feature for user experience, especially in media-rich Android applications. In this comprehensive tutorial, we’ll walk through adding a custom fullscreen button to a media3.PlayerView in Android using Jetpack Media3 (successor to ExoPlayer). We’ll also explain how to show/hide controls based on playback state, and provide complete XML and Kotlin examples.
Table of Contents
- Why Customize PlayerView?
- Prerequisites
- Step 1: Setup PlayerView in Layout
- Step 2: Create a Custom Controller Layout
- Step 3: Add Fullscreen Button
- Step 4: Handle Fullscreen Logic in Kotlin
- Step 5: Show/Hide Controls Based on Playback State
- Optional: Vector Drawable for Fullscreen Icon
Why Customize PlayerView?
The PlayerView component in Jetpack Media3 provides a flexible, styled video playback interface. However, the default layout may not include certain controls like fullscreen toggle, and behaviors like hiding controls when paused may require customization.
Benefits:
- Better UX with fullscreen toggle
- Responsive control behavior
- Branding flexibility
Prerequisites
Add the required Media3 dependencies in your build.gradle:
implementation 'androidx.media3:media3-exoplayer:1.3.0'
implementation 'androidx.media3:media3-ui:1.3.0'
Step 1: Setup PlayerView in Layout
In your layout file (e.g. activity_main.xml):
<androidx.media3.ui.PlayerView
android:id="@+id/player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:controller_layout_id="@layout/custom_player_controls"
app:show_buffering="never"
app:use_artwork="true" />
The
controller_layout_idpoints to a custom layout we’ll define next.
Step 2: Create a Custom Controller Layout
Create a new file res/layout/custom_player_controls.xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="horizontal"
android:padding="8dp">
<!-- Default Play/Pause -->
<ImageButton
android:id="@+id/exo_play"
style="@style/ExoMediaButton.Play" />
<ImageButton
android:id="@+id/exo_pause"
style="@style/ExoMediaButton.Pause"
android:visibility="gone" />
<!-- Fullscreen Toggle -->
<ImageButton
android:id="@+id/fullscreen_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_fullscreen_expand"
android:layout_gravity="end"
android:contentDescription="Fullscreen Toggle" />
</LinearLayout>
</FrameLayout>
Step 3: Add Fullscreen Button Logic
In your MainActivity.kt or VideoPlayerActivity.kt:
class MainActivity : AppCompatActivity() {
private lateinit var playerView: PlayerView
private lateinit var fullscreenButton: ImageButton
private var isFullscreen = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
playerView = findViewById(R.id.player_view)
val player = ExoPlayer.Builder(this).build()
playerView.player = player
fullscreenButton = playerView.findViewById(R.id.fullscreen_button)
fullscreenButton.setOnClickListener { toggleFullscreen() }
val mediaItem = MediaItem.fromUri("https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4")
player.setMediaItem(mediaItem)
player.prepare()
player.play()
}
private fun toggleFullscreen() {
isFullscreen = !isFullscreen
if (isFullscreen) {
supportActionBar?.hide()
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
} else {
supportActionBar?.show()
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}
}
}
Step 4: Show/Hide Controls Based on Playback State
To control when the UI controls are visible:
player.addListener(object : Player.Listener {
override fun onPlaybackStateChanged(state: Int) {
when (state) {
Player.STATE_READY -> {
playerView.useController = player.isPlaying
}
Player.STATE_ENDED, Player.STATE_IDLE -> {
playerView.useController = false
}
}
}
})
Optional: Vector Drawable for Fullscreen Icon
Save this in res/drawable/ic_fullscreen_expand.xml:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M7,14H5v5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-5c0,-0.55 0.45,-1 1,-1h2c0.55,0 1,0.45 1,1S7.55,14 7,14zM7,10c-0.55,0 -1,-0.45 -1,-1V5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4c0,0.55 -0.45,1 -1,1zM19,10c-0.55,0 -1,-0.45 -1,-1V5c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4c0,0.55 -0.45,1 -1,1zM19,14c-0.55,0 -1,0.45 -1,1v5c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-5c0,-0.55 -0.45,-1 -1,-1z" />
</vector>
Conclusion
With just a few lines of code and a custom controller layout, you can create a robust video playback experience using media3.PlayerView, including a custom fullscreen button, dynamic control visibility, and custom styling. This approach is clean, user-friendly, and adaptable for streaming apps, media players, and educational content apps.


