web-dev-qa-db-fra.com

VideoView pour correspondre à la hauteur du parent et conserver les proportions

J'ai un VideoView qui est configuré comme ceci:

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

Mais VideoView correspond à la largeur du conteneur parent, puis la hauteur est définie en fonction du rapport de format du film chargé.
Je voudrais faire exactement le contraire, je veux que VideoView corresponde à la hauteur du parent tout en conservant le rapport hauteur/largeur intact, la vidéo sera coupée sur les côtés.

J'ai réussi à étirer le VideoView pour remplir le parent, mais le rapport de format n'est pas conservé.

Une autre chose est que j'ajoute MediaController à VideoView comme ceci:

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

Cela fonctionne très bien et les contrôleurs restent toujours allumés, mais la hauteur des contrôleurs n'est pas prise en compte lors du calcul de l'emplacement de la vidéo (car elle est centrée verticalement).

Mes deux questions sont alors:

  1. Comment faire en sorte que VideoView corresponde à la hauteur du parent tout en conservant les proportions?
  2. Comment faire en sorte que VideoView prenne en compte la hauteur de ses contrôleurs?

Merci.

21
Nitzan Tomer

Vous devriez aller de la vue vidéo intégrée. 

Appelez setVideoSize avant d'afficher la vidéo, vous pouvez obtenir la taille de la vidéo à partir d'une vignette extraite de la vidéo.

Ainsi, lorsque la variable onMeasure de la vue vidéo est appelée, mVideoWidth & mVideoHeight sont> 0.

Si vous souhaitez rendre compte de la hauteur des contrôleurs, vous pouvez le faire vous-même dans la méthode onMeasure.

L'espoir aidera.

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);
        }
}
21
ax003d
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);
    }
}
6
Ronny Shibley

J'ai résolu ce problème avec la mise en page. Il semble que cela a bien fonctionné quand il a été bloqué dans les coins, mais la vidéo a été faussée. Pour tester, j'ai changé le fond de ma mise en page relative en # 990000 pour voir le rouge pointer à travers.

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

En ce qui concerne la question 1, je suis surpris que personne n'ait mentionné l'utilisation possible du mode de redimensionnement de MediaPlayer. https://developer.Android.com/reference/Android/media/MediaPlayer.html#setVideoScalingMode(int)

Il a 2 modes. Les deux remplissent toujours la zone d'affichage. Pour qu’il remplisse l’espace tout en préservant les proportions, en coupant ainsi le côté long, vous devez passer au deuxième mode, VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING. Cela résout une partie du problème. L’autre partie consiste à modifier le comportement de mesure de VideoView, comme le montrent certaines des autres réponses. C’est ce que j’ai fait, principalement par paresse et non familiarisé avec les API de métadonnées utilisées par les autres. Nous vous invitons à utiliser cette méthode ou l’une des autres pour corriger la taille de la vue. La capture de couverture garantit la sécurité lorsqu'elle est appelée avant la création de mMediaPlayer, car elle peut être appelée plusieurs fois. Elle reprend également l'ancien comportement si le nom du champ devait changer.

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

Donc, en utilisant cette classe dans la mise en page, il vous suffit de changer le mode de mise à l'échelle sur le lecteur multimédia chaque fois que vous en avez la possibilité. Tel que:

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

Je passe du temps à chercher cela sur Internet. Je n'ai trouvé aucune meilleure solution pouvant s'appliquer à l'affichage vidéo par défaut. J'ai donc cherché des bibliothèques qui résoudront mon besoin et finalement j'ai trouvé un 'FullscreenVideoView' ( https://github.com/rtoshiro/FullscreenVideoView ) cela a résolu mon besoin 

1
Bikesh M Annur

Solution rapide et efficace:

Pas besoin de créer une vue personnalisée s'étendant de VideoView. Il suffit de définir une valeur suffisamment grande pour Android:layout_width. Ceci définira la widthSpecMode de la vue vidéo sur View.MeasureSpec.AT_MOST, puis la méthode onMeasure() de VideoView ajustera automatiquement sa largeur en conservant le rapport.

<VideoView
     Android:id="@+id/video"
     Android:layout_width="2000dp"
     Android:layout_height="match_parent" />
0
Julian Paul

J'ai essayé beaucoup de solutions, alors que ma vidéo était toujours au format 1000 * 1000, j'ai donc créé une solution simple pour les personnes connaissant leur format d'image. Commencez par créer un VideoView dans un RelativeLayout comme ceci:

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

Ensuite, avant de charger la vidéo, changez la hauteur et utilisez le programme comme ceci:

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

Bien sûr, cela ne fonctionne qu'avec les proportions 1: 1, mais vous pouvez simplement utiliser vos proportions pour modifier la hauteur ou la largeur.

0
jobbert