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

Je souhaite télécharger une image (de taille inconnue, mais toujours approximativement carrée) et l’afficher afin qu’elle remplisse l’écran horizontalement et s’étende verticalement pour conserver le rapport hauteur / largeur de l’image, quelle que soit la taille de l’écran. Voici mon code (non fonctionnel). Il étire l’image horizontalement, mais pas verticalement, donc il est écrasé …

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

J’ai accompli ceci 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, AtsortingbuteSet attrs) { super(context, attrs); logo = context.getResources().getDrawable(R.drawable.banner); setBackgroundDrawable(logo); } public Banner(Context context, AtsortingbuteSet 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.getInsortingnsicHeight() / logo.getInsortingnsicWidth(); setMeasuredDimension(width, height); } } 

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

 DisplayMesortingcs dm = new DisplayMesortingcs(); context.getWindowManager().getDefaultDisplay().getMesortingcs(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)); 

Je viens de lire le code source pour ImageView et c’est fondamentalement impossible sans utiliser les solutions de sous-classement dans ce thread. 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 remplissage.

Et alors:

 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 définira widthSize = w + pleft + pright , ou en d’autres termes, la largeur maximale est égale à la largeur de l’image.

Cela signifie que si vous ne définissez pas une taille exacte, les images ne sont JAMAIS agrandies . Je considère cela comme un bug, mais bonne chance pour que Google prenne en compte ou corrige le problème. Edit: Manger mes propres mots, j’ai soumis un rapport de bogue et ils disent qu’il a été corrigé dans une prochaine version!

Une autre solution

Voici une autre solution de contournement sous-classée, mais vous devriez (en théorie, je n’ai pas vraiment beaucoup testé!) Être capable de l’utiliser n’importe où avec ImageView . Pour l’utiliser, utilisez layout_width="match_parent" et layout_height="wrap_content" . C’est beaucoup plus général que la solution acceptée. Par exemple, vous pouvez faire de l’ajustement à la taille ainsi que de l’ajustement à la largeur.

 import android.content.Context; import android.util.AtsortingbuteSet; 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, AtsortingbuteSet attrs) { super(context, attrs); } public StretchyImageView(Context context, AtsortingbuteSet 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().getInsortingnsicWidth(); int h = getDrawable().getInsortingnsicHeight(); 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); } } } 

Définir adjustViewBounds sur true et utiliser un groupe de vues LinearLayout a très bien fonctionné pour moi. Pas besoin de sous-classer ou de demander des mésortingques 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); 

J’ai lutté contre 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 étendant simplement View et en remplaçant onMeasure. De cette façon, vous pouvez l’utiliser avec n’importe quel dessin que vous voulez, et cela ne se cassera pas s’il n’y a pas d’image:

  public class CardImageView extends View { public CardImageView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } public CardImageView(Context context, AtsortingbuteSet 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.getInsortingnsicHeight() / bg.getInsortingnsicWidth(); setMeasuredDimension(width,height); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } } 

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

Pour ceux qui ont des problèmes avec une autre plate-forme, l’option “taille et forme à adapter” est gérée de manière optimale dans Android, mais c’est difficile à trouver.

Vous souhaitez généralement cette combinaison:

  • parenté de correspondance de largeur,
  • contenu d’enveloppe de taille,
  • adjustViewBounds activé (sic)
  • fitCenter
  • cropToPadding OFF (sic)

Alors c’est automatique et incroyable.

Si vous êtes un développeur iOS, il est tout à fait étonnant de voir comment vous pouvez simplement faire des “tailles de cellules totalement dynamics” dans une vue de tableau. Prendre plaisir.

  

entrer la description de l'image ici

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 

J’ai réussi à atteindre cet objective en utilisant uniquement ce code XML. Il se peut que l’éclipse ne rend pas la hauteur pour montrer son expansion pour s’adapter; Cependant, lorsque vous l’exécutez sur un périphérique, il affiche correctement le résultat souhaité. (enfin au moins pour moi)

    

Tout le monde le fait par programmation, alors je pensais que cette réponse irait parfaitement ici. Ce code a fonctionné pour moi dans le xml. Je ne pense pas encore au ratio, mais je voulais quand même poser cette réponse si cela pouvait aider quelqu’un.

 android:adjustViewBounds="true" 

À votre santé..

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

Voici le xml qui rend possible avec les Views Android standard:

 < ?xml version="1.0" encoding="utf-8"?>           

L’astuce est que vous définissez le ImageView pour remplir l’écran, mais il doit être au-dessus des autres mises en page. De cette façon, vous obtenez tout ce dont vous avez besoin.

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

  

Remarque: layout_width est défini sur match_parent

Vous définissez le ScaleType sur ScaleType.FIT_XY. Selon les javadocs , cela va étirer l’image pour l’adapter à toute la zone, en modifiant le rapport d’aspect si nécessaire. Cela expliquerait le comportement que vous voyez.

Pour obtenir le comportement souhaité, FIT_CENTER, FIT_START ou FIT_END sont proches, mais si l’image est plus étroite que grande, elle ne commencera pas à remplir la largeur. Vous pouvez cependant voir comment ceux-ci sont mis en œuvre et vous devriez probablement être en mesure de déterminer comment les ajuster à vos besoins.

ScaleType.CENTER_CROP fera ce que vous voulez: étirer sur toute la largeur 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.

Regardez, il y a 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); } 

Vous pouvez utiliser mon StretchableImageView en conservant le rapport hauteur / largeur en fonction de la largeur et de la hauteur du dessin:

 import android.content.Context; import android.util.AtsortingbuteSet; import android.widget.ImageView; public class StretchableImageView extends ImageView{ public StretchableImageView(Context context) { super(context); } public StretchableImageView(Context context, AtsortingbuteSet attrs) { super(context, attrs); } public StretchableImageView(Context context, AtsortingbuteSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if(getDrawable()!=null){ if(getDrawable().getInsortingnsicWidth()>=getDrawable().getInsortingnsicHeight()){ int width = MeasureSpec.getSize(widthMeasureSpec); int height = width * getDrawable().getInsortingnsicHeight() / getDrawable().getInsortingnsicWidth(); setMeasuredDimension(width, height); }else{ int height = MeasureSpec.getSize(heightMeasureSpec); int width = height * getDrawable().getInsortingnsicWidth() / getDrawable().getInsortingnsicHeight(); setMeasuredDimension(width, height); } } } } 

Pour moi, l’android: scaleType = “centerCrop” n’a pas résolu mon problème. Il a en fait élargi la manière de l’image plus. J’ai donc essayé avec Android: scaleType = “fitXY” et ça a fonctionné très bien.

Ce fonctionne bien selon mon exigence