将位图调整大小到固定值,但不改变其长宽比

15
我正在寻找以下问题的解决方案:如何将Bitmap的大小更改为固定大小(例如512x128)。必须保留位图内容的纵横比。
我认为应该是这样的:
  • 创建一个空的512x128位图

  • 将原始位图缩小以适应512x128像素,并保持其宽高比

  • 将缩放后的位图复制到空位图中(居中)

最简单的方法是什么?
所有这些的原因是,当图像的宽高比与其他图像不同时,GridView会使布局混乱。这里是一个截图(除了最后一张图片外,所有图片都具有4:1的宽高比):

screenshot

5个回答

42

试一下,计算比例然后重新缩放。

private Bitmap scaleBitmap(Bitmap bm) {
    int width = bm.getWidth();
    int height = bm.getHeight();

    Log.v("Pictures", "Width and height are " + width + "--" + height);

    if (width > height) {
        // landscape
        float ratio = (float) width / maxWidth;
        width = maxWidth;
        height = (int)(height / ratio);
    } else if (height > width) {
        // portrait
        float ratio = (float) height / maxHeight;
        height = maxHeight;
        width = (int)(width / ratio);
    } else {
        // square
        height = maxHeight;
        width = maxWidth;
    }

    Log.v("Pictures", "after scaling Width and height are " + width + "--" + height);

    bm = Bitmap.createScaledBitmap(bm, width, height, true);
    return bm;
}

