Skalieren Sie das Bild, um die Breite von ImageView zu füllen und das Seitenverhältnis beizubehalten


198

Ich habe eine GridView. Die Daten von GridViewsind eine Anfrage von einem Server.

Hier ist das Artikellayout in GridView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/analysis_micon_bg"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/half_activity_vertical_margin"
    android:paddingLeft="@dimen/half_activity_horizontal_margin"
    android:paddingRight="@dimen/half_activity_horizontal_margin"
    android:paddingTop="@dimen/half_activity_vertical_margin" >

    <ImageView
        android:id="@+id/ranking_prod_pic"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:contentDescription="@string/app_name"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/ranking_rank_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/ranking_prod_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

Ich fordere Daten vom Server an, erhalte die Bild-URL und lade das Bild auf Bitmap

public static Bitmap loadBitmapFromInputStream(InputStream is) {
    return BitmapFactory.decodeStream(is);
}

public static Bitmap loadBitmapFromHttpUrl(String url) {
    try {
        return loadBitmapFromInputStream((InputStream) (new URL(url).getContent()));
    } catch (Exception e) {
        Log.e(TAG, e.getMessage());
        return null;
    }
}

und da ist der Code der getView(int position, View convertView, ViewGroup parent)Methode im Adapter

Bitmap bitmap = BitmapUtil.loadBitmapFromHttpUrl(product.getHttpUrl());
prodImg.setImageBitmap(bitmap);

Die Bildgröße ist 210*210. Ich führe meine Anwendung auf meinem Nexus 4 aus. Das Bild füllt zwar die ImageViewBreite, aber die ImageViewHöhe skaliert nicht. ImageViewzeigt nicht das ganze Bild.

Wie löse ich dieses Problem?

Antworten:


544

Ohne benutzerdefinierte Klassen oder Bibliotheken zu verwenden:

<ImageView
    android:id="@id/img"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:adjustViewBounds="true"
    android:scaleType="fitCenter" />

scaleType="fitCenter" (Standard, wenn weggelassen)

  • macht es so breit wie es die Eltern erlauben und nach Bedarf nach oben / unten skalieren, wobei das Seitenverhältnis beibehalten wird.

scaleType="centerInside"

  • Wenn die Eigenbreite von srckleiner als die übergeordnete Breite ist,
    wird das Bild horizontal zentriert
  • Wenn die Eigenbreite von srcgrößer als die übergeordnete Breite ist,
    wird sie so breit, wie es die übergeordnete Breite zulässt, und das Seitenverhältnis wird verkleinert.

Es spielt keine Rolle, ob Sie android:srcoder ImageView.setImage*Methoden verwenden und der Schlüssel ist wahrscheinlich der adjustViewBounds.


5
android: layout_height = "wrap_content" android: adjustViewBounds = "true" android: scaleType = "fitCenter" macht den Trick
James Tan

@JamesTan fill_parentfür Breite ist nur eine gute Praxis, eine feste Größe funktioniert auch.
TWiStErRob

@TWiStErRob Ich erkenne, dass es Android braucht: adjustViewBounds = "true" damit, sonst passt es nicht entsprechend für die Höhe. und ja, die Breite für Eltern ist die vollständige Version
James Tan

9
Funktioniert wie Charm unter API 19+, jedoch nicht unter API 16 (getestet). Die benutzerdefinierte Ansicht von Alex Semeniuk funktioniert auch mit API 16.
Ashkan Sarlak

1
@ F.Mysir whops 😅, ja, es spielt keine Rolle, welches für das Problem, aber für die Verbreitung bewährter Verfahren stimme ich voll und ganz zu.
TWiStErRob

42

Ich mag die Antwort von arnefm, aber er hat einen kleinen Fehler gemacht (siehe Kommentare), den ich zu korrigieren versuchen werde:

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

/**
 * ImageView that keeps aspect ratio when scaled
 */
public class ScaleImageView extends ImageView {

  public ScaleImageView(Context context) {
    super(context);
  }

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

