Mettre l'image à l'échelle pour remplir la largeur d'ImageView et conserver les proportions

198

J'en ai un GridView. Les données GridViewsont demandées par un serveur.

Voici la disposition des éléments dans 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>

Je demande des données au serveur, j'obtiens l'URL de l'image et charge l'image sur 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;
    }
}

et il y a le code de getView(int position, View convertView, ViewGroup parent)méthode dans l'adaptateur

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

La taille de l'image est 210*210. J'exécute mon application sur mon Nexus 4. L'image remplit la ImageViewlargeur, mais la ImageViewhauteur n'est pas mise à l'échelle. ImageViewn'affiche pas l'image entière.

Comment résoudre ce problème?

Joe
la source

Réponses:

544

Sans utiliser de classes ou de bibliothèques personnalisées:

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

scaleType="fitCenter" (par défaut en cas d'omission)

  • le rendra aussi large que le parent le permet et montera / descendra l'échelle selon les besoins en gardant le rapport hauteur / largeur.

scaleType="centerInside"

  • si la largeur intrinsèque de srcest inférieure à la largeur parente centrera
    l'image horizontalement
  • si la largeur intrinsèque de srcest supérieure à la largeur du parent, elle
    sera aussi large que le parent le permet et le rapport d'aspect sera réduit.

Peu importe si vous utilisez des méthodes android:srcou ImageView.setImage*et la clé est probablement la adjustViewBounds.

TWiStErRob
la source
5
android: layout_height = "wrap_content" android: adjustViewBounds = "true" android: scaleType = "fitCenter" fait l'affaire
James Tan
@JamesTan fill_parentpour la largeur n'est qu'une bonne pratique, une taille fixe fonctionne également.
TWiStErRob
@TWiStErRob je me rends compte qu'il a besoin d'Android: adjustViewBounds = "true" avec lui, sinon il ne convient pas en conséquence pour la hauteur. et oui, la largeur pour s'adapter au parent est la version complète
James Tan
9
Fonctionne comme charme sur API 19+, mais pas sur API 16 (testé). La vue personnalisée d'Alex Semeniuk fonctionne également sur l'API 16.
Ashkan Sarlak
1
@ F.Mysir whops 😅, oui, peu importe lequel pour le problème, mais pour diffuser les bonnes pratiques, je suis totalement d'accord.
TWiStErRob
42

J'aime la réponse de arnefm mais il a fait une petite erreur (voir commentaires) que je vais essayer de corriger:

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

}

Ainsi, votre ImageViewsera mis à l'échelle correctement et n'aura aucun problème de dimension si (par exemple) mis à l'intérieur deScrollView

Alex Semeniuk
la source
thx, j'ai trouvé la solution, juste en dessous de la réponse de arnefm, le premier commentaire que j'ai ajouté. c'est thereun lien
Joe
36

J'ai eu un problème similaire une fois. Je l'ai résolu en créant une ImageView personnalisée.

public class CustomImageView extends ImageView

Remplacez ensuite la méthode onMeasure de l'imageview. J'ai fait quelque chose comme ça, je crois:

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

Cela étirera l'image pour l'adapter à l'écran tout en conservant le rapport d'aspect.

arnefm
la source
1
voir - bas . la première réponse
Joe
Eh bien, cette réponse n'est pas exacte. Considérez la situation dans laquelle vous vous placez android:layout_height="wrap_content". Dans ce cas, ce MeasureSpec.getSize(heightMeasureSpec)sera 0et votre viewSideRatiorésultat Infinity(pas besoin de vous demander, Java float autorise de telles valeurs.) Dans ce cas, votre heightet widthsera 0.
Alex Semeniuk
1
Pour que je fasse le remplissage, je devais échanger (imageSideRatio> = viewSideRatio) vers (imageSideRatio <viewSideRatio) .. sinon une bonne réponse
Marek Halmo
23

Utilisez android:scaleType="centerCrop".