2
为了得到一个简单的解决方案,我们可以加上一个加一操作。但是我会将 int ratio 更改为 float/double ratio,因为 int/int 在尝试除以 0 时可能会产生异常。 - just_user
非常棒的简单解决方案!但一定要使用浮点数而不是整数。否则结果大多数情况下可能会出错(例如,比率应该是1.4,但变成了1)。 - xip
@Hikaru 你说得对,我本来想做这件事的,但一直没做到。感谢你的修改和提醒。-) - Coen Damen
1
第三个条件似乎不是纵横比。不要使用 height = maxHeight; 与 width = maxWidth; 删除最后一个条件并将 else if (height > width) { 更改为 else{ - user3307005

22

Coen Damen的回答并不总是遵守最大高度和最大宽度限制。以下是一个能够遵守这些限制的答案:

 private static Bitmap resize(Bitmap image, int maxWidth, int maxHeight) {
    if (maxHeight > 0 && maxWidth > 0) {
        int width = image.getWidth();
        int height = image.getHeight();
        float ratioBitmap = (float) width / (float) height;
        float ratioMax = (float) maxWidth / (float) maxHeight;

        int finalWidth = maxWidth;
        int finalHeight = maxHeight;
        if (ratioMax > 1) {
            finalWidth = (int) ((float)maxHeight * ratioBitmap);
        } else {
            finalHeight = (int) ((float)maxWidth / ratioBitmap);
        }
        image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);
        return image;
    } else {
        return image;
    }
}

6
抄袭我的答案是可以的。当然,maxWidth和maxHeight是类变量而不是方法参数。 - Coen Damen

2

在我的项目中,我多次遇到了同样的问题,由于时间不足(和懒惰),每次都只能满足于不太理想的解决方案。但最近我找到了一些时间来解决这个问题。以下是我的解决方案,希望能帮助到其他人。

Bitmap scaleDownLargeImageWithAspectRatio(Bitmap image)
            {
                int imageVerticalAspectRatio,imageHorizontalAspectRatio;
                float bestFitScalingFactor=0;
                float percesionValue=(float) 0.2;
    
                //getAspect Ratio of Image
                int imageHeight=(int) (Math.ceil((double) image.getHeight()/100)*100);
                int imageWidth=(int) (Math.ceil((double) image.getWidth()/100)*100);
                int GCD=BigInteger.valueOf(imageHeight).gcd(BigInteger.valueOf(imageWidth)).intValue();
                imageVerticalAspectRatio=imageHeight/GCD;
                imageHorizontalAspectRatio=imageWidth/GCD;
                Log.i("scaleDownLargeImageWIthAspectRatio","Image Dimensions(W:H): "+imageWidth+":"+imageHeight);
                Log.i("scaleDownLargeImageWIthAspectRatio","Image AspectRatio(W:H): "+imageHorizontalAspectRatio+":"+imageVerticalAspectRatio);
    
                //getContainer Dimensions
                int displayWidth = getWindowManager().getDefaultDisplay().getWidth();
                int displayHeight = getWindowManager().getDefaultDisplay().getHeight();
               //I wanted to show the image to fit the entire device, as a best case. So my ccontainer dimensions were displayWidth & displayHeight. For your case, you will need to fetch container dimensions at run time or you can pass static values to these two parameters 
    
                int leftMargin = 0;
                int rightMargin = 0;
                int topMargin = 0;
                int bottomMargin = 0;
                int containerWidth = displayWidth - (leftMargin + rightMargin);
                int containerHeight = displayHeight - (topMargin + bottomMargin);
                Log.i("scaleDownLargeImageWIthAspectRatio","Container dimensions(W:H): "+containerWidth+":"+containerHeight);
    
                //iterate to get bestFitScaleFactor per constraints
                while((imageHorizontalAspectRatio*bestFitScalingFactor <= containerWidth) && 
                        (imageVerticalAspectRatio*bestFitScalingFactor<= containerHeight))
                {
                    bestFitScalingFactor+=percesionValue;
                }
    
                //return bestFit bitmap
                int bestFitHeight=(int) (imageVerticalAspectRatio*bestFitScalingFactor);
                int bestFitWidth=(int) (imageHorizontalAspectRatio*bestFitScalingFactor);
                Log.i("scaleDownLargeImageWIthAspectRatio","bestFitScalingFactor: "+bestFitScalingFactor);
                Log.i("scaleDownLargeImageWIthAspectRatio","bestFitOutPutDimesions(W:H): "+bestFitWidth+":"+bestFitHeight);
                image=Bitmap.createScaledBitmap(image, bestFitWidth,bestFitHeight, true);
    
                //Position the bitmap centre of the container
                int leftPadding=(containerWidth-image.getWidth())/2;
                int topPadding=(containerHeight-image.getHeight())/2;
                Bitmap backDrop=Bitmap.createBitmap(containerWidth, containerHeight, Bitmap.Config.RGB_565);
                Canvas can = new Canvas(backDrop);
                can.drawBitmap(image, leftPadding, topPadding, null);
    
                return backDrop;
            }

1

createScaledBitmap 无法解决我的问题。这已经是我使用的方法了。在示例中,最后一张图片就是这样缩放的。 - Dark.Rider
如果您已经有了一个缩小的位图(例如宽度小于512或高度小于128),只需在GridView项的ImageView中设置这些属性:android:layout_width="512dp",android:layout_height="128dp"和android:scaleType="center"或android:scaleType="centerInside"。 - Streets Of Boston
根据我的解释,示例中的最后一张图片已经被缩小了,因此它的高度已经达到了最大128像素。尽管如此,图像视图显示它比其他图像更大。此外,我不想要一个512x128dp网格的图像视图。 - Dark.Rider
在您的截图中,针对最后/底部的3幅图像:最后一幅图像更大(更高)。您想要使前两幅图像变高(以匹配最后一幅)还是想要使最后一幅变低(以匹配前两幅)? - Streets Of Boston
如果您希望GridView中的所有项具有相同的高度,请将ImageView的layout_height设置为128dp(或任何其他固定值),并将layout_width设置为wrap_content。将scaleType设置为fitEnd、fitStart、fitXY或fitCenter。 - Streets Of Boston

1
我认为@Coen的答案并不是这个问题的正确解决方案。我也需要这样的方法,但我想要正方形图片。
这是我关于正方形图片的解决方案;
public static Bitmap resizeBitmapImageForFitSquare(Bitmap image, int maxResolution) {

    if (maxResolution <= 0)
        return image;

    int width = image.getWidth();
    int height = image.getHeight();
    float ratio = (width >= height) ? (float)maxResolution/width :(float)maxResolution/height;

    int finalWidth = (int) ((float)width * ratio);
    int finalHeight = (int) ((float)height * ratio);

    image = Bitmap.createScaledBitmap(image, finalWidth, finalHeight, true);

    if (image.getWidth() == image.getHeight())
        return image;
    else {
        //fit height and width
        int left = 0;
        int top = 0;

        if(image.getWidth() != maxResolution)
            left = (maxResolution - image.getWidth()) / 2;

        if(image.getHeight() != maxResolution)
            top = (maxResolution - image.getHeight()) / 2;

        Bitmap bitmap = Bitmap.createBitmap(maxResolution, maxResolution, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(image, left, top, null);
        canvas.save();
        canvas.restore();

        return  bitmap;
    }
}

最大分辨率是什么? - ceng

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接