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

时间:2010-06-07 16:05:53

标签: java android layout

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

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

16 个答案:

答案 0 :(得分:132)

我用自定义视图完成了这个。设置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);
  }
}

答案 1 :(得分:24)

最后,我手动生成尺寸,效果很好:

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

答案 2 :(得分:21)

我刚刚阅读了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,换句话说,最大宽度等于图像宽度。

这意味着除非您设置精确的尺寸,否则图像永远不会放大。我认为这是一个错误,但祝谷歌注意或修复它。 编辑:吃我自己的话,我提交了a bug report,他们说它已在未来版本中修复过了!

另一种解决方案

这是另一个子类化解决方法,但是应该(理论上,我还没有真正测试过它!)能够在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);
        }
    }
}

答案 3 :(得分:17)

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

答案 4 :(得分:13)

我一直在以这种或那种形式为AGES努力解决这个问题,谢谢你,谢谢你,谢谢.... :)

我只是想指出,你可以通过扩展View和覆盖onMeasure从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);
            }
        }
    }

答案 5 :(得分:10)

在某些情况下,这个神奇的公式可以很好地解决问题。

对于那些在另一个平台上苦苦挣扎的人来说,“尺寸和形状适合”选项在Android中处理得很漂亮,但很难找到。

您通常需要这种组合:

  • 宽度匹配父,
  • 高度换行内容,
  • adjustViewBounds开启(原文如此)
  • 比例 fitCenter
  • cropToPadding OFF(原文如此)

然后它是自动而惊人的。

如果你是一个iOS开发者,那么在桌面视图中你可以做到“完全动态的单元格高度”真是太神奇了。错误,我的意思是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

答案 6 :(得分:5)

我在LinearLayout中使用这些值完成了它:

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

答案 7 :(得分:5)

我设法仅使用此XML代码实现此目的。可能是这样的情况,日食不会使高度显示它扩展到适合;但是,当您在设备上实际运行它时,它会正确呈现并提供所需的结果。 (至少对我而言)

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

答案 8 :(得分:3)

一个非常简单的解决方案是使用RelativeLayout提供的功能。

以下是使用标准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来填充屏幕,但它必须高于其他布局。这样您就可以实现所需的一切。

答案 9 :(得分:2)

在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

答案 10 :(得分:1)

您正在将ScaleType设置为ScaleType.FIT_XY。根据{{​​3}},这将拉伸图像以适应整个区域,如有必要,可以更改宽高比。这可以解释你所看到的行为。

要获得所需的行为... FIT_CENTER,FIT_START或FIT_END接近,但如果图像比它高,则不会开始填充宽度。你可以看看它们是如何实现的,你应该能够找出如何根据你的目的进行调整。

答案 11 :(得分:1)

ScaleType.CENTER_CROP将执行您想要的操作:拉伸到最大宽度,并相应地缩放高度。如果缩放高度超出屏幕限制,图像将被裁剪。

答案 12 :(得分:1)

看看你的问题有一个更简单的解决方案:

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

答案 13 :(得分:1)

您可以使用我的 StretchableImageView 保留宽高比(按宽度或高度),具体取决于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);
            }
        }
    }
}

答案 14 :(得分:0)

对我来说android:scaleType =“centerCrop”没有解决我的问题。它实际上扩展了图像的方式。所以我尝试使用android:scaleType =“fitXY”,它的效果非常好。

答案 15 :(得分:0)

根据我的要求,此工作正常

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