  public ScaleImageView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    try {
      Drawable drawable = getDrawable();
      if (drawable == null) {
        setMeasuredDimension(0, 0);
      } else {
        int measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
        int measuredHeight = MeasureSpec.getSize(heightMeasureSpec);
        if (measuredHeight == 0 && measuredWidth == 0) { //Height and width set to wrap_content
          setMeasuredDimension(measuredWidth, measuredHeight);
        } else if (measuredHeight == 0) { //Height set to wrap_content
          int width = measuredWidth;
          int height = width *  drawable.getIntrinsicHeight() / drawable.getIntrinsicWidth();
          setMeasuredDimension(width, height);
        } else if (measuredWidth == 0){ //Width set to wrap_content
          int height = measuredHeight;
          int width = height * drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight();
          setMeasuredDimension(width, height);
        } else { //Width and height are explicitly set (either to match_parent or to exact value)
          setMeasuredDimension(measuredWidth, measuredHeight);
        }
      }
    } catch (Exception e) {
      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  }

}

Auf ImageViewdiese Weise werden Sie richtig skaliert und haben keine Dimensionsprobleme, wenn Sie (zum Beispiel) in setzenScrollView


thx, ich habe die Lösung gefunden, direkt unter der Antwort von arnefm, dem ersten Kommentar, den ich hinzugefügt habe. das thereist ein Link
Joe

36

Ich hatte einmal ein ähnliches Problem. Ich habe es gelöst, indem ich eine benutzerdefinierte ImageView erstellt habe.

public class CustomImageView extends ImageView

Überschreiben Sie dann die onMeasure-Methode der Bildansicht. Ich glaube, ich habe so etwas gemacht:

    @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    try {
        Drawable drawable = getDrawable();

        if (drawable == null) {
            setMeasuredDimension(0, 0);
        } else {
            float imageSideRatio = (float)drawable.getIntrinsicWidth() / (float)drawable.getIntrinsicHeight();
            float viewSideRatio = (float)MeasureSpec.getSize(widthMeasureSpec) / (float)MeasureSpec.getSize(heightMeasureSpec);
            if (imageSideRatio >= viewSideRatio) {
                // Image is wider than the display (ratio)
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = (int)(width / imageSideRatio);
                setMeasuredDimension(width, height);
            } else {
                // Image is taller than the display (ratio)
                int height = MeasureSpec.getSize(heightMeasureSpec);
                int width = (int)(height * imageSideRatio);
                setMeasuredDimension(width, height);
            }
        }
    } catch (Exception e) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

Dadurch wird das Bild so gedehnt, dass es auf den Bildschirm passt, während das Seitenverhältnis beibehalten wird.


1
siehe dort . die erste Antwort
Joe

Nun, diese Antwort ist nicht korrekt. Betrachten Sie die Situation, in der Sie einstellen android:layout_height="wrap_content". In diesem Fall MeasureSpec.getSize(heightMeasureSpec)wird 0und Sie viewSideRatiowird zur Folge hat Infinity(keine Notwendigkeit, Wunder, kann Java - float solche Werte) . In diesem Fall sind beide Ihre heightund widthwird 0.
Alex Semeniuk

1
Damit die Füllung funktioniert, musste ich (imageSideRatio> = viewSideRatio) gegen (imageSideRatio <viewSideRatio) tauschen. Ansonsten eine gute Antwort
Marek Halmo

23

Verwenden Sie android:scaleType="centerCrop".


Nicht genau das, wonach die Frage gefragt wurde, aber genau das, was ich brauchte!
Nickjm

Können Sie @Jameson näher erläutern? Meinst du Android-Versionen?
Mick

Wenn ich centerCrop verwende, wird das Bild oben und unten etwas geschnitten.
Sreekanth Karumanaghat

9

Ich habe etwas Ähnliches wie oben gemacht und dann meinen Kopf ein paar Stunden lang gegen die Wand geschlagen, weil es in einem nicht funktioniert hat RelativeLayout. Am Ende hatte ich folgenden Code:

