次のように設定された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を拡大して親を満たすようにしましたが、アスペクト比は維持されません。
もう1つのことは、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();
}
});
これはうまく機能し、コントローラーは常にオンになりますが、ビデオの配置場所を計算するときにコントローラーの高さは考慮されません(垂直方向に中央に配置されるため)。
私の2つの質問は次のとおりです。
ありがとう。
組み込みのビデオビューから拡張する必要があります。
ビデオビューが表示される前に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);
}
}
この問題はレイアウトで解決しました。コーナーに固定されている場合は正常に機能したようですが、ビデオが歪んでいます。テストするために、相対的なレイアウトの背景を#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>
質問1に関して、MediaPlayerのスケーリングモードの使用の可能性について誰も言及していないことに驚いています。 https://developer.Android.com/reference/Android/media/MediaPlayer.html#setVideoScalingMode(int)
2つのモードがあります。両方とも常に表示領域を埋めます。縦横比を維持しながらスペースを埋めて長辺をトリミングするには、2番目のモードVIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
に切り替える必要があります。これで問題の一部が解決します。他の部分は、他の回答のいくつかが示すように、VideoViewの測定動作を変更することです。これは私がやった方法であり、ほとんどが怠ofであり、他の人が使用するメタデータ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()
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);
}
}
迅速かつ効率的な修正:
VideoViewから拡張するカスタムビューを作成する必要はありません。 _Android:layout_width
_に十分な大きさの値を設定するだけです。これにより、ビデオビューのwidthSpecMode
が_View.MeasureSpec.AT_MOST
_に設定され、VideoViewのonMeasure()
メソッドが比率を維持しながら幅を自動調整します。
_<VideoView
Android:id="@+id/video"
Android:layout_width="2000dp"
Android:layout_height="match_parent" />
_
質問が答えではなく私の問題に初めて答えました!!私の問題は、フルスクリーンのビデオの下に空白があることでした。 layout_height
をmatch_parent
に設定していました。解決策は、wrap_content
に設定し、親に黒い背景を与えることでした。それは、VideoView
を親の垂直方向の中央に配置することです。
私はこれをコメントとして書いたが、誰かが私と同じ問題を抱えているかもしれないと思ったので、ここでも答えとして。
私のビデオは常に1000 * 1000形式でしたが、アスペクト比を知っている人のための簡単なソリューションを作成しましたが、多くのソリューションを試しました。まず、次のようなRelativeLayoutでVideoViewを作成します。
<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のアスペクト比でのみ動作しますが、アスペクト比を使用して高さまたは幅を変更することができます。
誰かがそれを必要とする場合のために、KotlinでのJobbertの答え:
val max = if (videoView.height > videoView.width) videoView.height else videoView.width
videoView.layoutParams = ConstraintLayout.LayoutParams(max, max)