Android VideoView orientation change with buffered video
Asked Answered
E

11

50

I'm trying to replicate the functionality of the latest YouTube app in the Android marketplace. When watching a video there's two separate layouts, one in portrait which provides additional info, and one in landscape which provides a full screen view of the video.

YouTube app portrait layout
YouTupe app in portrait mode

YouTube app landscape layout
YouTube app in landscape mode

(Sorry for the randomness of the photos, but they were the first pics I could find of the actual layout)

This is pretty easy to do normally - just specify an alternate layout in layout-land and all will be good. The thing that the YouTube app does really well (and what I'm trying to replicate) is that on orientation change, the video continues playing and doesn't have to re-buffer from the beginning.

I've figured out that overriding onConfigurationChange() and setting new LayoutParameters will allow me to resize the video without forcing a rebuffer - however the video will randomly scale to different widths/heights when rotating the screen multiple times. I've tried doing all sorts of invalidate() calls on the VideoView, tried calling RequestLayout() on the parent RelativeLayout container and just trying as many different things as I can, but I can't seem to get it to work properly. Any advice would be greatly appreciated!

Here's my code:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        questionText.setVisibility(View.GONE);
        respond.setVisibility(View.GONE);
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
    } else {
        questionText.setVisibility(View.VISIBLE);
        respond.setVisibility(View.VISIBLE);
        Resources r = getResources();
        int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 150.0f, r.getDisplayMetrics());
        questionVideo.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, height));
    }
}

EDIT: I've discovered in logcat some interesting output that comes up when my video is rotated which seems to be the culprit - although I have no idea how to fix it:

Logcat output when resizing properly (takes up entire window)

notice the h=726

12-13 15:37:35.468  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=210}
12-13 15:37:35.561  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Adjusted Position/X1/Y0/W403/H225
12-13 15:37:35.561  1262  1268 I TIOverlay: Rotation/90
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay_set_position:: w=402 h=726
12-13 15:37:35.561  1262  1268 I Overlay : dumping driver state:
12-13 15:37:35.561  1262  1268 I Overlay : output pixfmt:
12-13 15:37:35.561  1262  1268 I Overlay : w: 432
12-13 15:37:35.561  1262  1268 I Overlay : h: 240
12-13 15:37:35.561  1262  1268 I Overlay : color: 7
12-13 15:37:35.561  1262  1268 I Overlay : UYVY
12-13 15:37:35.561  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:37:35.561  1262  1268 I Overlay : window l: 1 
12-13 15:37:35.561  1262  1268 I Overlay : window t: 0 
12-13 15:37:35.561  1262  1268 I Overlay : window w: 402 
12-13 15:37:35.561  1262  1268 I Overlay : window h: 726

Logcat output when resizing incorrectly (takes up tiny portion of full screen)

notice the h=480

12-13 15:43:00.085  1262  1270 I ActivityManager: Config changed: { scale=1.0 imsi=310/4 loc=en_US touch=3 keys=1/1/2 nav=1/1 orien=2 layout=34 uiMode=17 seq=216}
12-13 15:43:00.171  1262  1268 I TIOverlay: Position/X0/Y76/W480/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Adjusted Position/X138/Y0/W266/H225
12-13 15:43:00.171  1262  1268 I TIOverlay: Rotation/90
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=480 h=224
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay_set_position:: w=266 h=480
12-13 15:43:00.179  1262  1268 I Overlay : dumping driver state:
12-13 15:43:00.179  1262  1268 I Overlay : output pixfmt:
12-13 15:43:00.179  1262  1268 I Overlay : w: 432
12-13 15:43:00.179  1262  1268 I Overlay : h: 240
12-13 15:43:00.179  1262  1268 I Overlay : color: 7
12-13 15:43:00.179  1262  1268 I Overlay : UYVY
12-13 15:43:00.179  1262  1268 I Overlay : v4l2_overlay window:
12-13 15:43:00.179  1262  1268 I Overlay : window l: 138 
12-13 15:43:00.179  1262  1268 I Overlay : window t: 0 
12-13 15:43:00.179  1262  1268 I Overlay : window w: 266 
12-13 15:43:00.179  1262  1268 I Overlay : window h: 480

