VideoView pour correspondre à la hauteur des parents et garder le rapport d'aspect

j'ai une vue vidéo qui est configurée 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 la vue vidéo correspond à la largeur du conteneur parent, et ensuite la hauteur est réglée en fonction du format du film chargé.

Je voudrais faire exactement le contraire, je veux que la vue vidéo corresponde à la hauteur du parent tout en gardant le format d'image intact, la vidéo sera clippée sur les côtés.

j'ai réussi à étirer la vue vidéo pour remplir le parent mais alors le ratio d'aspect n'est pas conservée.

une autre chose est, J'ajoute MediaController à la vue vidéo 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 controllers restent toujours allumés, mais la hauteur des controllers n'est pas prise en compte lors du calcul de l'endroit où placer la vidéo (puisqu'elle est verticalement centrée).

Mes deux questions sont:

  1. Comment faire pour que la vue vidéo corresponde à la hauteur du parent tout en conservant l'aspect le ratio?
  2. Comment faire pour que le VideoView tienne compte de la hauteur de ses contrôleurs?

Merci.

20
demandé sur Nitzan Tomer 2012-11-28 15:03:37

7 réponses

vous devriez vous étendre à partir de la vue vidéo intégrée.

Appel setVideoSize avant que la vue vidéo soit affichée, vous pouvez obtenir la taille de la vidéo à partir de la vignette extraite de la vidéo.

donc, quand la vue vidéo est onMeasure est appelé, à la fois mVideoWidth& mVideoHeight sont > 0.

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

j'Espère vous 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);
        }
}
19
répondu ax003d 2017-03-17 02:54:56
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);
    }
}
4
répondu Ronny Shibley 2016-11-07 22:55:29

j'ai résolu ce problème avec la mise en page. Il semble que cela ait bien fonctionné quand il a été épinglé aux coins, mais il a causé la vidéo à fausser. Pour tester, j'ai changé l'arrière-plan de ma mise en page relative à #990000 pour voir le poking rouge à 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>
3
répondu Mevdev 2015-07-30 21:01:08

en ce qui concerne la question 1, je suis surpris que personne n'ait mentionné l'utilisation possible du mode de mise à l'échelle du Médiaplayer. https://developer.android.com/reference/android/media/MediaPlayer.html#setVideoScalingMode(int)

il y a 2 modes. Les deux remplissent toujours la zone de vue. Pour le faire remplir l'espace tout en préservant le rapport d'aspect, donc recadrer le côté long, vous devez passer au second mode,VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING. Qui résout une partie du problème. L'autre partie est de changer le comportement de mesure de VideoView, tout comme certaines des autres réponses le démontrent. C'est la façon dont je l'ai fait, la plupart du temps par par paresse et pas familier avec les API de métadonnées que les autres utilisent, Vous êtes invités à utiliser cette méthode ou l'une des autres méthodes pour fixer la taille de la vue. La prise de couverture assure la sécurité quand ceci est appelé avant que mMediaPlayer existe, comme il peut être appelé de nombreuses fois, et tombe également de nouveau à un vieux comportement si le nom du champ jamais changement.

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, vous changez simplement le mode de mise à l'échelle sur le lecteur multimédia où que vous ayez une chance. Tels que:

        video.setOnPreparedListener { mp: MediaPlayer ->
            mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING)
            mp.isLooping = true
            mp.setScreenOnWhilePlaying(false)
        }
        video.start()
2
répondu androidguy 2017-10-27 19:41:30

je passe du temps à chercher ceci sur internet je n'ai pas trouvé de meilleure solution qui puisse s'appliquer à la vue Vidéo par défaut. J'ai donc cherché des bibliothèques qui résoudront mes problèmes et finalement j'ai trouvé une 'FullscreenVideoView' (https://github.com/rtoshiro/FullscreenVideoView) cela a résolu mon exigence

0
répondu Bikesh M Annur 2016-08-16 09:50:44

j'ai essayé beaucoup de solutions, alors que ma vidéo était toujours en format 1000*1000, donc j'ai créé une solution facile pour les gens qui connaissent leur format. Tout d'abord, créez une Viewvidéo dans un format relatif comme celui-ci:

<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 programmez 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 1:1 ratio d'aspect, mais vous pouvez simplement utiliser votre ratio d'aspect pour modifier la hauteur ou la largeur.

0
répondu jobbert 2017-12-21 15:05:49

la réponse de Jobbert à Kotlin, au cas où quelqu'un en aurait besoin:

val max = if (videoView.height > videoView.width) videoView.height else videoView.width
videoView.layoutParams = ConstraintLayout.LayoutParams(max, max)
-1
répondu Michele Marchetti 2018-07-20 10:18:01