Android: comment étirer une image sur la largeur de l'écran tout en maintenant le format d'image?
je veux télécharger une image (de taille inconnue, mais qui est toujours à peu près carrée) et l'afficher de sorte qu'elle remplisse l'écran horizontalement, et s'étire verticalement pour maintenir le rapport d'aspect de l'image, sur n'importe quelle taille d'écran. Voici ma (non-travail) du code. Il étire l'image horizontalement, mais pas verticalement, de sorte qu'elle est é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));
17 réponses
j'ai accompli ceci avec une vue personnalisée. Définissez layout_width= "fill_parent" et layout_height=" wrap_content", et pointez-le vers le drawable 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);
}
}
à la fin, 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 pour ImageView
et il est fondamentalement impossible sans utiliser les solutions de sous-classement dans 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.
et ensuite:
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 sera placé 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 que C'est un bug, mais bonne chance pour que Google s'en aperçoive ou le Répare. Edit: mangeant mes propres mots, j'ai soumis un rapport de bogue et ils disent qu'il a été corrigé dans une future version!
une autre solution
Voici une autre solution subclassée, mais vous devrait (en théorie, Je ne l'ai pas vraiment testé beaucoup!) être en mesure de l'utiliser partout où vous ImageView
. Pour l'utiliser, Mettez layout_width="match_parent"
, et layout_height="wrap_content"
. Il est beaucoup plus générale que la solution retenue. E. g. vous pouvez le faire adapter à la hauteur ainsi que 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);
}
}
}
mettre adjustViewBounds à true et en utilisant un groupe de vue linéaire a très bien fonctionné pour moi. Pas besoin de sous-classe ou de demander des paramètres 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 été aux prises avec ce problème sous une forme ou une autre pendant des siècles, 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 la vue et en dépassant onMeasure. De cette façon, vous pouvez l'utiliser avec n'importe quel dessin que vous voulez, et il ne se brisera 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 toute personne aux prises avec ce qui vient d'une autre plate-forme, l'option " taille et la forme pour s'adapter " est géré magnifiquement dans Android, mais il est difficile de trouver.
vous voulez typiquement cette combinaison:
- largeur match parent,
- hauteur du contenu de l'enveloppe,
- adjustViewBounds activée (sic)
- scale fitCenter
- cropToPadding OFF (sic)
Alors c'est automatique et incroyable.
si vous êtes un dev iOS, il est tout simplement incroyable comment vous pouvez faire "hauteurs de cellules totalement dynamique" dans une vue de table .. euh, je veux dire ListView. Profiter.
<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"/>
Je l'ai fait avec ces valeurs dans une distribution linéaire:
Scale type: fitStart
Layout gravity: fill_horizontal
Layout height: wrap_content
Layout weight: 1
Layout width: fill_parent
j'ai réussi à réaliser ceci en utilisant ce code XML seulement. Il se peut que eclipse ne rende pas la hauteur pour montrer qu'elle s'étend pour s'adapter; cependant, quand vous exécutez réellement ceci sur un périphérique, Il rend correctement et fournit le résultat désiré. (du 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 fait ce programme donc j'ai pensé que cette réponse conviendrait parfaitement ici. Ce code a fonctionné pour moi dans le xml. Je ne pense pas encore à ratio, mais toujours voulu placer cette réponse si elle aiderait quelqu'un.
android:adjustViewBounds="true"
santé..
une solution très simple est d'utiliser simplement les caractéristiques fournies par RelativeLayout
.
voici le xml qui le rend possible avec Android standard 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>
le truc est que vous mettez le ImageView
pour remplir l'écran mais il doit être au-dessus des autres layouts. De cette façon, vous réalisez tout ce dont vous avez besoin.
sa simple question de définir 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"
/>
Note: layout_width
est réglé sur match_parent
vous définissez ScaleType à ScaleType.FIT_XY. Selon le javadocs , cela permettra d'étirer l'image pour s'adapter à toute la zone, en changeant le rapport d'aspect si nécessaire. Cela expliquerait le comportement que vous voyez.
Pour obtenir le comportement que vous voulez... 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 regarder comment ils sont mis en œuvre cependant, et vous devriez probablement être en mesure de comprendre comment l'ajuster en fonction de votre but.
ScaleType.CENTER_CROP fera ce que vous voulez: étirez jusqu'à la pleine largeur, et dimensionnez la hauteur en conséquence. si à l'échelle de la hauteur dépasse les limites de l'écran, l'image sera rogné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 préserver le rapport d'aspect (par la largeur ou par la hauteur) en fonction de la largeur et la hauteur de tirable:
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);
}
}
}
}
pour moi l'android:scaleType="centerCrop" n'a pas résolu mon problème. Il a en fait élargi l'image beaucoup plus. J'ai donc essayé avec android: scaleType= "fitXY" et il a fonctionné excellent.
Ce travail bien que par mon exigence
<ImageView
android:id="@+id/imgIssue"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY"/>