webentwicklung-frage-antwort-db.com.de

Android ImageView skaliert ein kleineres Bild in der Breite mit flexibler Höhe ohne Beschneiden oder Verzerren

Oft gefragt, nie beantwortet (zumindest nicht reproduzierbar).

Ich habe eine Bildansicht mit einem Bild, das kleiner als die Ansicht ist. Ich möchte das Bild auf die Breite des Bildschirms skalieren und die Höhe der ImageView anpassen, um die proportional korrekte Höhe des Bildes wiederzugeben.

<ImageView
    Android:layout_width="fill_parent"
    Android:layout_height="wrap_content"
/>

Dies hat zur Folge, dass das Bild in seiner ursprünglichen Größe (kleiner als die Bildschirmbreite) mit Rändern an der Seite zentriert ist. Nicht gut.

Also habe ich hinzugefügt

Android:adjustViewBounds="true" 

Gleiche Wirkung, nicht gut. Ich fügte hinzu

Android:scaleType="centerInside"

Gleiche Wirkung, nicht gut. Ich habe centerInside in fitCenter geändert. Gleiche Wirkung, nicht gut. Ich habe centerInside in centerCrop geändert. 

Android:scaleType="centerCrop"

Nun ist das Bild schließlich skaliert auf die Breite des Bildschirms - aber abgeschnitten oben und unten! Also änderte ich centerCrop in fitXY

Android:scaleType="fitXY"

Jetzt wird das Bild auf die Breite des Bildschirms skaliert, auf der y-Achse jedoch nicht , so dass ein verzerrtes Bild entsteht.

Das Entfernen von Android:adjustViewBounds="true" hat keine Auswirkungen. Das Hinzufügen eines Android:layout_gravity, wie an anderer Stelle vorgeschlagen, hat wieder keine Auswirkungen. 

Ich habe andere Kombinationen ausprobiert - vergebens. Also, bitte weiß jemand:

Wie kann man das XML einer ImageView so einrichten, dass die Bildschirmbreite ausgefüllt wird, ein kleineres Bild so skaliert wird, dass es die gesamte Ansicht ausfüllt, und das Bild mit seinem Seitenverhältnis ohne Verzerrung oder Beschneiden anzeigt?

EDIT: Ich habe auch versucht, eine beliebige numerische Höhe einzustellen. Dies wirkt sich nur bei der Einstellung centerCrop aus. Das Bild wird entsprechend der Ansichtshöhe vertikal verzerrt.

73
Mundi

Es gibt keine praktikable Lösung innerhalb des XML-Layoutstandards. 

Die einzige zuverlässige Möglichkeit, auf eine dynamische Bildgröße zu reagieren, ist die Verwendung von LayoutParams im Code. 

Enttäuschend.

20
Mundi

Ich habe dieses Problem gelöst, indem ich eine Java-Klasse erstellt habe, die Sie in Ihre Layout-Datei aufnehmen:

public class DynamicImageView extends ImageView {

