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));
132445 次浏览

您正在将 ScaleType 设置为 ScaleType.FIT _ XY。根据 Javadocs,这将拉伸图像,以适应整个区域,改变纵横比,如果必要的。这就能解释你现在的行为了。

要获得所需的行为... ... FIT _ CENTER、 FIT _ START 或 FIT _ END 都很接近,但是如果图像比它的高度窄,它就不会开始填充宽度。但是,您可以查看这些是如何实现的,并且您可能应该能够找出如何根据您的目的调整它。

I accomplished this with a custom view. Set layout_width="fill_parent" and layout_height="wrap_content", and point it to the appropriate 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));

CENTER _ CROP 将执行您想要的操作: 拉伸到全宽,并相应地缩放高度。如果缩放高度超过屏幕限制,图像将被裁剪。

将调整 ViewBound 设置为 true 并使用 LinearLayout 视图组对我来说非常有效。不需要子类化或者询问设备指标:

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

我在 LinearLayout 中使用了这些值:

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

多年来,我一直在以这样或那样的形式与这个问题作斗争,谢谢,谢谢,谢谢... ...)

我只是想指出,您可以通过扩展 View 和覆盖 onScale 从 Bob Lee 所做的工作中获得一个通用的解决方案。这样你就可以把它用在任何你想画的地方,而且如果没有图像的话它也不会坏:

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

我刚刚读了 ImageView的源代码,如果不使用这个线程中的子类化解决方案,基本上是不可能的。在 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,或者换句话说,最大宽度等于图像宽度。

这意味着 除非你设置了精确的尺寸,否则图像永远不会放大。我认为这是一个 bug,但是希望谷歌能够注意到或者修复它。编辑: 我自己说的,我提交了一个 bug 报告,他们说在未来的版本中已经修复了!

另一个解决办法

这里是另一个子类化的解决方案,但是您可以使用 应该(理论上,我还没有真正测试过它!)能够使用它在任何地方你 ImageView。使用它设置 layout_width="match_parent"layout_height="wrap_content"。它也比公认的解决方案更为普遍。例如,你可以做适合的高度以及适合的宽度。

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

一个非常简单的解决方案是只使用 RelativeLayout提供的特性。

Here is the xml that makes it possible with 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>

诀窍是,你设置的 ImageView填补屏幕,但它必须高于其他布局。这样你就能得到你想要的一切。

对我来说,android: scaleType = “ centerCrop”并没有解决我的问题。它实际上扩展了图像。所以我试用了 android: scaleType = “ fitXY”,它工作得很好。

I have managed to achieve this using this XML code only. It might be the case that eclipse does not render the height to show it expanding to fit; however, when you actually run this on a device, it properly renders and provides the desired result. (well at least for me)

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

在某些情况下,这个神奇的公式完美地解决了问题。

对于那些来自其他平台的人来说,“尺寸和形状适合”选项在 Android 中处理得很漂亮,但是很难找到。

你通常需要这样的组合:

  • 宽度匹配父,
  • 高度包裹内容,
  • 调整视图界限打开(原文如此)
  • scale fitCenter
  • (原文如此)

然后就自然而然,令人惊叹。

如果你是一个 iOS 开发人员,你会惊奇地发现,在 Android 系统中,你可以在表格视图中完成“完全动态的单元格高度”。.呃,我是说 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"/>

enter image description here

看,有一个更简单的方法来解决你的问题:

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

这个工作按我的要求很好

< ImageView Android: id = “@+ id/img宫” Android: layout _ width = “ fill _ parent” Android: layout _ height = “ wrap _ content” 调整 ViewBounds = “ true” Android: scaleType = “ fitXY”/>

每个人都在做这个程序,所以我认为这个答案非常适合这里。这段代码在 xml 中对我有效。我还没有考虑比率,但仍然想把这个答案,如果它会帮助任何人。

android:adjustViewBounds="true"

干杯。

You can use my StretchableImageView preserving the aspect ratio (by width or by height) depending on width and height of drawable:

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