package com.example;

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

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

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

        if (d != null) {
            int width;
            int height;
            if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
                height = MeasureSpec.getSize(heightMeasureSpec);
                width = (int) Math.ceil(height * (float) d.getIntrinsicWidth() / d.getIntrinsicHeight());
            } else {
                width = MeasureSpec.getSize(widthMeasureSpec);
                height = (int) Math.ceil(width * (float) d.getIntrinsicHeight() / d.getIntrinsicWidth());
            }
            setMeasuredDimension(width, height);
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }
}

Und um zu verhindern RelativeLayout, dass die gemessene Dimension ignoriert wird, habe ich Folgendes getan:

    <FrameLayout
        android:id="@+id/image_frame"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/something">

        <com.example.ScaledImageView
            android:id="@+id/image"
            android:layout_width="wrap_content"
            android:layout_height="150dp"/>
    </FrameLayout>

5

Verwenden Sie diese Eigenschaften in ImageView, um das Seitenverhältnis beizubehalten:

android:adjustViewBounds="true"
android:scaleType="fitXY"

9
fitXY behält das Seitenverhältnis NICHT bei. Das Bild wird so gedehnt, dass es genau der Breite und Höhe des Layouts entspricht.
Verlassener Wagen

5

Dies gilt nicht, wenn Sie das Bild in ImageView als Hintergrund festlegen und es auf src (android: src) einstellen müssen.

Vielen Dank.


5

Gehen Sie wie folgt vor, um ein Bild zu erstellen, dessen Breite der Bildschirmbreite entspricht und dessen Höhe proportional zum Seitenverhältnis festgelegt wird.

Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
                    @Override
                    public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {

                        // creating the image that maintain aspect ratio with width of image is set to screenwidth.
                        int width = imageView.getMeasuredWidth();
                        int diw = resource.getWidth();
                        if (diw > 0) {
                            int height = 0;
                            height = width * resource.getHeight() / diw;
                            resource = Bitmap.createScaledBitmap(resource, width, height, false);
                        }
                                              imageView.setImageBitmap(resource);
                    }
                });

Hoffe das hilft.


Dies ist die beste Antwort, die ich gefunden habe. Ich suche nach 2 Tagen, danke Fathima
Karthick Ramanathan

4

Versuchen Sie dies: Es hat das Problem für mich gelöst

   android:adjustViewBounds="true"
    android:scaleType="fitXY"

4

Du brauchst keinen Java-Code. Du musst nur :

<ImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:adjustViewBounds="true"
    android:scaleType="centerCrop" />

Der Schlüssel befindet sich im übergeordneten Element für Breite und Höhe


centerCrop schneidet tatsächlich einen Teil des Bildes.
Sreekanth Karumanaghat

2

Versuchen Sie es mit dieser einfachen Zeile ... fügen Sie diese Zeile in Ihren XML-Code im Bildansicht-Tag ein, ohne eine Abhängigkeit hinzuzufügen. android: scaleType = "fitXY"


fitXY behält das Seitenverhältnis nicht bei, sodass das Bild wahrscheinlich gestreckt wird
ProjectDelta

2

benutze android: ScaleType = "fitXY" im ImageView xml


fitXY behält das Seitenverhältnis nicht bei, sodass das Bild wahrscheinlich gestreckt wird
ProjectDelta

1

Sie können versuchen, das zu tun, was Sie tun, indem Sie die Bilder manuell laden. Ich würde jedoch dringend empfehlen, einen Blick auf Universal Image Loader zu werfen .

Ich habe es kürzlich in mein Projekt integriert und ich muss sagen, es ist fantastisch. Macht all die Sorgen, Dinge asynchron zu machen, die Größe zu ändern und Bilder zwischenzuspeichern. Es ist wirklich einfach zu integrieren und einzurichten. Innerhalb von 5 Minuten können Sie es wahrscheinlich dazu bringen, das zu tun, was Sie wollen.

