Adding a Custom Fullscreen Button to Android media3.PlayerView

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?

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_id points 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.

This article is inspired by real-world challenges we tackle in our projects. If you're looking for expert solutions or need a team to bring your idea to life,

Let's talk!

    Please fill your details, and we will contact you back

      Please fill your details, and we will contact you back