Android如何创建运行时缩略图


67

我有一个大尺寸的图像。在运行时,我想从存储中读取图像并对其进行缩放,以减少其重量和尺寸,并且可以将其用作缩略图。当用户单击缩略图时,我要显示完整尺寸的图像。

Answers:


49

我的解决方案

byte[] imageData = null;

        try     
        {

            final int THUMBNAIL_SIZE = 64;

            FileInputStream fis = new FileInputStream(fileName);
            Bitmap imageBitmap = BitmapFactory.decodeStream(fis);

            imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false);

            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
            imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            imageData = baos.toByteArray();

        }
        catch(Exception ex) {

        }

1
我的文件很大,因此我必须在该BitmapFactory.decodeStream(fis);步骤中使用子采样。有关子采样的更多详细信息,请参阅文档
Diederik 2013年

7
ThumbnailUils好像下面的答案一样使用Android的类。
afollestad 2014年

2
@afollestad不是,这种方法是正确的。仅当您100%确定要使用小文件时,才使用ThumbnailUtils。
GuillermoMP 2015年

该解决方案存在一个BIG错误:imageBitmap = Bitmap.createScaledBitmap(imageBitmap,...); 您将丢失第一个imageBitmap而无需回收!迟早它会导致OutOfMemoryError ...
Massimo 2016年

1
尽管如此,该解决方案仍将整个位图加载到内存中,然后对其进行缩放。
Madeyedexter,2013年

132

尝试这个

Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(imagePath), THUMBSIZE, THUMBSIZE);

该实用程序可从API_LEVEl 8获得。[源]


4
使用此代码,您实际上是在内存中加载大位图的副本,因此这不是管理大图像的好方法。
GuillermoMP

9
正确的方法是解码文件的降采样版本。其他答案提供了这种方法。官方文档也很好地解释了这一过程:developer.android.com/intl/es/training/displaying-bitmaps/…。由于这是最投票的答案(由于其简单性),我只想警告所有人,这不是最好的方法。
GuillermoMP 2015年

如何将其应用于可绘制资源?
JCarlosR '16

2
此解决方案是否保持长宽比?
Matan Tubul

14

我发现的最佳解决方案如下。与其他解决方案相比,此解决方案无需加载完整图像即可创建缩略图,因此效率更高! 它的局限性是您不能拥有宽度和高度精确的缩略图,而解决方案则应尽可能接近。

File file = ...; // the image file

Options bitmapOptions = new Options();
bitmapOptions.inJustDecodeBounds = true; // obtain the size of the image, without loading it in memory
BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);

// find the best scaling factor for the desired dimensions
int desiredWidth = 400;
int desiredHeight = 300;
float widthScale = (float)bitmapOptions.outWidth/desiredWidth;
float heightScale = (float)bitmapOptions.outHeight/desiredHeight;
float scale = Math.min(widthScale, heightScale);

int sampleSize = 1;
while (sampleSize < scale) {
    sampleSize *= 2;
}
bitmapOptions.inSampleSize = sampleSize; // this value must be a power of 2,
                                         // this is why you can not have an image scaled as you would like
bitmapOptions.inJustDecodeBounds = false; // now we want to load the image

// Let's load just the part of the image necessary for creating the thumbnail, not the whole image
Bitmap thumbnail = BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);

// Save the thumbnail
File thumbnailFile = ...;
FileOutputStream fos = new FileOutputStream(thumbnailFile);
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
fos.close();

// Use the thumbail on an ImageView or recycle it!
thumbnail.recycle();

2
这最适合低内存设备
Duna

谢谢。它解决了内存不足异常的问题。
Yury Finchenko '17

9

这是将位图缩小到缩略图大小的更完整的解决方案。它通过保持图像的纵横比并将其填充到相同的宽度,从而使它们在ListView中看起来不错,从而扩展了Bitmap.createScaledBitmap解决方案。

另外,最好一次缩放一次并将结果位图存储为Sqlite数据库中的Blob。我已经包含了有关如何为此目的将位图转换为字节数组的代码段。

public static final int THUMBNAIL_HEIGHT = 48;
public static final int THUMBNAIL_WIDTH = 66;

imageBitmap = BitmapFactory.decodeByteArray(mImageData, 0, mImageData.length);
Float width  = new Float(imageBitmap.getWidth());
Float height = new Float(imageBitmap.getHeight());
Float ratio = width/height;
imageBitmap = Bitmap.createScaledBitmap(imageBitmap, (int)(THUMBNAIL_HEIGHT*ratio), THUMBNAIL_HEIGHT, false);

int padding = (THUMBNAIL_WIDTH - imageBitmap.getWidth())/2;
imageView.setPadding(padding, 0, padding, 0);
imageView.setImageBitmap(imageBitmap);



ByteArrayOutputStream baos = new ByteArrayOutputStream();  
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] byteArray = baos.toByteArray();

你有什么切换参数顺序是有原因的THUMBNAIL_HEIGHT,并THUMBNAIL_HEIGHTcreateScaledBitmap
jayeffkay

6

使用BitmapFactory.decodeFile(...)让您的Bitmap对象,并将其设置为ImageViewImageView.setImageBitmap()

ImageView设置上,将布局尺寸设置为较小,例如:

android:layout_width="66dip" android:layout_height="48dip"

一个添加onClickListenerImageView并推出新的活动,在那里你在全尺寸显示图像