    public DynamicImageView(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        final Drawable d = this.getDrawable();

        if (d != null) {
            // ceil not round - avoid thin vertical gaps along the left/right edges
        final int width = MeasureSpec.getSize(widthMeasureSpec);
        final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            this.setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Jetzt verwenden Sie dies, indem Sie Ihre Klasse zu Ihrer Layout-Datei hinzufügen:

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="fill_parent"
    Android:layout_height="fill_parent" >

    <my.package.name.DynamicImageView
        Android:layout_width="fill_parent"
        Android:layout_height="wrap_content"
        Android:scaleType="centerCrop"
        Android:src="@drawable/about_image" />
</RelativeLayout>
108
Mark Martinsson

Ich hatte das gleiche Problem wie Sie. Nach 30 Minuten des Versuchens unterschiedlicher Variationen löste ich das Problem auf diese Weise. Es gibt Ihnen die flexible Höhe und Breite, die an die übergeordnete Breite angepasst ist

<ImageView
            Android:id="@+id/imageview_company_logo"
            Android:layout_width="fill_parent"
            Android:layout_height="fill_parent"
            Android:adjustViewBounds="true"
            Android:scaleType="fitCenter"
            Android:src="@drawable/appicon" />
31
haydarkarrar
  Android:scaleType="fitCenter"

Berechnen Sie eine Skala, die das ursprüngliche src-Seitenverhältnis beibehält, aber auch dafür sorgt, dass src vollständig in dst passt. Mindestens eine Achse (X oder Y) passt genau. Das Ergebnis wird in dst zentriert.

edit:

Das Problem hierbei ist, dass der layout_height="wrap_content" das Bild nicht "erweitert". Sie müssen eine Größe für diese Änderung festlegen

  Android:layout_height="wrap_content"

zu

  Android:layout_height="100dp"  // or whatever size you want it to be

edit2:

funktioniert gut:

<ImageView
    Android:id="@+id/imageView1"
    Android:layout_width="match_parent"
    Android:layout_height="300dp"
    Android:scaleType="fitCenter"
    Android:src="@drawable/img715945m" />
7
Budius

Dies ist eine kleine Ergänzung der hervorragenden Lösung von Mark Martinsson.

Wenn die Breite Ihres Bildes größer als die Höhe ist, lässt Marks Lösung am oberen und unteren Bildschirmrand Platz.

Im Folgenden wird das Problem behoben, indem zunächst Breite und Höhe verglichen werden: Wenn Bildbreite> = Höhe, wird die Höhe an die Bildschirmhöhe angepasst und dann an die Breite, um das Seitenverhältnis zu erhalten. Wenn Bildhöhe> Breite gleich ist, wird die Breite entsprechend der Bildschirmbreite und dann die Höhe skaliert, um das Seitenverhältnis beizubehalten.

Mit anderen Worten, es erfüllt die Definition von scaleType="centerCrop":

http://developer.Android.com/reference/Android/widget/ImageView.ScaleType.html

Skalieren Sie das Bild gleichmäßig (beibehalten Sie das Seitenverhältnis des Bildes), sodass Beide Dimensionen (Breite und Höhe) des Bildes sind gleich odergrößer als die entsprechende Dimension der Ansicht (minus Auffüllen).

package com.mypackage;

import Android.content.Context;
import Android.graphics.drawable.Drawable;
import Android.util.AttributeSet;
import Android.widget.ImageView;

public class FixedCenterCrop extends ImageView
{
    public FixedCenterCrop(final Context context, final AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec)
    {
        final Drawable d = this.getDrawable();

        if(d != null) {
            int height = MeasureSpec.getSize(heightMeasureSpec);
            int width = MeasureSpec.getSize(widthMeasureSpec);

            if(width >= height)
                height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            else
                width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());

            this.setMeasuredDimension(width, height);

        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Diese Lösung arbeitet automatisch im Hoch- oder Querformat. Sie referenzieren es in Ihrem Layout genauso wie in der Lösung von Mark. Z.B.:

<com.mypackage.FixedCenterCrop
    Android:id="@+id/imgLoginBackground"
    Android:src="@drawable/mybackground"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_centerInParent="true"
    Android:scaleType="centerCrop" />
7

Ich hatte das gleiche Problem, aber mit einer festen Höhe und skalierten Breite, die das ursprüngliche Seitenverhältnis des Bildes beibehielt. Ich habe es über ein gewichtetes lineares Layout gelöst. Sie können es hoffentlich an Ihre Bedürfnisse anpassen.

<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:orientation="horizontal">

    <ImageView
        Android:id="@+id/image"
        Android:layout_width="0px"
        Android:layout_height="180dip"
        Android:layout_weight="1.0"
        Android:adjustViewBounds="true"
        Android:scaleType="fitStart" />

</LinearLayout>
3
tbm

Kotlin-Version von Mark Martinssons Antwort :

class DynamicImageView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    val drawable = this.drawable

    if (drawable != null) {
        //Ceil not round - avoid thin vertical gaps along the left/right edges
        val width = View.MeasureSpec.getSize(widthMeasureSpec)
        val height = Math.ceil((width * drawable.intrinsicHeight.toFloat() / drawable.intrinsicWidth).toDouble()).toInt()
        this.setMeasuredDimension(width, height)
    } else {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}
0
ngu

Eine weitere Ergänzung zur Lösung von Mark Matinsson. Ich habe festgestellt, dass einige meiner Bilder größer waren als andere. Also habe ich die Klasse so geändert, dass das Bild mit einem maximalen Faktor skaliert wird. Wenn das Bild zu klein ist, um bei maximaler Breite skaliert zu werden, ohne verschwommen zu werden, wird die Skalierung über die maximale Grenze hinaus beendet.

public class DynamicImageView extends ImageView {

    final int MAX_SCALE_FACTOR = 2;
    public DynamicImageView(final Context context) {
        super(context);
    }

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        final Drawable d = this.getDrawable();

        if (d != null) {
            // ceil not round - avoid thin vertical gaps along the left/right edges
            int width = View.MeasureSpec.getSize(widthMeasureSpec);
            if (width > (d.getIntrinsicWidth()*MAX_SCALE_FACTOR)) width = d.getIntrinsicWidth()*MAX_SCALE_FACTOR;
            final int height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            this.setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}
0
azmath