Mark Buikema
la source
Pas exactement ce que la question demandait, mais exactement ce dont j'avais besoin!
nickjm
Pouvez-vous élaborer @Jameson? Voulez-vous dire les versions Android?
Mick
Si j'utilise centerCrop, l'image se coupe un peu en haut et en bas.
Sreekanth Karumanaghat
9

J'ai fait quelque chose de similaire à ce qui précède, puis je me suis cogné la tête contre le mur pendant quelques heures car cela ne fonctionnait pas à l'intérieur d'un RelativeLayout. Je me suis retrouvé avec le code suivant:

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

Et puis pour éviter RelativeLayoutd'ignorer la dimension mesurée, j'ai fait ceci:

    <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>
Dave Cole
la source
5

Utilisez ces propriétés dans ImageView pour conserver les proportions:

android:adjustViewBounds="true"
android:scaleType="fitXY"
Atif Mahmood
la source
9
fitXY ne conserve PAS le rapport hauteur / largeur. Il étire l'image pour s'adapter exactement à la largeur et à la hauteur de la mise en page.
Panier abandonné
5

Cela ne sera pas applicable si vous définissez l'image comme arrière-plan dans ImageView, devez la définir sur src (android: src).

Merci.

Guna Sekhar
la source
5

Pour créer une image dont la largeur est égale à la largeur de l'écran et la hauteur définie proportionnellement en fonction du rapport d'aspect, procédez comme suit.

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

J'espère que cela t'aides.

Fathima km
la source
c'est la meilleure réponse que j'ai trouvée, je cherche depuis 2 jours, merci fathima
Karthick Ramanathan
4

Essayez ceci: cela a résolu le problème pour moi

   android:adjustViewBounds="true"
    android:scaleType="fitXY"
Esprit d'ingénieur
la source
4

Vous n'avez pas besoin de code java. Il vous suffit de:

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

La clé est dans le parent de correspondance pour la largeur et la hauteur

Romu Dizzy
la source
centerCrop coupe en fait une partie de l'image.
Sreekanth Karumanaghat
2

essayez avec cette ligne simple ... ajoutez cette ligne dans votre code xml dans la balise de vue d'image sans ajouter de dépendance android: scaleType = "fitXY"

ziddi609
la source
fitXY ne maintient pas le rapport hauteur / largeur, donc l'image sera probablement étirée
ProjectDelta
2

utiliser android: ScaleType = "fitXY" im ImageView xml

Naveed Naeem
la source
fitXY ne maintient pas le rapport hauteur / largeur, donc l'image sera probablement étirée
ProjectDelta
1

Vous pouvez essayer de faire ce que vous faites en chargeant manuellement les images, mais je recommanderais très fortement de jeter un œil à Universal Image Loader .

Je l'ai récemment intégré à mon projet et je dois dire que c'est fantastique. Fait tout le souci de rendre les choses asynchrones, de redimensionner, de mettre en cache des images pour vous. C'est très facile à intégrer et à configurer. En 5 minutes, vous pouvez probablement le faire faire ce que vous voulez.

Exemple de code:

//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);

Cela peut paresseusement charger les images, les adapter correctement à la taille de l'imageView montrant une image d'espace réservé pendant le chargement, et montrant une icône par défaut si le chargement échoue et la mise en cache des ressources.

- Je dois également ajouter que cette configuration actuelle conserve le rapport hauteur / largeur de l'image, donc applicable à votre question d'origine

rcbevans
la source
0

Utilisez simplement UniversalImageLoader et définissez

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

et aucun paramètre d'échelle sur ImageView

Artem
la source
0

J'ai eu un problème similaire, j'ai trouvé la raison de cela parce que vous devez calculer le dp. Android studio calcule le ImageViewmoment où vous le chargez depuis le drawable, mais lorsque vous utilisez une autre méthode, comme le chargement à partir du bitmap, le dpn'est pas automatiquement pris en compte,

Voici mon 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" />

J'utilise Kotlin et je charge le dessin à partir d'un fichier d'actif, voici comment je calcule cela

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
DarkPh03n1X
la source
-1

Utilisez picasso qui est facile à utiliser ..

Dans votre adaptateur ..

@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/ entrez la description de l'image ici

chiragkyada
la source