Beispielcode:

//ImageLoader config
DisplayImageOptions displayimageOptions = new DisplayImageOptions.Builder().showStubImage(R.drawable.downloadplaceholder).cacheInMemory().cacheOnDisc().showImageOnFail(R.drawable.loading).build();

    ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()).
            defaultDisplayImageOptions(displayimageOptions).memoryCache(new WeakMemoryCache()).discCache(new UnlimitedDiscCache(cacheDir)).build();

    if (ImageLoader.getInstance().isInited()) {
        ImageLoader.getInstance().destroy();
    }
    ImageLoader.getInstance().init(config);

    imageLoadingListener = new ImageLoadingListener() {
        @Override
        public void onLoadingStarted(String s, View view) {

        }

        @Override
        public void onLoadingFailed(String s, View view, FailReason failReason) {
            ImageView imageView = (ImageView) view;
            imageView.setImageResource(R.drawable.android);
            Log.i("Failed to Load " + s, failReason.toString());
        }

        @Override
        public void onLoadingComplete(String s, View view, Bitmap bitmap) {

        }

        @Override
        public void onLoadingCancelled(String s, View view) {

        }
    };

//Imageloader usage
ImageView imageView = new ImageView(getApplicationContext());
    if (orientation == 1) {
        imageView.setLayoutParams(new LinearLayout.LayoutParams(width / 6, width / 6));
    } else {
        imageView.setLayoutParams(new LinearLayout.LayoutParams(height / 6, height / 6));
    }
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageLoader.displayImage(SERVER_HOSTNAME + "demos" + demo.getPathRoot() + demo.getRootName() + ".png", imageView, imageLoadingListener);

Dadurch können die Bilder verzögert geladen, korrekt an die Größe der Bildansicht angepasst werden, in der beim Laden ein Platzhalterbild angezeigt wird, und ein Standardsymbol angezeigt werden, wenn das Laden fehlschlägt und die Ressourcen zwischengespeichert werden.

- Ich sollte auch hinzufügen, dass diese aktuelle Konfiguration das Bildseitenverhältnis beibehält und somit auf Ihre ursprüngliche Frage anwendbar ist


0

Verwenden Sie einfach UniversalImageLoader und legen Sie fest

DisplayImageOptions.Builder()
    .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)
    .build();

und keine Skalierungseinstellungen in ImageView


0

Ich hatte ein ähnliches Problem. Ich fand den Grund dafür, dass Sie das berechnen müssen dp. Android Studio berechnet, ImageViewwann Sie es von der laden drawable, aber wenn Sie eine andere Methode verwenden, wie das Laden von Bitmap, wird das dpnicht automatisch berücksichtigt.

Hier ist meine XML

<ImageView
  android:id="@+id/imageViewer"
  android:layout_width="match_parent"
  android:layout_height="match_parent"//dp is not automaticly updated, when loading from a other source
  android:scaleType="fitCenter"
  tools:srcCompat="@drawable/a8" />

Ich verwende Kotlin und lade Drawable aus einer Asset-Datei. So berechne ich das

val d = Drawable.createFromStream(assets.open("imageData/${imageName}.png"), null)
bitHeight = d.minimumHeight//get the image height
imageViewer.layoutParams.height = (bitHeight * resources.displayMetrics.density).toInt()//set the height
imageViewer.setImageDrawable(d)//set the image from the drawable
imageViewer.requestLayout()//here I apply it to the layout

-1

Verwenden Sie Picasso, das einfach zu bedienen ist.

In Ihrem Adapter ..

@Override
public void getView(int position, View convertView, ViewGroup parent) {

 ImageView view = (ImageView) convertView.findViewById(R.id.ranking_prod_pic);

 Picasso.with(context).load(url).into(view); //url is image url

 //you can resize image if you want

 /* Picasso.with(context) .load(url) .resize(50, 50) .centerCrop() .into(view) */

}

http://square.github.io/picasso/ Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.