webentwicklung-frage-antwort-db.com.de

VideoView, um die Höhe des übergeordneten Elements anzupassen und das Seitenverhältnis zu erhalten

Ich habe ein VideoView, das folgendermaßen eingerichtet ist:

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

Die VideoView entspricht jedoch der Breite des übergeordneten Containers, und die Höhe wird entsprechend dem Seitenverhältnis des geladenen Films festgelegt.
Ich möchte genau das Gegenteil tun. Ich möchte, dass VideoView der Höhe des übergeordneten Elements entspricht und das Seitenverhältnis intakt bleibt. Das Video wird an den Seiten beschnitten.

Ich habe es geschafft, das VideoView auf das übergeordnete Element zu strecken, aber das Seitenverhältnis wird nicht beibehalten.

Eine andere Sache ist, ich füge MediaController wie folgt zum VideoView hinzu:

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

Dies funktioniert großartig und die Controller bleiben immer eingeschaltet, aber die Höhe der Controller wird bei der Berechnung der Position des Videos nicht berücksichtigt (da es vertikal zentriert ist).

Meine zwei Fragen sind dann:

  1. Wie kann ich das VideoView an die Höhe des übergeordneten Elements anpassen und dabei das Seitenverhältnis beibehalten?
  2. Wie kann ich die Höhe der Controller von VideoView berücksichtigen?

Vielen Dank.

21
Nitzan Tomer

Sie sollten von der integrierten Videoansicht ausgehen. 

Rufen Sie setVideoSize auf, bevor die Videoansicht angezeigt wird. Sie können die Videogröße aus der Miniaturansicht des Videos erhalten.

Wenn also die onMeasure der Videoansicht aufgerufen wird, sind beide mVideoWidth und mVideoHeight> 0.

Wenn Sie die Höhe der Controller berücksichtigen möchten, können Sie dies in der onMeasure-Methode selbst vornehmen.

Hoffnung wird helfen.

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

Ich habe dieses Problem mit dem Layout gelöst. Es hat den Anschein, dass es gut funktioniert hat, wenn es an den Ecken befestigt wurde, aber das Video lief schief. Zu Testzwecken änderte ich den Hintergrund meines relativen Layouts in # 990000, um das rote Durchstöbern zu sehen.

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

Zu Frage 1 bin ich erstaunt, dass niemand die mögliche Verwendung des Skalierungsmodus des MediaPlayer erwähnt hat. https://developer.Android.com/reference/Android/media/MediaPlayer.html#setVideoScalingMode(int)

Es hat 2 Modi. Beide füllen immer den Ansichtsbereich. Um den Raum auszufüllen und dabei das Seitenverhältnis beizubehalten, sodass die lange Seite abgeschnitten wird, müssen Sie in den zweiten Modus VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING wechseln. Das löst einen Teil des Problems. Der andere Teil besteht darin, das Messverhalten von VideoView zu ändern, wie auch einige der anderen Antworten zeigen. So habe ich es gemacht, meistens aus Faulheit und nicht mit den Metadaten-APIs vertraut, die die anderen verwenden. Sie können diese Methode oder eine der anderen Methoden verwenden, um die Größe der Ansicht zu bestimmen. Die Überdeckungssicherung sorgt für Sicherheit, wenn dieser Aufruf aufgerufen wird, bevor der mMediaPlayer existiert, wie er oft genannt wird, und er fällt auch zurück, wenn sich der Feldname ändert.

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

Wenn Sie diese Klasse im Layout verwenden, ändern Sie einfach den Skalierungsmodus des Media Players, wo immer Sie eine Chance haben. Sowie:

        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

Ich verbringe viel Zeit damit, das im Internet zu suchen. Ich habe keine bessere Lösung gefunden, die sich auf die Standard-Videoansicht anwenden lässt. Also habe ich nach Bibliotheken gesucht, die meine Anforderung lösen werden. Schließlich habe ich eine "FullscreenVideoView" ( https://github.com/rtoshiro/FullscreenVideoView ) gefunden, die meine Anforderung gelöst hat 

1
Bikesh M Annur

Zum ersten Mal beantwortete eine Frage mein Problem anstelle von Antworten !! Mein Problem war, dass ich im Vollbildmodus einen weißen Bereich unter dem Video hatte. Ich habe den layout_height bis match_parent. Die Lösung bestand darin, es auf wrap_content und geben Sie den Eltern einen schwarzen Hintergrund. Das und mit dem VideoView vertikal zentriert in seinem Elternteil.

Ich schrieb dies als Kommentar, dachte dann aber, jemand könnte dasselbe Problem haben, das ich hatte, und hier ist es auch als Antwort.

1

Schnelle und effiziente Lösung:

Sie müssen keine benutzerdefinierte Ansicht erstellen, die sich von VideoView aus erstreckt. Legen Sie einfach einen Wert fest, der groß genug ist, um Android:layout_width. Dadurch wird widthSpecMode der Videoansicht auf View.MeasureSpec.AT_MOST gesetzt und die onMeasure()-Methode von VideoView passt die Breite automatisch an und behält das Verhältnis bei.

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

Ich habe viele Lösungen ausprobiert, während mein Video immer im 1000 * 1000-Format war. Daher habe ich eine einfache Lösung für Personen erstellt, die ihr Seitenverhältnis kennen. Erstellen Sie zunächst ein VideoView in einem RelativeLayout wie folgt:

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

Bevor Sie das Video laden, ändern Sie die Höhe und programmgesteuert wie folgt:

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

Natürlich funktioniert das nur mit einem Seitenverhältnis von 1: 1, aber Sie können das Seitenverhältnis verwenden, um entweder die Höhe oder die Breite zu ändern.

0
jobbert

Jobberts Antwort in Kotlin, falls jemand sie braucht:

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