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


118

我想下载一个图像(大小不明,但始终大致为正方形)并显示它,以便它在任何屏幕尺寸上都水平填充屏幕,并垂直拉伸以保持图像的纵横比。这是我的代码(无效)。它水平拉伸图像,但不垂直拉伸图像,因此被压缩...

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

所有Android设备的宽度/高度比是否完全相同?如果不是这样,就不可能在保持原始比例的情况下缩放图像以适合整个宽度/高度...
NoozNooz42 2010年

4
不,我不希望图像填满整个屏幕,仅是按比例缩放到屏幕宽度,只要图像以正确的比例,我不在乎图像在垂直方向上占据了多少屏幕。
fredley 2010年

1
类似的问题,很好的答案:stackoverflow.com/questions/4677269/...
PēterisCaune

1
'adjustViewBounds'不起作用吗?
达尔潘

Answers:


132

我使用自定义视图完成了此操作。设置layout_width =“ fill_parent”和layout_height =“ wrap_content”,并将其指向适当的可绘制对象:

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

12
谢谢!!这应该是正确的答案!:)我在这篇文章中对此进行了调整,以提供更多的灵活性:stackoverflow.com/questions/4677269/…我在这里使用了ImageView,因此可以在XML中设置可绘制对象或像在代码中使用普通ImageView一样使用它来设置图像。很棒!
Patrick Boos

1
怪胎天才!就像冠军一样!谢谢,它解决了我的问题,并非在所有屏幕尺寸上都显示了顶部的图像横幅!非常感谢!
JPM

1
注意徽标是否为空(如果要从Internet下载内容)。否则,这很有用,请按照Patrick的XML代码段文章。
Artem Russakovskii

44
我无法相信在Android上做在iOS和Windows Phone上轻而易举的事情有多么困难。
mxcl 2012年

1
我进行了一些更改,但是可以将答案转换为社区Wiki,而不是将其发布到其他位置?我所做的更改是处理这里提到的特殊情况的所有内容,以及通过布局定义选择调整哪一侧尺寸的能力。
史蒂夫·波默罗伊

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

我一直在寻找解决此问题的解决方案,借助此计算,我成功地在不降低宽高比的情况下加载了图像。
史蒂夫

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

这意味着除非您设置确切的尺寸,否则图像永远不会放大。我认为这是一个错误,但是祝您好运,让Google注意到或修复它。编辑:用我自己的话说,我提交了一个错误报告,他们说它在将来的版本中已得到修复!

另一种解决方案

这是另一个子类化的解决方法,但是您应该(理论上,我还没有真正测试过!)可以在您的任何地方使用它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);
        }
    }
}

我提交了一个错误报告。注意,因为它被忽略了!
Timmmm 2012年

3
显然我必须听我的话-他们说它在将来的版本中已得到修复!
Timmmm

蒂姆,非常感谢。这应该是最终的正确答案。我已经搜索了很多时间,没有什么比您的解决方案更好的了!
tainy

如果使用上述StretchyImageView时需要设置maxHeight怎么办?我没有找到任何解决方案。
tainy

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

1
...或使用ImageView XML属性-android:adjustViewBounds =“ true”
Anatoliy Shuba

完善!这非常容易,并且可以完美地拉伸图像。这种答案不是正确的答案吗?CustomView的答案对我不起作用(一些奇怪的xml错误)
user3800924,2016年

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

2
在解决这个问题的众多解决方案中,这是第一个只是一个嵌入式解决方案,其他解决方案总是有缺点(例如,如果不重写代码就无法在运行时更改映像)。谢谢!
MacD 2013年

这真是太棒了-现在已经为此苦苦挣扎了一段时间,使用xml属性的所有显而易见的解决方案都行不通。这样,我可以将我的ImageView替换为扩展视图,一切都按预期工作!
老虎机

这绝对是解决此问题的最佳方法。
erlando 2013年

那是一个很好的答案。您可以在xml文件中使用此类,而无需担心,例如:<com.yourpackagename.CardImageView android:layout_width =“ fill_parent” android:layout_height =“ wrap_content” android:background =“ @ drawable / your_photo” />。大!
arniotaki

11

在某些情况下,该魔术公式可以很好地解决该问题。

对于在另一个平台上苦苦挣扎的人来说,“大小和形状适合”在Android中可以很好地处理,但是很难找到。

您通常需要这种组合:

  • 宽度匹配父项,
  • 高度包装内容,
  • AdjustViewBounds已打开(原文如此)
  • 标度fitCenter
  • cropToPadding OFF(原文如此)

然后,它是自动的并且令人惊奇。

如果您是iOS开发人员,那么简直令人惊讶,在Android中,您可以在表格视图中进行“完全动态的单元格高度” .. 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"/>

在此处输入图片说明


6

我设法仅使用此XML代码实现了这一点。可能是因为eclipse无法呈现高度以显示其扩展到适合;但是,当您实际在设备上运行它时,它会正确渲染并提供所需的结果。(至少对我来说)

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

抱歉,这不起作用。它缩放宽度,但是centerCrop不会保持纵横比。
ricosrealm 2014年

1
经过数小时的努力,自定义类扩展了ImageView(如许多答案和文章中所写),并遇到了类似“找不到以下类”的异常,我删除了该代码。突然我发现ImageView中的一个问题是背景属性!将其更改src为ImageView成比例。
CoolMind

5

我在LinearLayout中使用这些值来做到这一点:

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

你能举一个真实的例子吗?我应该将这些值放在xml文件中的什么位置?
约翰·史密斯可选

5

每个人都在编程,所以我认为这个答案非常适合。这段代码在xml中对我有用。我还没有考虑比率,但是仍然想提出这个答案,如果它可以帮助任何人。

android:adjustViewBounds="true"

干杯..


3

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

这是使标准Android成为可能的xml 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为填满屏幕,但它必须位于其他布局上方。这样您就可以实现所需的一切。


2

设置adjustViewBounds="true"scaleType="fitCenter"在ImageView的XML文件中都非常简单!

<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


1

您正在将ScaleType设置为ScaleType.FIT_XY。根据javadocs,这将拉伸图像以适合整个区域,并在必要时更改宽高比。那可以解释您所看到的行为。

要获得所需的行为,... FIT_CENTER,FIT_START或FIT_END接近,但是如果图像比高窄,则不会开始填充宽度。不过,您可以看看这些是如何实现的,并且应该可以弄清楚如何针对您的目的进行调整。


2
我尝试过FIT_CENTER,但其他人则没有。不幸的是,它们都不起作用,都将setAdjustViewBounds设置为true和false。无论如何,是否可以获得设备屏幕的像素宽度以及下载图像的宽度/高度并手动设置尺寸?
弗雷德里(Fredley)2010年

1

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


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

1

您可以使用我的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);
            }
        }
    }
}

0

对我来说,android:scaleType =“ centerCrop”无法解决我的问题。实际上,它进一步扩展了图像方式。所以我尝试了android:scaleType =“ fitXY”,效果很好。


0

按我的要求,这个工作正常

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.