VideoView to match parent height and keep aspect ratio
Asked Answered
S

13

38

I have a VideoView which is set up like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:id="@+id/player" 
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">

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

    <ProgressBar
        android:id="@+id/loader"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true" />

</RelativeLayout>

But the VideoView matches the width of the parent container, and then the height is set according to the aspect ratio of the loaded movie.
I would like to do just the opposite, I want the VideoView to match the height of the parent while keeping the aspect ratio intact, the video will be clipped on the sides.

I managed to stretch the VideoView to fill the parent but then the aspect ratio is not kept.

Another thing is, I'm adding MediaController to the VideoView like this:

MediaController controllers = new MediaController(this) {
    @Override
    public void hide() {
        if (state != State.Hidden) {
            this.show();
        }
        else {
            super.hide();
        }
    }
};
controllers.setAnchorView(videoView);
videoView.setMediaController(controllers);

videoView.setOnPreparedListener(new OnPreparedListener() {
    @Override
    public void onPrepared(MediaPlayer mediaPlayer) {
        controllers.show();
    }
});

This works great, and the controllers always stay on, but the height of the controllers is not being taken into account when calculating where to place the video (since it's vertically centered).

My two questions then are:

  1. How do I make the VideoView match the height of the parent yet keep the aspect ratio?
  2. How do I make the VideoView take into account the height of it's controllers?

Thanks.

Seiler answered 28/11, 2012 at 11:3 Comment(3)
Are you talking about a WebView or VideoView? You change about half way down.Erythrite
For the first time a question answered my issue instead of answers!! My issue was that I have a white space under the video on full screen. I was setting the layout_height to match_parent. The solution was to set it wrap_content and give the parent a black background. That, and having the VideoView centered vertically in its parent.Entree
Keep your VideoView width and height as match_parent and android:scaleType="fitXY" you will get results as per your requirement.Belfast
M
31

You should extends from the built-in video view.

Call setVideoSize before video view is shown, you can get video size from thumbnail extracted from video.

So that, when video view's onMeasure is called, both mVideoWidth & mVideoHeight are > 0.

If you want to account the height of controllers, you can do it yourself in the onMeasure method.

Hope will help.

public class MyVideoView extends VideoView {

        private int mVideoWidth;
        private int mVideoHeight;

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

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

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

        public void setVideoSize(int width, int height) {
            mVideoWidth = width;
            mVideoHeight = height;
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            // Log.i("@@@", "onMeasure");
            int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
            int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
            if (mVideoWidth > 0 && mVideoHeight > 0) {
                if (mVideoWidth * height > width * mVideoHeight) {
                    // Log.i("@@@", "image too tall, correcting");
                    height = width * mVideoHeight / mVideoWidth;
                } else if (mVideoWidth * height < width * mVideoHeight) {
                    // Log.i("@@@", "image too wide, correcting");
                    width = height * mVideoWidth / mVideoHeight;
                } else {
                    // Log.i("@@@", "aspect ratio is correct: " +
                    // width+"/"+height+"="+
                    // mVideoWidth+"/"+mVideoHeight);
                }
            }
            // Log.i("@@@", "setting size: " + width + 'x' + height);
            setMeasuredDimension(width, height);
        }
}
Maryannmaryanna answered 29/9, 2013 at 6:44 Comment(5)
This definitely helped but I also needed to set up the View to be larger than the screen as here: #3813549Pheidippides
@Maryannmaryanna i have tried this videoView.setLayoutParams(params); videoView.setDimensions(params.width,params.width); videoView.getHolder().setFixedSize(params.width,params.height); but still its not setting my videoview heightCordova
This is nice answer, but instead of manually use setVideoSize(), I would override method which load video: @Override public void setVideoURI(Uri uri) { MediaMetadataRetriever retriever = new MediaMetadataRetriever(); retriever.setDataSource(this.getContext(), uri); mVideoWidth = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)); mVideoHeight = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)); super.setVideoURI(uri); }Wellspoken
Works like a charm. Just a little hint: if you are using mediaPlayer to host a video to the videoView: you can't use setVideoUri. For this case the setVideoSize method is totally fine. (btw I'm using Xamarin (C#)).Ciera
setMeasuredDimension has no effect it did not change size of video, i do not know whyFerrite
C
21

I solved this problem with layout. It seems that it worked fine when it was pinned to the corners but it caused the video to skew. To test I changed my relative layout's background to #990000 to see the red poking through.

<?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"
    android:id="@+id/relative_parent"
    android:background="#000000">
    <VideoView
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:id="@+id/videoView" />
</RelativeLayout>
Corposant answered 30/7, 2015 at 21:1 Comment(3)
well, its working but i couldn't understand why its not working under other layouts like frame layout and constraint layout if you have solution using other layouts as well please share.Rn
Its, Works + 1, It's because of android:layout_alignParentLeft="true" android:layout_alignParentRight="true", videoview gets fit to both right and left of the relativelayout.Benzaldehyde
simple hack! thanks .Sansbury
S
14

Regarding question 1, I am surprised no one has mentioned the possible use of the MediaPlayer's scaling mode. https://developer.android.com/reference/android/media/MediaPlayer.html#setVideoScalingMode(int)

It has 2 modes. Both of them always fill the view area. To get it to fill the space while preserving the aspect ratio, thus cropping the long side, you need to switch to the second mode, VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING. That solves one part of the problem. The other part is to change VideoView's measuring behavior, just as some of the other answers demonstrate. This is the way I did it, mostly out of laziness and not familiar with the metadata API's that the others use, you are welcome to use this method or one of the other methods to fix the size of the view. The blanket catch ensures safety when this is called before mMediaPlayer exists, as it may be called many times, and also falls back to old behavior should the field name ever change.

class FixedSizeVideoView : VideoView {
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)

    // rather than shrink down to fit, stay at the size requested by layout params. Let the scaling mode
    // of the media player shine through. If the scaling mode on the media player is set to the one
    // with cropping, you can make a player similar to AVLayerVideoGravityResizeAspectFill on iOS
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        try {
            val mpField = VideoView::class.java.getDeclaredField("mMediaPlayer")
            mpField.isAccessible = true
            val mediaPlayer: MediaPlayer = mpField.get(this) as MediaPlayer

            val width = View.getDefaultSize(mediaPlayer.videoWidth, widthMeasureSpec)
            val height = View.getDefaultSize(mediaPlayer.videoHeight, heightMeasureSpec)
            setMeasuredDimension(width, height)
        }
        catch (ex: Exception) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        }
    }
}