android:layout_width="wrap_content" android:layout_height="wrap_content"

或指定更大的尺寸。


11
当有多个图像时,您应该考虑事先将其缩小到拇指大小。否则可能会降低移动设备时的性能。
莫里兹

5
确实,要做到这一点,您可以使用Bitmap.createScaledBitmap(originalBitmap,newWidth,newHeight,false);。
吉姆·布莱克勒

1
dons下面的方法还可以减轻图像的重量?Bitmap.createScaledBitmap(originalBitmap,newWidth,newHeight,false)
d-man 2010年

3
/**
 * Creates a centered bitmap of the desired size.
 *
 * @param source original bitmap source
 * @param width targeted width
 * @param height targeted height
 * @param options options used during thumbnail extraction
 */
public static Bitmap extractThumbnail(
        Bitmap source, int width, int height, int options) {
    if (source == null) {
        return null;
    }

    float scale;
    if (source.getWidth() < source.getHeight()) {
        scale = width / (float) source.getWidth();
    } else {
        scale = height / (float) source.getHeight();
    }
    Matrix matrix = new Matrix();
    matrix.setScale(scale, scale);
    Bitmap thumbnail = transform(matrix, source, width, height,
            OPTIONS_SCALE_UP | options);
    return thumbnail;
}

变换在哪里(矩阵,源,宽度,高度,OPTIONS_SCALE_UP |选项)方法
Andy


2

我找到了一种简单的方法

Bitmap thumbnail = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(mPath),200,200)

句法

Bitmap thumbnail = ThumbnailUtils.extractThumbnail(Bitmap source,int width,int height)

要么

使用毕加索依赖

编译'com.squareup.picasso:picasso:2.5.2'

Picasso.with(context)
    .load("file:///android_asset/DvpvklR.png")
    .resize(50, 50)
    .into(imageView2);

参考毕加索


0

如果要获得高质量的结果,请使用[RapidDecoder] [1]库。它很简单,如下所示:

import rapid.decoder.BitmapDecoder;
...
Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image)
                             .scale(width, height)
                             .useBuiltInDecoder(true)
                             .decode();

如果您想缩小到50%以下并获得HQ结果,请不要忘记使用内置解码器。


0

该答案基于https://developer.android.com/topic/performance/graphics/load-bitmap.html中提供的解决方案(不使用外部库)中提供并由我进行了一些更改以使其功能更好,更实用。

有关此解决方案的一些注意事项:

  1. 假定您要保持宽高比。换一种说法:

    finalWidth / finalHeight == sourceBitmap.getWidth() / sourceBitmap.getWidth() (与转换和舍入无关)

  2. 假定您有两个值(maxWidthmaxHeight),并且希望最终位图的任何尺寸不超过其对应值。换一种说法:

    finalWidth <= maxWidth && finalHeight <= maxHeight

    因此minRatio已被放置为计算的基础(请参阅实现)。不像maxRatio在实际中作为计算基础的基本解决方案。同样,的计算也要inSampleSize好得多(更多的逻辑,简短的效率)。

  3. 假设你想(至少)的最后一个维度具有完全相同其对应的包括maxValue的值 (每一个是可能的,通过考虑上述假设)。换一种说法:

    finalWidth == maxWidth || finalHeight == maxHeight

    与基本解决方案(Bitmap.createScaledBitmap(...))相比,最后的额外步骤是针对此“完全”约束。非常重要的一点是,您不应该一开始就采取此步骤(例如已接受的答案),因为在大图像情况下,它会消耗大量内存!

  4. 它用于解码file。您可以像对基本解码器resource(或所有BitmapFactory支持的解码器)的基本解决方案进行更改。

实现:

public static Bitmap decodeSampledBitmap(String pathName, int maxWidth, int maxHeight) {
    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(pathName, options);

    final float wRatio_inv = (float) options.outWidth / maxWidth,
          hRatio_inv = (float) options.outHeight / maxHeight; // Working with inverse ratios is more comfortable
    final int finalW, finalH, minRatio_inv /* = max{Ratio_inv} */;

    if (wRatio_inv > hRatio_inv) {
        minRatio_inv = (int) wRatio_inv;
        finalW = maxWidth;
        finalH = Math.round(options.outHeight / wRatio_inv);
    } else {
        minRatio_inv = (int) hRatio_inv;
        finalH = maxHeight;
        finalW = Math.round(options.outWidth / hRatio_inv);
    }

    options.inSampleSize = pow2Ceil(minRatio_inv); // pow2Ceil: A utility function that comes later
    options.inJustDecodeBounds = false; // Decode bitmap with inSampleSize set

    return Bitmap.createScaledBitmap(BitmapFactory.decodeFile(pathName, options),
          finalW, finalH, true);
}

/**
 * @return the largest power of 2 that is smaller than or equal to number. 
 * WARNING: return {0b1000000...000} for ZERO input.
 */
public static int pow2Ceil(int number) {
    return 1 << -(Integer.numberOfLeadingZeros(number) + 1); // is equivalent to:
    // return Integer.rotateRight(1, Integer.numberOfLeadingZeros(number) + 1);
}

样本用法,如果您有一个imageView的确定值layout_widthmatch_parent或一个显式值)和一个不确定的值layout_heightwrap_content),而是一个确定的值maxHeight

imageView.setImageBitmap(decodeSampledBitmap(filePath, 
        imageView.getWidth(), imageView.getMaxHeight()));
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.