web-dev-qa-db-fra.com

Android ImageView scale image plus petite à la largeur avec une hauteur flexible sans recadrage ni distorsion

Souvent demandé, jamais répondu (du moins pas de manière reproductible).

J'ai une vue d'image avec une image plus petite que la vue. Je souhaite redimensionner l'image à la largeur de l'écran et ajuster la hauteur du ImageView pour refléter la hauteur proportionnellement correcte de l'image.

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

Il en résulte que l'image est centrée sur sa taille d'origine (inférieure à la largeur de l'écran) avec des marges latérales. Pas bien.

Alors j'ai ajouté

Android:adjustViewBounds="true" 

Même effet, pas bon. J'ai ajouté

Android:scaleType="centerInside"

Même effet, pas bon. J'ai changé centerInside en fitCenter. Même effet, pas bon. J'ai changé centerInside en centerCrop

Android:scaleType="centerCrop"

Maintenant, enfin, l'image est mise à l'échelle à la largeur de l'écran - mais recadrée en haut et en bas! J'ai donc changé centerCrop en fitXY

Android:scaleType="fitXY"

Maintenant, l'image est redimensionnée à la largeur de l'écran, mais pas est redimensionnée sur l'axe des ordonnées, ce qui donne une image déformée.

Supprimer Android:adjustViewBounds="true" n'a aucun effet. L'ajout d'un Android:layout_gravity, comme suggéré ailleurs, n'a encore aucun effet. 

J'ai essayé d'autres combinaisons - en vain. Alors, s'il vous plaît, est-ce que quelqu'un sait:

Comment configurez-vous le XML d'un ImageView pour qu'il remplisse la largeur de l'écran, redimensionne une image plus petite afin de remplir la totalité de la vue, en affichant l'image avec son rapport de format sans déformation ni recadrage?

EDIT: J'ai également essayé de définir une hauteur numérique arbitraire. Cela n'a d'effet qu'avec le paramètre centerCrop. Cela déformera l'image verticalement en fonction de la hauteur de vue.

73
Mundi

Il n’existe pas de solution viable dans le standard de présentation XML. 

Le seul moyen fiable de réagir à une taille d'image dynamique consiste à utiliser LayoutParams dans le code. 

Décevant.

20
Mundi

J'ai résolu ce problème en créant une classe Java que vous incluez dans votre fichier de mise en page:

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

Maintenant, vous utilisez ceci en ajoutant votre classe à votre fichier de mise en page:

<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

J'ai eu le même problème que le vôtre. Après 30 minutes d’essai de variations différentes, j’ai résolu le problème de cette façon. Il vous donne la hauteur flexible et largeur ajustée à la largeur parente

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

Calculez une échelle qui conservera le rapport hauteur/largeur d'origine, mais garantira également que src s'intègre parfaitement à dst. Au moins un axe (X ou Y) conviendra parfaitement. Le résultat est centré dans dst.

modifier:

Le problème ici est que le layout_height="wrap_content" ne permet pas à l'image de se développer. Vous devrez définir une taille pour cela, pour ce changement

  Android:layout_height="wrap_content"

à

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

edit2:

fonctionne bien:

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

C'est un petit ajout à l'excellente solution de Mark Martinsson.

Si la largeur de votre image est supérieure à sa hauteur, la solution de Mark laissera de l'espace en haut et en bas de l'écran.

La solution ci-dessous résout ce problème en comparant d’abord la largeur et la hauteur: si la largeur de l’image est supérieure à la hauteur, elle redimensionnera la hauteur en fonction de la hauteur de l’écran, puis redimensionnera la largeur pour préserver le rapport de format. De même, si la hauteur de l'image est supérieure à la largeur, la largeur sera redimensionnée pour correspondre à la largeur de l'écran, puis la hauteur pour conserver le rapport de format.

En d'autres termes, il répond correctement à la définition de scaleType="centerCrop":

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

Redimensionne l'image uniformément (conserve le rapport de format de l'image) de sorte que les deux dimensions (largeur et hauteur) de l'image seront égales à ouplus grand que la dimension correspondante de la vue (moins le remplissage).

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

Cette solution fonctionne automatiquement en mode portrait ou paysage. Vous le référencez dans votre mise en page comme dans la solution de Mark. Par exemple.:

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

J'ai rencontré le même problème, mais avec une hauteur fixe, une largeur réduite en conservant le format d'image original. Je l'ai résolu via une mise en page linéaire pondérée. Espérons que vous pourrez le modifier pour vos besoins.

<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

Version Kotlin du answer de Mark Martinsson:

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

Un ajout de plus à la solution de Mark Matinsson. J'ai trouvé que certaines de mes images étaient plus grandes que d'autres. J'ai donc modifié la classe pour que l'image soit mise à l'échelle avec un facteur maximal. Si l'image est trop petite pour être redimensionnée à la largeur maximale sans devenir floue, elle s'arrête au-delà de la limite maximale.

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