Maybe someone knows what 'Overlay' is and why it's not getting the correct height value?

Elane answered 13/12, 2010 at 22:16 Comment(2)
Finally got this working using the new Fragment API and a TextureView, I know this is an older question, but I've posted my solution as well so people might see an updated approach.Needful
This answer works for me - one line of code, since I already subclass VideoView https://mcmap.net/q/330811/-android-videoview-orientation-change-with-buffered-videoDoth
E
57

EDIT: (June 2016)

This answer is very old (I think android 2.2/2.3) and probably is not as relevant as the other answers below! Look to them first unless you're dev-ing on legacy Android :)


I was able to narrow down the problem to the onMeasure function in the VideoView class. By creating a child class and overriding the onMeasure function, I was able to get the desired functionality.

public class VideoViewCustom extends VideoView {

    private int mForceHeight = 0;
    private int mForceWidth = 0;
    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setDimensions(int w, int h) {
        this.mForceHeight = h;
        this.mForceWidth = w;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.i("@@@@", "onMeasure");

        setMeasuredDimension(mForceWidth, mForceHeight);
    }
}

Then inside my Activity I just did the following:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        questionVideo.setDimensions(displayHeight, displayWidth);
        questionVideo.getHolder().setFixedSize(displayHeight, displayWidth);

    } else {
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        questionVideo.setDimensions(displayWidth, smallHeight);
        questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

    }
}

The line:

questionVideo.getHolder().setFixedSize(displayWidth, smallHeight);

is key in order to make this work. If you do the setDimensions call without this guy, the video still will not resize.

The only other thing you need to do is ensure that you call setDimensions() inside the onCreate() method as well or your video will not start buffering as the video won't be set to draw on a surface of any size.

// onCreate()
questionVideo.setDimensions(initialWidth, initialHeight); 

One last key part - if you ever find yourself wondering why the VideoView isn't resizing on rotation, you need to ensure the dimensions you're resizing to are either exactly equal to the visible area or less than it. I had a really big problem where I was setting the VideoView's width/height to the entire display size when I still had the notification bar/title bar on the screen and it was not resizing the VideoView at all. Simply removing the notification bar and title bar fixed the problem.

Hopefully this helps someone in the future!

Elane answered 15/12, 2010 at 16:52 Comment(2)
Thanks for the info! I've tried it and this class helps a lot with the rotation. However, on my device when going portrait the screen gets corrupted. Do you have any ideas why this could be happening? I've started #6525159 in the hope of finding out but so far have no ideas.Loveliesbleeding
I have use it, but it is not playing the video.Stlouis
B
25

Here is a really easy way to accomplish what you want with minimal code:

AndroidManifest.xml:

android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

Note: Edit as needed for your API, this covers 10+, but lower APIs require removing the "screenSize|screenLayout|uiMode" portion of this line

Inside the "OnCreate" method, usually under "super.onCreate", add:

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

And then somewhere, usually at the bottom, add:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

This will resize the video to fullscreen whenever the orientation has changed without interrupting playback and only requires overriding the configuration method.

Bedel answered 1/1, 2013 at 18:55 Comment(1)
On some devices, you may also need to hide the media controller or it will prevent the video from being resized. This can be done using the mediaController.hide() function.Bedel
D
24

First of all, thanks a lot for your own extensive answer.

I had the same problem, the video would most of the time be smaller, or bigger, or distorted inside the VideoView after a rotation.

I tried your solution, but I also tried random things, and by chance I noticed that if my VideoView is centered in its parent, it magically works by itself (no custom VideoView needed or anything).

