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:
- Comment faire pour que la vue vidéo corresponde à la hauteur du parent tout en conservant l'aspect le ratio?
- Comment faire pour que le VideoView tienne compte de la hauteur de ses contrôleurs?
Merci.
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);
}
}
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);
}
}
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>
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()
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
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.
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)