VideoView to match parent height and keep aspect ratio

前端 未结 12 1863
轻奢々
轻奢々 2020-11-28 08:15

I have a VideoView which is set up like this:



        
相关标签:
12条回答
  • 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()
    
    0 讨论(0)
  • 2020-11-28 08:30

    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" />
    
    0 讨论(0)
  • 2020-11-28 08:31

    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>
    
    0 讨论(0)
  • 2020-11-28 08:33

    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>
    
    0 讨论(0)
  • 2020-11-28 08:33

    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"
        />
    
    0 讨论(0)
  • 2020-11-28 08:36

    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);
            }
    }
    
    0 讨论(0)
提交回复
热议问题