To be more specific, with this layout, I reproduce the problem most of the time:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>

With this one layout, I never have the problem (plus, the video is centered, which is how it should be anyway ;):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" />

</RelativeLayout>

It also works with wrap_content instead of match_parent (the video still takes all the space) which does not make much sense to me.

Anyway, I don't have any explanation for this - this looks like a VideoView bug to me.

Danielladanielle answered 1/11, 2011 at 23:35 Comment(1)
Simple and working answer. However, in some cases, android:layout_centerInParent="true" will add some vertical and horizontal paddings that might not be ideal. Try using android:layout_alignParentBottom="true" android:layout_alignParentTop="true" android:layout_alignParentStart="true" android:layout_alignParentEnd="true" in that case.Howenstein
P
11

VideoView uses what's called an overlay which is an area where the video is rendered. That overlay is behind the window holding the VideoView. VideoView punches a hole in its window so that the overlay is visible. It then keeps it in sync with the layout (eg. if you move or resize VideoView, the overlay has to be moved and resized as well).

There's a bug somewhere during the layout phase which makes the overlay use the previous size set by VideoView.

To fix it, subclass VideoView and override onLayout:

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    getHolder().setSizeFromLayout();
}

and the overlay will have the correct size from VideoView's layout dimensions.

Polonaise answered 28/2, 2014 at 16:59 Comment(0)
N
7

Replicating the YouTube app

I managed to build a sample project that does not require android:configChanges="orientation" or a custom VideoView. The resulting experience is identical to how the YouTube app handles rotation during video playback. In other words, the video does not need to be paused, re-buffered, or reloaded, and does not skip or drop any audio frames when the device orientation changes.

Optimal Method

This method uses a TextureView and its accompanying SurfaceTexture as a sink for the MediaPlayer's current video frame. Since a SurfaceTexture uses a GL texture object (simply referenced by an integer from the GL context), believe it's okay to retain a reference to a SurfaceTexture through configuration changes. The TextureView itself is destroyed and recreated during the configuration change (along with the backing Activity), and the newly created TextureView is simply updated with the SurfaceTexture reference before it is attached.

I've created a full working example showing how and when to initialize your MediaPlayer and a possible MediaController, and I'll highlight the interesting parts relevant to this question below:

public class VideoFragment {
    
    TextureView mDisplay;
    SurfaceTexture mTexture;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        
        final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        mDisplay = (TextureView) rootView.findViewById(R.id.texture_view);
        if (mTexture != null) {
            mDisplay.setSurfaceTexture(mTexture);
        }
        mDisplay.setSurfaceTextureListener(mTextureListener);

        return rootView;
    }

    TextureView.SurfaceTextureListener mTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
            // Initialize your media now or flag that the SurfaceTexture is available..
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            mTexture = surface;
        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
            mTexture = surface;
            return false; // this says you are still using the SurfaceTexture..
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            mTexture = surface;
        }
    };

    @Override
    public void onDestroyView() {
        mDisplay = null;
        super.onDestroyView();
    }

    // ...

}
 

Since the solution uses a retained Fragment rather than an Activity that handles configuration changes manually, you can fully leverage the configuration specific resource inflation system as you would naturally. Unfortunately, if your minimum sdk is below API 16, you will essentially need to backport TextureView (which I have not done).

Finally, if you're interested, checkout my initial question detailing: my original approach, the Android media stack, why it didn't work, and the alternate workaround.

Needful answered 10/12, 2013 at 0:48 Comment(8)
Will retained Fragment create memory leak?Trusteeship
@GZ95 no. We're throwing away all references to a Context and reusing every reference we retain.Needful
I'm trying to implement your solution, but I can't get the video to be retained on rotation. The view always goes black and then the video starts from the beginning. Any idea what causes that?Dzungaria
That happens when for me when you don't set the old texture via TextureView.setSurfaceTexture().Needful
@Needful how to overcome video buffering in activity ?Rheo
@Needful how do you initialize the media/video?Analogize
@Rai what media do you have?Needful
@Needful a VideoView but I actually ended finding this.Analogize
A
3