So using this class in the layout, you just change the scaling mode on the media Player wherever you have a chance. Such as:

        video.setOnPreparedListener { mp: MediaPlayer ->
            mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
            mp.isLooping = true
            mp.setScreenOnWhilePlaying(false)
        }
        video.start()
Snap answered 27/10, 2017 at 19:41 Comment(4)
This isn't android is it?Gerent
Kotlin, dear sir :)Snap
Since this answer is still relevant, we should keep in mind the new SDK reflection restrictions since Android 9. One might consider this question: #11186374. Also, the VideoView.mMediaPlayer field is officially still 'graylisted' as of Android 11.Snap
how to get AVLayerVideoGravityResizeAspect effect that exist on IOSFerrite
H
8

Quick and efficient fix:

No need to create a custom view extending from VideoView. Just set a value big enough to android:layout_width. This will set the widthSpecMode of the video view to View.MeasureSpec.AT_MOST and then the onMeasure() method of VideoView will auto-adjust its width keeping the ratio.

<VideoView
     android:id="@+id/video"
     android:layout_width="2000dp"
     android:layout_height="match_parent" />
Horton answered 15/10, 2018 at 17:36 Comment(1)
I really wished this work, but it doesn't do anything.Weese
B
7
public class MyVideoView extends VideoView {
    private int mVideoWidth;
    private int mVideoHeight;

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

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

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


    @Override
    public void setVideoURI(Uri uri) {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        retriever.setDataSource(this.getContext(), uri);
        mVideoWidth = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH));
        mVideoHeight = Integer.parseInt(retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT));
        super.setVideoURI(uri);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Log.i("@@@", "onMeasure");
        int width = getDefaultSize(mVideoWidth, widthMeasureSpec);
        int height = getDefaultSize(mVideoHeight, heightMeasureSpec);
        if (mVideoWidth > 0 && mVideoHeight > 0) {
            if (mVideoWidth * height > width * mVideoHeight) {
                // Log.i("@@@", "image too tall, correcting");
                height = width * mVideoHeight / mVideoWidth;
            } else if (mVideoWidth * height < width * mVideoHeight) {
                // Log.i("@@@", "image too wide, correcting");
                width = height * mVideoWidth / mVideoHeight;
            } else {
                // Log.i("@@@", "aspect ratio is correct: " +
                // width+"/"+height+"="+
                // mVideoWidth+"/"+mVideoHeight);
            }
        }
        // Log.i("@@@", "setting size: " + width + 'x' + height);
        setMeasuredDimension(width, height);
    }
}
Blackboard answered 7/11, 2016 at 22:42 Comment(0)
B
5

