web-dev-qa-db-fra.com

Android: Comment étirer une image à la largeur de l'écran tout en maintenant les proportions?

Je souhaite télécharger une image (de taille inconnue, mais toujours approximativement carrée) et l'afficher de manière à ce qu'elle remplisse l'écran horizontalement et s'étire verticalement pour conserver le rapport de format de l'image, quelle que soit la taille de l'écran. Voici mon code (non fonctionnel). Il étire l'image horizontalement, mais pas verticalement, elle est donc écrasée ...

ImageView mainImageView = new ImageView(context);
    mainImageView.setImageBitmap(mainImage); //downloaded from server
    mainImageView.setScaleType(ScaleType.FIT_XY);
    //mainImageView.setAdjustViewBounds(true); 
    //with this line enabled, just scales image down
    addView(mainImageView,new LinearLayout.LayoutParams( 
            LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
118
fredley

J'ai accompli cela avec une vue personnalisée. Définissez layout_width = "fill_parent" et layout_height = "wrap_content" et pointez-le sur le dessin approprié:

public class Banner extends View {

  private final Drawable logo;

  public Banner(Context context) {
    super(context);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs) {
    super(context, attrs);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  public Banner(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    logo = context.getResources().getDrawable(R.drawable.banner);
    setBackgroundDrawable(logo);
  }

  @Override protected void onMeasure(int widthMeasureSpec,
      int heightMeasureSpec) {
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = width * logo.getIntrinsicHeight() / logo.getIntrinsicWidth();
    setMeasuredDimension(width, height);
  }
}
132
Bob Lee

Au final, j'ai généré les dimensions manuellement, ce qui fonctionne très bien:

DisplayMetrics dm = new DisplayMetrics();
context.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = width * mainImage.getHeight() / mainImage.getWidth(); //mainImage is the Bitmap I'm drawing
addView(mainImageView,new LinearLayout.LayoutParams( 
        width, height));
24
fredley

Je viens de lire le code source de ImageView et il est fondamentalement impossible de ne pas utiliser les solutions de sous-classement de ce fil. Dans ImageView.onMeasure nous arrivons à ces lignes:

        // Get the max possible width given our constraints
        widthSize = resolveAdjustedSize(w + pleft + pright, mMaxWidth, widthMeasureSpec);

        // Get the max possible height given our constraints
        heightSize = resolveAdjustedSize(h + ptop + pbottom, mMaxHeight, heightMeasureSpec);

h et w sont les dimensions de l'image et p* est le rembourrage.

Puis:

private int resolveAdjustedSize(int desiredSize, int maxSize,
                               int measureSpec) {
    ...
    switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            /* Parent says we can be as big as we want. Just don't be larger
               than max size imposed on ourselves.
            */
            result = Math.min(desiredSize, maxSize);

Donc, si vous avez un layout_height="wrap_content" il va régler widthSize = w + pleft + pright, ou en d'autres termes, la largeur maximale est égale à la largeur de l'image.

Cela signifie que sauf si vous définissez une taille exacte, les images ne sont JAMAIS agrandies. Je considère cela comme un bogue, mais bonne chance pour que Google en prenne connaissance ou le corrige. Edit: En mangeant mes propres mots, j'ai soumis n rapport de bogue et ils disent que cela a été corrigé dans une prochaine version !

Une autre solution

Voici une autre solution de contournement, mais vous devriez (en théorie, je ne l’ai pas vraiment testé!) Pouvez l’utiliser où que vous soyez ImageView. Pour l'utiliser set layout_width="match_parent", et layout_height="wrap_content". C'est aussi beaucoup plus général que la solution acceptée. Par exemple. vous pouvez faire de l'ajustement à la hauteur ainsi que de l'ajustement à la largeur.

import Android.content.Context;
import Android.util.AttributeSet;
import Android.widget.ImageView;

// This works around the issue described here: http://stackoverflow.com/a/12675430/265521
public class StretchyImageView extends ImageView
{

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        // Call super() so that resolveUri() is called.
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // If there's no drawable we can just use the result from super.
        if (getDrawable() == null)
            return;

        final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        int w = getDrawable().getIntrinsicWidth();
        int h = getDrawable().getIntrinsicHeight();
        if (w <= 0)
            w = 1;
        if (h <= 0)
            h = 1;

        // Desired aspect ratio of the view's contents (not including padding)
        float desiredAspect = (float) w / (float) h;

        // We are allowed to change the view's width
        boolean resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;

        // We are allowed to change the view's height
        boolean resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;

        int pleft = getPaddingLeft();
        int pright = getPaddingRight();
        int ptop = getPaddingTop();
        int pbottom = getPaddingBottom();

        // Get the sizes that ImageView decided on.
        int widthSize = getMeasuredWidth();
        int heightSize = getMeasuredHeight();

        if (resizeWidth && !resizeHeight)
        {
            // Resize the width to the height, maintaining aspect ratio.
            int newWidth = (int) (desiredAspect * (heightSize - ptop - pbottom)) + pleft + pright;
            setMeasuredDimension(newWidth, heightSize);
        }
        else if (resizeHeight && !resizeWidth)
        {
            int newHeight = (int) ((widthSize - pleft - pright) / desiredAspect) + ptop + pbottom;
            setMeasuredDimension(widthSize, newHeight);
        }
    }
}
21
Timmmm

La définition de adjustViewBounds sur true et l'utilisation d'un groupe de vues LinearLayout ont très bien fonctionné pour moi. Pas besoin de sous-classer ou de demander des métriques de périphérique:

//NOTE: "this" is a subclass of LinearLayout
ImageView splashImageView = new ImageView(context);
splashImageView.setImageResource(R.drawable.splash);
splashImageView.setAdjustViewBounds(true);
addView(splashImageView);
17
Matt Stoker

J'ai eu du mal à résoudre ce problème sous une forme ou une autre pour AGES, merci, merci, MERCI .... :)

Je voulais juste souligner que vous pouvez obtenir une solution généralisable à partir de ce que Bob Lee a fait en développant simplement View et en remplaçant onMeasure. De cette façon, vous pouvez utiliser ceci avec tout dessinable que vous voulez, et ça ne casse pas s'il n'y a pas d'image:

    public class CardImageView extends View {
        public CardImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
        }

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

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

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Drawable bg = getBackground();
            if (bg != null) {
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * bg.getIntrinsicHeight() / bg.getIntrinsicWidth();
                setMeasuredDimension(width,height);
            }
            else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
    }
13
duhrer

Dans certains cas, cette formule magique résout magnifiquement le problème.

Pour les personnes aux prises avec ce problème provenant d'une autre plate-forme, l'option "taille et forme adaptées" est parfaitement gérée dans Android, mais difficile à trouver.

Vous voulez généralement cette combinaison:

  • parent de correspondance de largeur,
  • contenu en hauteur,
  • adjustViewBounds activé (sic)
  • échelle fitCenter
  • cropToPadding OFF (sic)

Ensuite, c'est automatique et incroyable.

Si vous êtes un développeur iOS, il est vraiment étonnant de voir avec quelle simplicité vous pouvez faire des "hauteurs de cellules totalement dynamiques" dans une vue sous forme de tableau ... euh, je veux dire ListView. Prendre plaisir.

<com.parse.ParseImageView
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:id="@+id/post_image"
    Android:src="@drawable/icon_192"
    Android:layout_margin="0dp"
    Android:cropToPadding="false"
    Android:adjustViewBounds="true"
    Android:scaleType="fitCenter"
    Android:background="#eff2eb"/>

enter image description here

10
Fattie

J'ai réussi à atteindre cet objectif en utilisant ce code XML uniquement. Eclipse ne rend peut-être pas la hauteur voulue pour indiquer qu’elle s’agrandit pour s’adapter; Cependant, lorsque vous l'exécutez sur un périphérique, celui-ci affiche correctement le résultat souhaité. (enfin au moins pour moi)

<FrameLayout
     Android:layout_width="match_parent"
     Android:layout_height="wrap_content">

     <ImageView
          Android:layout_width="match_parent"
          Android:layout_height="wrap_content"
          Android:adjustViewBounds="true"
          Android:scaleType="centerCrop"
          Android:src="@drawable/whatever" />
</FrameLayout>
5
NeilDA

Tout le monde le fait par programmation, alors j'ai pensé que cette réponse conviendrait parfaitement ici. Ce code a fonctionné pour mon xml. Je ne pensais pas encore au rapport, mais je voulais toujours placer cette réponse si cela pouvait aider quelqu'un.

Android:adjustViewBounds="true"

À votre santé..

5
Sindri Þór

Je l'ai fait avec ces valeurs dans un LinearLayout:

Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
5
helduel

Une solution très simple consiste simplement à utiliser les fonctionnalités fournies par RelativeLayout.

Voici le xml qui rend possible avec standard Android Views:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:fillViewport="true">

    <RelativeLayout 
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        >
        <LinearLayout
            Android:id="@+id/button_container"
            Android:layout_width="match_parent"
            Android:layout_height="wrap_content"
            Android:orientation="vertical"
            Android:layout_alignParentBottom="true"
            >
            <Button
                Android:text="button"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"/>
            <Button
                Android:text="button"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"/>
            <Button
                Android:text="button"
                Android:layout_width="wrap_content"
                Android:layout_height="wrap_content"/>
        </LinearLayout>
        <ImageView 
            Android:src="@drawable/cat"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:adjustViewBounds="true"
            Android:scaleType="centerCrop"
            Android:layout_above="@id/button_container"/>
    </RelativeLayout>
</ScrollView>

L'astuce consiste à définir le ImageView pour qu'il remplisse l'écran, mais il doit être placé au-dessus des autres dispositions. De cette façon, vous réalisez tout ce dont vous avez besoin.

3
Warpzit

Sa simple question de réglage adjustViewBounds="true" et scaleType="fitCenter" dans le fichier XML pour ImageView!

<ImageView
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content"
        Android:src="@drawable/image"

        Android:adjustViewBounds="true"
        Android:scaleType="fitCenter"
        />

Remarque: layout_width est réglé sur match_parent

2
Kaushik NP

ScaleType.CENTER_CROP fera ce que vous voulez: étirer à la largeur totale et redimensionner la hauteur en conséquence. si la hauteur mise à l'échelle dépasse les limites de l'écran, l'image sera recadrée.

1
P.Melch

Vous pouvez utiliser mon StretchableImageView tout en préservant les proportions (en largeur ou en hauteur) en fonction de la largeur et de la hauteur du dessin:

import Android.content.Context;
import Android.util.AttributeSet;
import Android.widget.ImageView;

public class StretchableImageView extends ImageView{

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if(getDrawable()!=null){
            if(getDrawable().getIntrinsicWidth()>=getDrawable().getIntrinsicHeight()){
                int width = MeasureSpec.getSize(widthMeasureSpec);
                int height = width * getDrawable().getIntrinsicHeight()
                            / getDrawable().getIntrinsicWidth();
                setMeasuredDimension(width, height);
            }else{
                int height = MeasureSpec.getSize(heightMeasureSpec);
                int width = height * getDrawable().getIntrinsicWidth()
                            / getDrawable().getIntrinsicHeight();
                setMeasuredDimension(width, height);
            }
        }
    }
}
1
valerybodak

Vous définissez le ScaleType sur ScaleType.FIT_XY. Selon le javadocs , cela étendra l'image à la totalité de la zone, en modifiant le rapport de format si nécessaire. Cela expliquerait le comportement que vous observez.

Pour obtenir le comportement souhaité, FIT_CENTER, FIT_START ou FIT_END sont proches, mais si l'image est plus étroite que haute, elle ne commencera pas à remplir la largeur. Vous pouvez toutefois voir comment celles-ci sont mises en œuvre, et vous devriez probablement être en mesure de déterminer comment l'adapter à vos besoins.

1
Cheryl Simon

Regardez, il existe une solution beaucoup plus facile à votre problème:

ImageView imageView;

protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_layout);
    imageView =(ImageView)findViewById(R.id.your_imageView);
    Bitmap imageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.your_image);
    Point screenSize = new Point();
    getWindowManager().getDefaultDisplay().getSize(screenSize);
    Bitmap temp = Bitmap.createBitmap(screenSize.x, screenSize.x, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(temp);
    canvas.drawBitmap(imageBitmap,null, new Rect(0,0,screenSize.x,screenSize.x), null);
    imageView.setImageBitmap(temp);
}
1
user3673209

Cela fonctionne bien selon mes besoins

<ImageView Android:id="@+id/imgIssue" Android:layout_width="fill_parent" Android:layout_height="wrap_content" Android:adjustViewBounds="true" Android:scaleType="fitXY"/>

0
Anand Savjani

Pour moi, Android: scaleType = "centerCrop" n'a pas résolu mon problème. En fait, l'image augmentait beaucoup plus. J'ai donc essayé avec Android: scaleType = "fitXY" et cela a fonctionné parfaitement.

0
Ricardo