Android:如何在保持宽高比的同时将图像拉伸到屏幕宽度?

我想下载一张图片(尺寸未知,但总是大致为正方形),并将其显示为水平填充屏幕,并在任何屏幕尺寸下垂直延伸以保持图像的高宽比。 这是我的(非工作)代码。 它横向延伸图像,但不是垂直,所以它被压扁…

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

我用自定义的视图完成了这个。 设置layout_width =“fill_parent”和layout_height =“wrap_content”,并将其指向适当的drawable:

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

最后,我手动生成了尺寸,这很好用:

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

我刚刚阅读了ImageView的源代码,如果没有在这个线程中使用子类化解决scheme,基本上是不可能的。 在ImageView.onMeasure我们得到以下ImageView.onMeasure行:

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

其中hw是图像的尺寸, p*是填充。

接着:

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

所以如果你有一个layout_height="wrap_content" ,它将设置widthSize = w + pleft + pright ,换句话说,最大宽度等于图像宽度。

这意味着, 除非您设置了确切的尺寸,否则图像从未被放大 。 我认为这是一个错误,但让Google注意或修复它是一件好事。 编辑:吃我自己的话,我提交了一个错误报告 ,他们说它已经在未来的版本中修复!

解决scheme

这里是另一个子types的解决方法,但是您应该 (理论上,我没有真正testing过它!)能够在ImageView任何位置使用它。 要使用它,请设置layout_width="match_parent"layout_height="wrap_content" 。 它比公认的解决scheme还要普遍得多。 例如,你可以做适合身高和适合身高。

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

将adjustViewBounds设置为true并使用LinearLayout视图组对我来说效果很好。 无需子类或要求设备度量标准:

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

对于AGES,我一直在用这种或那种forms来解决这个问题,谢谢,谢谢,谢谢…. 🙂

我只想指出,你可以从Bob Lee所做的扩展View和覆盖onMeasure的方法中得到一个可以概括的解决scheme。 这样,你可以用任何你想要的drawable来使用它,如果没有图像,它不会被破坏:

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

在某些情况下,这个神奇的配方精美地解决了这个问题。

对于从另一个平台上挣扎的人来说, “适合尺寸和形状”选项在Android中处理得非常好,但是很难find。

你通常想要这个组合:

  • 宽度匹配父,
  • 身高包裹内容,
  • adjustViewBounds打开(原文如此)
  • 规模合适的中心
  • cropToPadding OFF(原文如此)

然后它是自动和惊人的。

如果你是一个iOS开发者,那么在表格视图中简单地完成“完全dynamic的单元格高度”是非常了不起的。err,我的意思是ListView。 请享用。

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

在这里输入图像描述

我在LinearLayout中使用了这些值:

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

我已经成功地使用这个XML代码来实现这一点。 可能是这样的情况,日食没有渲染高度来显示它扩大到适合; 然而,当你真的在设备上运行它时,它会正确渲染并提供所需的结果。 (至less对我来说)

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

大家都这样编程,所以我觉得这个答案在这里完全适合。 这段代码适用于我的XML。 我还没有考虑比例,但如果它能帮助任何人,还是要把这个答案。

 android:adjustViewBounds="true" 

干杯..

一个非常简单的解决scheme就是使用RelativeLayout提供的function。

以下是使用标准Android Views实现的xml:

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

诀窍是你设置ImageView来填充屏幕,但它必须高于其他布局。 这样你就可以实现你所需要的一切。

您将ScaleType设置为ScaleType.FIT_XY。 根据javadocs ,这将拉伸图像,以适应整个地区,如有必要改变纵横比。 这将解释你所看到的行为。

为了得到你想要的行为… FIT_CENTER,FIT_START或FIT_END是接近的,但是如果图像比它高,它将不会开始填充宽度。 你可以看看这些是如何实现的,你应该能够弄清楚如何调整它以达到你的目的。

ScaleType.CENTER_CROP将做你想要的:拉伸到全宽,并相应地缩放高度。 如果缩放的高度超出屏幕限制,则图像将被裁剪。

看看你的问题有一个更容易的解决scheme:

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

您可以使用我的StretchableImageView根据可绘制的宽度和高度来保留长宽比(按宽度或高度):

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

在ImageView的XML文件中设置adjustViewBounds="true"scaleType="fitCenter"这个简单的事情!

 <ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:src="@drawable/image" android:adjustViewBounds="true" android:scaleType="fitCenter" /> 

注意: layout_width被设置为match_parent

对于我的android:scaleType =“centerCrop”没有解决我的问题。 它实际上扩大了形象的方式。 所以我尝试使用android:scaleType =“fitXY”,它工作得很好。

这工作正常根据我的要求

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