use this :

@Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

            getActivity().getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
            getActivity().getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);

        } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

            getActivity().getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
            getActivity().getWindow().clearFlags(
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);

        }
    }

Also dont forget to add the line below to your Activity in the Manifest:

android:configChanges="orientation|keyboard|keyboardHidden|screenSize"
Alleluia answered 28/10, 2013 at 10:48 Comment(0)
W
2

I took examples from some of the answers here and tried to do it my way. Looks like a easy solution.

videoLayout = (RelativeLayout) videoView.findViewById(R.id.videoFrame);

onConfigurationChange inside a fragment.Where video is displayed in fullscreen in landscape mode.Notice that I am hiding the action bar.

@Override
public void onConfigurationChanged(Configuration newConfig) {

    int          height     = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200.0f,getResources().getDisplayMetrics());
    ActionBar    actionBar  = weatherActivity.getSupportActionBar();
    LayoutParams params     = videoLayout.getLayoutParams();

    if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
    {
        if(actionBar.isShowing())
            actionBar.hide();


        params.width  = ViewGroup.LayoutParams.MATCH_PARENT;
        params.height = ViewGroup.LayoutParams.MATCH_PARENT;

        videoLayout.requestLayout();
    }
    else if(newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
    {
        if(!actionBar.isShowing())
            actionBar.show();

        params.width  = ViewGroup.LayoutParams.MATCH_PARENT;
        params.height = height;

        videoLayout.requestLayout();
    }

    super.onConfigurationChanged(newConfig);
}   

and here is my layout file

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

<RelativeLayout 
    android:id="@+id/videoFrame"
    android:layout_height="200dp"
    android:layout_width="match_parent">

    <VideoView
        android:id="@+id/video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"/>

</RelativeLayout>

I have another relativelayout below this which is not in this layout. But that would not make any difference.

Watthour answered 8/4, 2015 at 17:53 Comment(0)
R
2

see my sample code, it work for me

public class CustomVideoView extends android.widget.VideoView {
private int width;
private int height;
private Context context;
private VideoSizeChangeListener listener;
private boolean isFullscreen;

public CustomVideoView(Context context) {
    super(context);
    init(context);
}

public CustomVideoView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

/**
 * get video screen width and height for calculate size
 *
 * @param context Context
 */
private void init(Context context) {
    this.context = context;
    setScreenSize();
}

/**
 * calculate real screen size
 */
private void setScreenSize() {
    Display display = ((Activity) context).getWindowManager().getDefaultDisplay();

    if (Build.VERSION.SDK_INT >= 17) {
        //new pleasant way to get real metrics
        DisplayMetrics realMetrics = new DisplayMetrics();
        display.getRealMetrics(realMetrics);
        width = realMetrics.widthPixels;
        height = realMetrics.heightPixels;

    } else if (Build.VERSION.SDK_INT >= 14) {
        //reflection for this weird in-between time
        try {
            Method mGetRawH = Display.class.getMethod("getRawHeight");
            Method mGetRawW = Display.class.getMethod("getRawWidth");
            width = (Integer) mGetRawW.invoke(display);
            height = (Integer) mGetRawH.invoke(display);
        } catch (Exception e) {
            //this may not be 100% accurate, but it's all we've got
            width = display.getWidth();
            height = display.getHeight();
        }

    } else {
        //This should be close, as lower API devices should not have window navigation bars
        width = display.getWidth();
        height = display.getHeight();
    }

    // when landscape w > h, swap it
    if (width > height) {
        int temp = width;
        width = height;
        height = temp;
    }
}

/**
 * set video size change listener
 *
 */
public void setVideoSizeChangeListener(VideoSizeChangeListener listener) {
    this.listener = listener;
}

public interface VideoSizeChangeListener {
    /**
     * when landscape
     */
    void onFullScreen();

