首页 文章

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

17 回答

  • 1

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

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

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

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

    这意味着除非您设置精确的尺寸,否则图像永远不会放大 . 我认为这是一个错误,但祝谷歌注意或修复它 . Edit: Eating my own words, I submitted a bug report and they say it has been fixed in a future release!

    另一个解决方案

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

    将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

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

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

    对于那些在另一个平台上苦苦挣扎的人来说, "size and shape to fit" 选项在Android中得到了很好的处理,但很难找到 .

    您通常需要这种组合:

    • 宽度匹配父级,

    • 高度换行内容,

    • adjustViewBounds开启(原文如此)

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

  • 21

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

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

    我已经设法使用此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>
    
  • 24

    每个人都在做这个程序,所以我认为这个答案在这里完全适合 . 这段代码适用于我的xml . 我还没考虑比率,但是如果能帮助任何人,仍然想要这个答案 .

    android:adjustViewBounds="true"
    

    干杯..

  • 0

    一个非常简单的解决方案是使用 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 来填充屏幕,但它必须高于其他布局 . 这样您就可以实现所需的一切 .

  • 1

    在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

  • 0

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

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

  • 17

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

  • 2

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

    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

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

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

  • 3

    这可以按照我的要求正常工作

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

相关问题