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

Java Android:如何在保持纵横比的同时将图像拉伸到屏幕宽度?,java,android,layout,Java,Android,Layout,我想下载一个图像(大小未知,但总是大致呈正方形),并将其显示为水平填充屏幕,垂直拉伸以在任何屏幕大小上保持图像的纵横比。这是我的(非工作)代码。它会水平拉伸图像,但不会垂直拉伸,因此会挤压图像 ImageView mainImageView = new ImageView(context); mainImageView.setImageBitmap(mainImage); //downloaded from server mainImageView.setScaleType(Sc

我想下载一个图像(大小未知,但总是大致呈正方形),并将其显示为水平填充屏幕,垂直拉伸以在任何屏幕大小上保持图像的纵横比。这是我的(非工作)代码。它会水平拉伸图像,但不会垂直拉伸,因此会挤压图像

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

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


为了得到你想要的行为。。。FIT_CENTER、FIT_START或FIT_END接近,但如果图像的宽度小于其高度,则不会开始填充宽度。不过,您可以看看这些是如何实现的,并且您应该能够找到如何根据自己的目的对其进行调整。

我通过自定义视图实现了这一点。设置layout\u width=“fill\u parent”和layout\u height=“wrap\u 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);
  }
}
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);
            }
        }
    }
}

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

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

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

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

我在线性布局中使用了这些值:

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

多年来,我一直在以这样或那样的方式努力解决这个问题,谢谢,谢谢,谢谢……)

我只是想指出,通过扩展视图和覆盖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);
            }
        }
    }

我刚刚阅读了
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);
其中
h
w
是图像的尺寸,
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\u height=“wrap\u content”
它将设置
widthSize=w+pleft+pright
,或者换句话说,最大宽度等于图像宽度

这意味着,除非您设置精确的大小,否则图像永远不会放大。我认为这是一个bug,但祝你好运,让谷歌注意或修复它。strong>编辑:吃我自己的话,我提交了,他们说它已经在未来的版本中修复

另一个解决方案 这里是另一个子类解决方法,但您应该(理论上,我没有对它进行过太多测试!)能够在
ImageView
的任何地方使用它。要使用它,请设置
layout\u width=“match\u parent”
,以及
layout\u height=“wrap\u 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);
        }
    }
}
导入android.content.Context;
导入android.util.AttributeSet;
导入android.widget.ImageView;
//这可以解决此处描述的问题:http://stackoverflow.com/a/12675430/265521
公共类StretchImageView扩展了ImageView
{
公共StretchyImageView(上下文)
{
超级(上下文);
}
公共StretchImageView(上下文、属性集属性)
{
超级(上下文,attrs);
}
公共StretchyImageView(上下文上下文、属性集属性、int-defStyle)
{
超级(上下文、属性、定义样式);
}
@凌驾
测量时的保护空隙(内部宽度测量等级、内部高度测量等级)
{
//调用super()以便调用resolveUri()。
超级测量(宽度测量、高度测量);
//如果没有绘图功能,我们可以使用super的结果。
if(getDrawable()==null)
返回;
最终int-widthSpecMode=MeasureSpec.getMode(widthsmeasurespec);
最终int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
int w=getDrawable().getIntrinsicWidth();
int h=getDrawable().getIntrinsicHeight();

如果(w一个非常简单的解决方案是只使用
RelativeLayout
提供的功能

以下是使标准Android
视图成为可能的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>


诀窍在于,你可以设置
图像视图来填充屏幕,但它必须位于其他布局之上。这样你就可以实现你所需要的一切。

对我来说,android:scaleType=“centerCrop”并没有解决我的问题。它实际上扩展了更多的图像。因此我尝试使用android:scaleType=“fitXY”它工作得非常出色。

我仅使用此XML代码就成功地实现了这一点。可能是eclipse没有呈现高度以显示它扩展到合适的位置;但是,当您在设备上实际运行此功能时,它会正确地呈现并提供所需的结果。(至少对我来说是这样)


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

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

您通常需要以下组合:

  • 宽度匹配父级
  • 高度包裹内容
  • 调整视图边界已打开(原文如此)
  • 比例fitCenter
  • 克罗普托夫(sic)
然后,它是自动和惊人的

如果你是一个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"/>


看,有一个更简单的解决方案:

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);
}
android:adjustViewBounds="true"
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
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/image"

        android:adjustViewBounds="true"
        android:scaleType="fitCenter"
        />