Подтвердить что ты не робот

VideoView, чтобы соответствовать высоте родителя и сохранить соотношение сторон

У меня есть VideoView, который настроен следующим образом:

<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>

Но VideoView соответствует ширине родительского контейнера, а затем высота устанавливается в соответствии с соотношением сторон загруженного фильма.
Я хотел бы сделать как раз наоборот, я хочу, чтобы VideoView соответствовал высоте родителя, сохраняя неизменное соотношение сторон, видео будет обрезано по бокам.

Мне удалось растянуть VideoView, чтобы заполнить родительский элемент, но тогда соотношение сторон не сохраняется.

Другое дело, я добавляю MediaController к VideoView следующим образом:

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();
    }
});

Это отлично работает, и контроллеры всегда остаются включенными, но высота контроллеров не учитывается при расчете места размещения видео (поскольку оно вертикально центрировано).

Вот мои два вопроса:

  • Как заставить VideoView соответствовать высоте родителя, но сохраните пропорции?
  • Как заставить VideoView учитывать высоту его контроллеров?

Спасибо.

4b9b3361

Ответ 1

Вы должны проходить через встроенный просмотр видео.

Вызовите setVideoSize перед отображением видео, вы можете получить размер видео из миниатюры, извлеченной из видео.

Итак, когда вызывается просмотр видео onMeasure, оба mVideoWidth и mVideoHeight равны > 0.

Если вы хотите учитывать высоту контроллеров, вы можете сделать это самостоятельно в методе onMeasure.

Надежда поможет.

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);
        }
}

Ответ 2

Я решил эту проблему с макетом. Кажется, что он отлично работал, когда он был привязан к углам, но это вызвало перекос видео. Чтобы проверить, я изменил свой относительный макет фона на # 990000, чтобы увидеть красную прокрутку.

<?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>

Ответ 3

Что касается вопроса 1, я удивлен, что никто не упомянул о возможном использовании режима масштабирования MediaPlayer. https://developer.android.com/reference/android/media/MediaPlayer.html#setVideoScalingMode(int)

Имеет 2 режима. Оба они всегда заполняют область просмотра. Чтобы он занимал пространство при сохранении соотношения сторон и, таким образом, обрезая длинную сторону, вам нужно переключиться на второй режим, VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING. Это решает одну часть проблемы. Другая часть состоит в том, чтобы изменить поведение измерения VideoView, как демонстрируют некоторые другие ответы. Я так и сделал, в основном из-за лени и не знаком с API-интерфейсом метаданных, который используют другие, вы можете использовать этот метод или один из других методов, чтобы исправить размер представления. Общий захват обеспечивает безопасность, когда он вызывается до того, как mMediaPlayer существует, так как он может вызываться много раз, а также возвращается к старому поведению в случае изменения имени поля.

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)
        }
    }
}

Таким образом, используя этот класс в макете, вы просто меняете режим масштабирования в медиаплеере, где бы у вас не было шансов. Такие как:

        video.setOnPreparedListener { mp: MediaPlayer ->
            mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
            mp.isLooping = true
            mp.setScreenOnWhilePlaying(false)
        }
        video.start()

Ответ 4

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);
    }
}

Ответ 5

Быстрое и эффективное исправление:

Нет необходимости создавать пользовательское представление, расширяющееся от VideoView. Просто установите достаточно большое значение для android:layout_width. Это установит widthSpecMode вида видео в View.MeasureSpec.AT_MOST а затем метод onMeasure() VideoView автоматически отрегулирует его ширину, сохраняя соотношение.

<VideoView
     android:id="@+id/video"
     android:layout_width="2000dp"
     android:layout_height="match_parent" />

Ответ 6

Я трачу время на поиск этого в Интернете. Я не нашел лучшего решения, которое может применяться к просмотру видео по умолчанию. Поэтому я искал библиотеки, которые разрешат мое требование, наконец, я нашел один "FullscreenVideoView" (https://github.com/rtoshiro/FullscreenVideoView), это решило мое требование

Ответ 7

Я пробовал много решений, в то время как мое видео всегда было в формате 1000 * 1000, поэтому я создал легкое решение для людей, которые знают их соотношение сторон. Сначала создайте VideoView в RelativeLayout следующим образом:

<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>

Затем перед загрузкой видео измените высоту и программно следующим образом:

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

Конечно, это работает только с соотношением сторон 1:1, но вы можете просто использовать соотношение сторон для изменения высоты или ширины.

Ответ 8

Впервые вопрос ответил на мою проблему вместо ответов !! Моя проблема заключалась в том, что у меня было пустое пространство под видео на полном экране. Я устанавливал layout_height в match_parent. Решением было установить для него wrap_content и дать родителю черный фон. Это и наличие VideoView центру вертикально по отношению к его родителю.

Я написал это как комментарий, но потом подумал, что у кого-то может быть та же проблема, что и у меня, так что здесь это тоже ответ.

Ответ 10

Джобберт ответит в Котлине, если кому-то это понадобится:

val max = if (videoView.height > videoView.width) videoView.height else videoView.width
videoView.layoutParams = ConstraintLayout.LayoutParams(max, max)