Using ConstraintLayout we can achieve this, refer below xml code.

When layout_width and layout_height are 0dp, the size and position of the VideoView are calculated dynamically based on the other constraints. The layout_constraintDimensionRatio attribute indicates that when the app calculates the size of the VideoView, the ratio of the width to the height should be 3:4. This constraint keeps the aspect ratio of the video the same and prevents the view from being stretched too far in either direction (depending on how the device is rotated).

Change layout_constraintDimensionRatio value depending on requirement Portrait/Landscape.

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintDimensionRatio="3:4"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Bordure answered 12/8, 2020 at 20:11 Comment(1)
This is the opposite the question was asking. He wants it to be cropped.Moralez
E
2

For the first time a question answered my issue instead of answers!! My issue was that I had a white space under the video on full screen. I was setting the layout_height to match_parent. The solution was to set it to wrap_content and give the parent a black background. That, and having the VideoView centered vertically in its parent.

I wrote this as a comment but then thought someone might have the same issue I had, so here it is as an answer also.

Entree answered 22/3, 2019 at 10:35 Comment(0)
G
1

I've tried a lot of solutions, while my video was always in 1000*1000 format, so I've created an easy solution for people who know their aspect ratio. First create a VideoView in a RelativeLayout like this:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
      android:id="@+id/video_holder"
      android:layout_width="wrap_content"
      android:layout_height="fill_parent"
      android:clipToPadding="false">

      <VideoView  
          android:id="@+id/videoView"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:layout_alignParentBottom="true"
          android:layout_alignParentEnd="true"
          android:layout_alignParentStart="true"
          android:layout_alignParentTop="true" />
</RelativeLayout>

Then before you load the video change the height and with programmatically like this:

    int i = videoView.getHeight() > videoView.getWidth() ? videoView.getHeight() : videoView.getWidth();
    video_holder.setLayoutParams(new ConstraintLayout.LayoutParams(i, i));

Of course this only works with 1:1 aspect ratio's but you could just use your aspect ratio to change either the height or the width.

Gerent answered 21/12, 2017 at 15:5 Comment(2)
Should be RelativeLayout not ConstraintLayout here, but it still doesn't work.Weese
@MartinErlic You're right with the constraint, but it did work for me, that was the reason why I've posted it. If you want, post your code in github/lab and link here and I'll check it for you.Gerent
E
1

Jobbert's answer in Kotlin, in case anyone needs it:

val max = if (videoView.height > videoView.width) videoView.height else videoView.width
videoView.layoutParams = ConstraintLayout.LayoutParams(max, max)
Exieexigency answered 20/7, 2018 at 10:18 Comment(0)
B
0

I have been looking for ways to display video in aspect fill in VideoView but after trying many solutions, none of them seems to work.

So I implemented the following approach and it's working for me:

Code:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    // getting screen size
    ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    int width = displayMetrics.widthPixels;
    int height = displayMetrics.heightPixels;

    double videoSizeRatio = (double) mVideoHeight / mVideoWidth;
    double screenSizeRatio = (double) height / width;

    if (mVideoWidth > 0 && mVideoHeight > 0) {
        if (videoSizeRatio > screenSizeRatio) { // screen is wider than video width
            height = (int) (videoSizeRatio * width);
        } else if (videoSizeRatio < screenSizeRatio) {
            width = (int) (height / videoSizeRatio);
        }

    }
    setMeasuredDimension(width, height);
}

Layout:

 <YourCustomizedVideoView
    android:id="@+id/videoView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    />
Broch answered 30/1, 2020 at 4:19 Comment(3)
and mVideoWidth / mVideoHeight is calculated how??Seismic
mVideoWidth / mVideoHeight as name suggested, they are video width and heightBroch
Code is fine but we need only where you have calculated them? where is declaration of displayMetrics?Ferrite
E
0

The best way to do so is Set width of videoview to max I was facing the same issue. I just set the Width to 999px and Height to match parent It works. We get a perfect view like Tik tok or Instagram reels

Essay answered 8/8, 2022 at 7:54 Comment(0)
G
-1

Just put your VideoView inside the RelativeLayout and set the desired size for that relative layout. like below code,

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="300dp">

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

</RelativeLayout>

It will work.

Greenway answered 7/5, 2020 at 15:52 Comment(0)
T
-3

You just need to put Videoview widget in RelativeLayout (changes in xml file only)

here is reference answer link

Tailor answered 29/4, 2020 at 5:36 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.