    /**
     * when portrait
     */
    void onNormalSize();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // full screen when landscape
        setSize(height, width);
        if (listener != null) listener.onFullScreen();
        isFullscreen = true;
    } else {
        // height = width * 9/16
        setSize(width, width * 9 / 16);
        if (listener != null) listener.onNormalSize();
        isFullscreen = false;
    }
}

/**
 * @return true: fullscreen
 */
public boolean isFullscreen() {
    return isFullscreen;
}

/**
 * set video sie
 *
 * @param w Width
 * @param h Height
 */
private void setSize(int w, int h) {
    setMeasuredDimension(w, h);
    getHolder().setFixedSize(w, h);
}
}

and do not re-create view when change orientation

// AndroidManifest.xml
android:configChanges="screenSize|orientation|keyboardHidden"

portrain

landscape landscape

my source code here

Radome answered 26/6, 2015 at 19:32 Comment(4)
tx for your solution it helped me a lotEpicedium
hi @war_Hero , i've just update my code, because it have problem when device landscape -> open app -> video is not fullscreen, but now it workRadome
Yes it had that issue, also then that the pressing matters is that old code don't work with fragmentsEpicedium
Got en Error on line : Display display = ((Activity) context).getWindowManager().getDefaultDisplay(); Caused by: java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.ActivityAlcus
M
1

While Mark37's (very useful) answer does work, it requires setting the dimensions manually (using setDimensions). This may be fine in an app where the desired dimensions are known in advance, but if you want the views to determine the size automatically based on the video's parameters (e.g. to make sure the original aspect ratio is kept), a different approach is needed.

Fortunately, it turns out the setDimensions part isn't actually necessary. VideoView's onMeasure already includes all the necessary logic, so instead of relying on someone to call setDimensions, it's possible to just call super.onMeasure, and then use the view's getMeasuredWidth/getMeasuredHeight to get the fixed size.

So, the VideoViewCustom class becomes simply:

public class VideoViewCustom extends VideoView {

    public VideoViewCustom(Context context) {
        super(context);
    }

    public VideoViewCustom(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VideoViewCustom(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        getHolder().setFixedSize(getMeasuredWidth(), getMeasuredHeight());
    }
}

This version doesn't require any additional code in the caller, and takes full advantage of VideoView's existing onMeasure implementation.

This is in fact rather similar to the approach suggested by Zapek, which does basically the same thing, only with onLayout instead of onMeasure.

Maggy answered 9/4, 2014 at 11:27 Comment(1)
I have used your code ,But I have issue with the height match to the screen size can you help me for same..Illuminati
M
1

SIMPLE answer and up to date 2018.

Positive points for this answer: Code is below

1) It doesn't matter if you set to FULL_SCREEN or not. As it match the parent, it will always work. In the old accepted answer if you do not set to FULL_SCREEN, android will take the whole phone size and then the VideoView will be outside of the screen;

2) You do not need to create a Custom VideoView or PlayerView;

3) It does not call the method several times. In the old accepeted answer, it calls onMeasure several times unecessary;

4) There is also a button to change orientation like youtube app;

5) Setting phone's auto rotate to OFF also block the app orientation change.

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    ...
    setPlayerViewLayoutParams();
    ...
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        setPlayerViewLayoutParamsForLandScape();
    } else {
        setPlayerViewLayoutParamsForPortrait();
    }
}

private void setPlayerViewLayoutParamsForLandScape() {
    ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT);
    mPlayerView.setLayoutParams(lp);
}

private void setPlayerViewLayoutParamsForPortrait() {
    Display display = myContext.getWindowManager().getDefaultDisplay();
    Point size = new Point();
    display.getSize(size);
    int width = size.x;
    Double doubleHeight = width / 1.5;
    Integer height = doubleHeight.intValue();

    ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams(
            ConstraintLayout.LayoutParams.MATCH_PARENT, height);
    mPlayerView.setLayoutParams(lp);
}

