I am running into an issue where the Exoplayer playerview isn't initially fitting the containing frame, and maintaining the video's aspect ratio in portrait. The view is only resized correctly when rotating the device to landscape, and then rotating back to portrait orientation.
Note, this is a React Native application and the orientation for the rest of the app is locked to portrait. In this case, only the player is to freely move between different orientations.
What I have tried so far:
resize_mode
to fit
on the player view (both programmatically & via layout xml)16:9
videoScalingMode
on the exoplayer instance to VIDEO_SCALING_MODE_SCALE_TO_FIT
portrait
. In the app manifest, I removed the android:ScreenOrientation="portrait"
setting for the MainActivity.I am at a little bit of a loss right now. My understand is, setting the resize_mode
to fit as the default should work when the player view is created. Is there something with how the layout is created? Any help is appreciated.
For reference, here are parts of the code for the player:
video.xml - layout
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/black">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/content_container"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/exoplayerview_video"
app:resize_mode="fit"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</merge>
Main Activity - android manifest (partial)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MainApplication"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:theme="@style/AppTheme"
android:extractNativeLibs="${extractNativeLibs}"
android:supportsRtl="true">
<meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
android:value="com.reactnative.googlecast.GoogleCastOptionsProvider" />
<meta-data
android:name="com.reactnative.googlecast.RECEIVER_APPLICATION_ID"
android:value="${castReceiverId}" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${google_maps_api_key}" />
<activity
android:name=".MainActivity"
android:theme="@style/SplashTheme"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:screenOrientation="portrait"
android:exported="true"
android:label="@string/app_name"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
............
Here are some screenshots of what I am seeing vs. what is intended:
Initial Portrait issue - resize mode not respected:
Additional notes:
compileSdkVersion
: 33targetSdkVersion
: 332.13.1
In case any one else comes across this problem, I found the issue. There appears to be an open (as of 5/21/24) issue on react native's github: https://github.com/facebook/react-native/issues/17968. In short, you may run into re-layout problems when trying to update custom native views and their children.
My solution was to request a re-layout of my player's view.
My Main player class:
class VideoPlayer(context: ThemedReactContext, type: PlayerType) : MediaPlayer(
context,
R.layout.video) {
// Called by `requestLayout`
private val measureAndLayout = Runnable {
measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY))
layout(left, top, right, bottom)
}
override fun requestLayout() {
super.requestLayout()
// refresh view & children layouts by triggering a measure & layout
// pass after a requestLayout() call.
// ===================================================================
// active issue: https://github.com/facebook/react-native/issues/17968
post(measureAndLayout)
}
// ........
}
Inside my player's controller:
// called on my player's main view -- I invoked this on init of my controller, which was enough
view.requestLayout()
disclaimer: I'm still on react-native version 0.71.1
, so not sure if this is still an issue on newer versions using the new RN architecture