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));
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);
}
}
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));
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);
Où 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 !
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);
}
}
}
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);
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);
}
}
}
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:
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"/>
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>
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é..
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
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.
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
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.
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);
}
}
}
}
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.
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);
}
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"/>
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.