private void setPlayerViewLayoutParams() {
    if (myContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
        setPlayerViewLayoutParamsForLandScape();
    } else {
        setPlayerViewLayoutParamsForPortrait();
    }
}

OBS: I am using PlayerView inside a ConstraintLayout. Because I am working with ExoPlayer Media Library.

fragment_player.xml

<android.support.constraint.ConstraintLayout   
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">

<com.google.android.exoplayer2.ui.PlayerView
    android:id="@+id/player_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorBlack"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintVertical_bias="0.0"/>

</android.support.constraint.ConstraintLayout>

ADDITIONAL

I also want a button to change the orientation

To fully reproduce youtube app you will probably want a button to set the player to landscape or portrait, this is useful if the user is with the phone's auto rotation off.

First, you will need to follow these steps:

  1. Create the button that will change the orientation;
  2. When the user clicks the button, check the current orientation;
  3. Set to opposite orientation, for example, if it is current in LandScape, set to Portrait.

PlayerFragment.java

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.image_button_full_screen:
            if (myContext.getResources().getConfiguration().orientation 
                == Configuration.ORIENTATION_LANDSCAPE) {
                myContext.setRequestedOrientation(
                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
            } else {
                myContext.setRequestedOrientation(
                        ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
            }
            break;
    }
}

PROBLEM 1: Rotating the device does not work anymore

You will notice that if you click in the button to change the orientation, rotating the device will not work anymore to change the player size. This happens because you set the orientation to LandScape or Portrait programatically and it will stay like this forever, therefore you need a way to set the orientation to SENSOR, so when you rotate the device it will check what is the real current orientation. The method below will do it for you.

PROBLEM 2: Phone's auto rotate is OFF and it still change orientation

Inside the method below you need to check if the phone's auto rotate is ON or OFF, in case it is ON you can set the ORIENTATION to SENSOR, so the phone will be able to change orientation when the user rotate the device. In case auto rotate is OFF you do nothing, so the orientation will be block in Portrait or in LandScape, depending if the user pressed the full screen button or not.

private void setOrientationListener() {
    OrientationEventListener orientationEventListener = new OrientationEventListener(this) {
        @Override
        public void onOrientationChanged(int orientation) {
            // 94 > 90 - 10 and 94 < 90 + 10     //   80 < orientation < 100.  Rotating to the LEFT.
            // 278 > 270 - 10 and 278 < 270 + 10 //   260 < orientation < 280. Rotating to the RIGHT.
            int epsilon = 10;
            int leftLandscape = 90;
            int rightLandscape = 270;
            if (epsilonCheck(orientation, leftLandscape, epsilon) ||
                    epsilonCheck(orientation, rightLandscape, epsilon)) {
                if (android.provider.Settings.System.getInt(getContentResolver(),
                        Settings.System.ACCELEROMETER_ROTATION, 0) == 1) {
                    // Phone's auto rotation is ON
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
                }
            }
        }

        private boolean epsilonCheck(int a, int b, int epsilon) {
            return a > b - epsilon && a < b + epsilon;
        }
    };
    orientationEventListener.enable();
}

If you working with a media app, I have a sample that will guide you on how to powerfully use Exoplayer Library.

Maroc answered 26/7, 2018 at 21:26 Comment(0)
M
0

I tried to use the code of other answers, but it didn't solve my problem, because if I rotated the screen quickly, my video size was bumped. So I use in part their code, which I put in a thread which updates the window very quickly. So, here is my code:

  new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            while(true)
            {
                try
                {
                    Thread.sleep(1);
                }
                catch(InterruptedException e){}
                handler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                WindowManager.LayoutParams.FLAG_FULLSCREEN);
                    }
                });
            }
        }
    }).start();

In this way, the video size will be always right (in my case).

Mab answered 27/4, 2017 at 10:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.