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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2991110/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:20:01  来源:igfitidea点击:

Android: How to stretch an image to the screen width while maintaining aspect ratio?

javaandroidlayout

提问by fredley

I want to download an image (of unknown size, but which is always roughly square) and display it so that it fills the screen horizontally, and stretches vertically to maintain the aspect ratio of the image, on any screen size. Here is my (non-working) code. It stretches the image horizontally, but not vertically, so it is squashed...

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

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

采纳答案by Bob Lee

I accomplished this with a custom view. Set layout_width="fill_parent" and layout_height="wrap_content", and point it to the appropriate drawable:

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

回答by Cheryl Simon

You are setting the ScaleType to ScaleType.FIT_XY. According to the javadocs, this will stretch the image to fit the whole area, changing the aspect ratio if necessary. That would explain the behavior you are seeing.

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

To get the behavior you want... FIT_CENTER, FIT_START, or FIT_END are close, but if the image is narrower than it is tall, it will not start to fill the width. You could look at how those are implemented though, and you should probably be able to figure out how to adjust it for your purpose.

要获得您想要的行为... FIT_CENTER、FIT_START 或 FIT_END 很接近,但如果图像窄于高,则不会开始填充宽度。你可以看看这些是如何实现的,你应该能够弄清楚如何根据你的目的进行调整。

回答by fredley

In the end, I generated the dimensions manually, which works great:

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

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

回答by P.Melch

ScaleType.CENTER_CROP will do what you want: stretch to full width, and scale the height accordingly. if the scaled height exceeds the screen limits, the image will be cropped.

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

回答by Matt Stoker

Setting adjustViewBounds to true and using a LinearLayout view group worked very well for me. No need to subclass or ask for device metrics:

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

回答by helduel

I did it with these values within a LinearLayout:

我在 LinearLayout 中使用这些值做到了:

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

回答by duhrer

I've been struggling with this problem in one form or another for AGES, thank you, Thank You, THANK YOU.... :)

我一直在以一种或另一种形式为这个问题苦苦挣扎 AGES,谢谢,谢谢,谢谢....:)

I just wanted to point out that you can get a generalizable solution from what Bob Lee's done by just extending View and overriding onMeasure. That way you can use this with any drawable you want, and it won't break if there's no image:

我只是想指出,您可以通过扩展 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);
            }
        }
    }

回答by Timmmm

I just read the source code for ImageViewand it is basically impossible without using the subclassing solutions in this thread. In ImageView.onMeasurewe get to these lines:

我只是阅读了源代码,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);

Where hand ware the dimensions of the image, and p*is the padding.

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

And then:

进而:

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

So if you have a layout_height="wrap_content"it will set widthSize = w + pleft + pright, or in other words, the maximum width is equal to the image width.

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

This means that unless you set an exact size, images are NEVER enlarged. I consider this to be a bug, but good luck getting Google to take notice or fix it. Edit: Eating my own words, I submitted a bug reportand they say it has been fixed in a future release!

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

Another solution

另一种解决方案

Here is another subclassed workaround, but you should(in theory, I haven't really tested it much!) be able to use it anywhere you ImageView. To use it set layout_width="match_parent", and layout_height="wrap_content". It is quite a lot more general than the accepted solution too. E.g. you can do fit-to-height as well as fit-to-width.

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

回答by Warpzit

A very simple solution is to just use the features provided by RelativeLayout.

一个非常简单的解决方案是仅使用RelativeLayout.

Here is the xml that makes it possible with standard Android Views:

这是使标准 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>

The trick is that you set the ImageViewto fill the screen but it has to be above the other layouts. This way you achieve everything you need.

诀窍是您将 设置ImageView为填充屏幕,但它必须高于其他布局。通过这种方式,您可以实现所需的一切。

回答by Ricardo

For me the android:scaleType="centerCrop" did not resolve my problem. It actually expanded the image way more. So I tried with android:scaleType="fitXY" and It worked excellent.

对我来说 android:scaleType="centerCrop" 没有解决我的问题。它实际上更多地扩展了图像。所以我尝试使用 android:scaleType="fitXY" 并且效果很好。