在android中對大圖檔進行縮放真的很不盡如人意,不知道是不是我的方法不對。下面我列出3種對圖檔縮放的方法,并給出相應速度。請高人指教。
第一種是bitmapfactory和bitmapfactory.options。
首先,bitmapfactory.options有幾個fields很有用:
injustdecodebounds:if set to true, the decoder will return null (no bitmap), but the out...
也就是說,當injustdecodebounds設成true時,bitmap并不加載到記憶體,這樣效率很高哦。而這時,你可以獲得bitmap的高、寬等資訊。
outheight:the resulting height of the bitmap, set independent of the state of injustdecodebounds.
outwidth:the resulting width of the bitmap, set independent of the state of injustdecodebounds.
看到了吧,上面3個變量是相關聯的哦。
insamplesize : if set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
這就是用來做縮放比的。這裡有個技巧:
insamplesize=(outheight/height+outwidth/width)/2
實踐證明,這樣縮放出來的圖檔還是很好的。
最後用bitmapfactory.decodefile(path, options)生成。
由于隻是對bitmap加載到記憶體一次,是以效率比較高。解析速度快。
第二種是使用bitmap加matrix來縮放。
首先要獲得原bitmap,再從原bitmap的基礎上生成新圖檔。這樣效率很低。
第三種是用2.2新加的類thumbnailutils來做。
讓我們新看看這個類,從api中來看,此類就三個靜态方法:createvideothumbnail、extractthumbnail(bitmap source, int width, int height, int options)、extractthumbnail(bitmap source, int width, int height)。
我這裡使用了第三個方法。再看看它的源碼,下面會附上。是上面我們用到的bitmapfactory.options和matrix等經過人家一陣加工而成。
效率好像比第二種方法高一點點。
下面是我的例子:
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<imageview
android:id="@+id/imageshow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:id="@+id/image2"
/>
<textview
android:id="@+id/text"
android:layout_width="fill_parent"
android:text="@string/hello"
/>
</linearlayout>
package com.linc.resolvepicture;
import java.io.file;
import java.io.filenotfoundexception;
import java.io.fileoutputstream;
import java.io.ioexception;
import android.app.activity;
import android.graphics.bitmap;
import android.graphics.bitmapfactory;
import android.graphics.matrix;
import android.graphics.drawable.bitmapdrawable;
import android.graphics.drawable.drawable;
import android.media.thumbnailutils;
import android.os.bundle;
import android.util.log;
import android.widget.imageview;
import android.widget.textview;
public class resolvepicture extends activity {
private static string tag="resolvepicture";
drawable bmimg;
imageview imview;
imageview imview2;
textview text;
string thetime;
long start, stop;
/** called when the activity is first created. */
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
text=(textview)findviewbyid(r.id.text);
imview=(imageview) findviewbyid(r.id.imageshow);
imview2=(imageview) findviewbyid(r.id.image2);
bitmap bitmap = bitmapfactory.decoderesource(getresources(),
r.drawable.pic);
start=system.currenttimemillis();
// imview.setimagedrawable(resizeimage(bitmap, 300, 100));
imview2.setimagedrawable(resizeimage2("/sdcard/2.jpeg", 200, 100));
stop=system.currenttimemillis();
string thetime= string.format("\n1 iterative: (%d msec)",
stop - start);
imview.setimagebitmap(thumbnailutils.extractthumbnail(bitmap,200,100));//2.2才加進來的新類,簡單易用
// imview.setimagedrawable(resizeimage(bitmap, 30, 30));
thetime+= string.format("\n2 iterative: (%d msec)",
stop - start);
text.settext(thetime);
}
//使用bitmap加matrix來縮放
public static drawable resizeimage(bitmap bitmap, int w, int h)
{
bitmap bitmaporg = bitmap;
int width = bitmaporg.getwidth();
int height = bitmaporg.getheight();
int newwidth = w;
int newheight = h;
float scalewidth = ((float) newwidth) / width;
float scaleheight = ((float) newheight) / height;
matrix matrix = new matrix();
matrix.postscale(scalewidth, scaleheight);
// if you want to rotate the bitmap
// matrix.postrotate(45);
bitmap resizedbitmap = bitmap.createbitmap(bitmaporg, 0, 0, width,
height, matrix, true);
return new bitmapdrawable(resizedbitmap);
//使用bitmapfactory.options的insamplesize參數來縮放
public static drawable resizeimage2(string path,
int width,int height)
{
bitmapfactory.options options = new bitmapfactory.options();
options.injustdecodebounds = true;//不加載bitmap到記憶體中
bitmapfactory.decodefile(path,options);
int outwidth = options.outwidth;
int outheight = options.outheight;
options.indither = false;
options.inpreferredconfig = bitmap.config.argb_8888;
options.insamplesize = 1;
if (outwidth != 0 && outheight != 0 && width != 0 && height != 0)
{
int samplesize=(outwidth/width+outheight/height)/2;
log.d(tag, "samplesize = " + samplesize);
options.insamplesize = samplesize;
}
options.injustdecodebounds = false;
return new bitmapdrawable(bitmapfactory.decodefile(path, options));
//圖檔儲存
private void savethepicture(bitmap bitmap)
file file=new file("/sdcard/2.jpeg");
try
fileoutputstream fos=new fileoutputstream(file);
if(bitmap.compress(bitmap.compressformat.jpeg, 100, fos))
{
fos.flush();
fos.close();
}
catch(filenotfoundexception e1)
e1.printstacktrace();
catch(ioexception e2)
e2.printstacktrace();
}
thumbnailutils源碼:
/*
* copyright (c) 2009 the android open source project
*
* licensed under the apache license, version 2.0 (the "license");
* you may not use this file except in compliance with the license.
* you may obtain a copy of the license at
* http://www.apache.org/licenses/license-2.0
* unless required by applicable law or agreed to in writing, software
* distributed under the license is distributed on an "as is" basis,
* without warranties or conditions of any kind, either express or implied.
* see the license for the specific language governing permissions and
* limitations under the license.
*/
package android.media;
import android.content.contentresolver;
import android.content.contenturis;
import android.content.contentvalues;
import android.database.cursor;
import android.graphics.canvas;
import android.graphics.rect;
import android.media.mediametadataretriever;
import android.media.mediafile.mediafiletype;
import android.net.uri;
import android.os.parcelfiledescriptor;
import android.provider.basecolumns;
import android.provider.mediastore.images;
import android.provider.mediastore.images.thumbnails;
import java.io.fileinputstream;
import java.io.filedescriptor;
import java.io.outputstream;
/**
* thumbnail generation routines for media provider.
public class thumbnailutils {
private static final string tag = "thumbnailutils";
/* maximum pixels size for created bitmap. */
private static final int max_num_pixels_thumbnail = 512 * 384;
private static final int max_num_pixels_micro_thumbnail = 128 * 128;
private static final int unconstrained = -1;
/* options used internally. */
private static final int options_none = 0x0;
private static final int options_scale_up = 0x1;
/**
* constant used to indicate we should recycle the input in
* {@link #extractthumbnail(bitmap, int, int, int)} unless the output is the input.
*/
public static final int options_recycle_input = 0x2;
* constant used to indicate the dimension of mini thumbnail.
* @hide only used by media framework and media provider internally.
public static final int target_size_mini_thumbnail = 320;
* constant used to indicate the dimension of micro thumbnail.
public static final int target_size_micro_thumbnail = 96;
* this method first examines if the thumbnail embedded in exif is bigger than our target
* size. if not, then it'll create a thumbnail from original image. due to efficiency
* consideration, we want to let mediathumbrequest avoid calling this method twice for
* both kinds, so it only requests for micro_kind and set saveimage to true.
*
* this method always returns a "square thumbnail" for micro_kind thumbnail.
* @param filepath the path of image file
* @param kind could be mini_kind or micro_kind
* @return bitmap
* @hide this method is only used by media framework and media provider internally.
public static bitmap createimagethumbnail(string filepath, int kind) {
boolean wantmini = (kind == images.thumbnails.mini_kind);
int targetsize = wantmini
? target_size_mini_thumbnail
: target_size_micro_thumbnail;
int maxpixels = wantmini
? max_num_pixels_thumbnail
: max_num_pixels_micro_thumbnail;
sizedthumbnailbitmap sizedthumbnailbitmap = new sizedthumbnailbitmap();
bitmap bitmap = null;
mediafiletype filetype = mediafile.getfiletype(filepath);
if (filetype != null && filetype.filetype == mediafile.file_type_jpeg) {
createthumbnailfromexif(filepath, targetsize, maxpixels, sizedthumbnailbitmap);
bitmap = sizedthumbnailbitmap.mbitmap;
if (bitmap == null) {
try {
filedescriptor fd = new fileinputstream(filepath).getfd();
bitmapfactory.options options = new bitmapfactory.options();
options.insamplesize = 1;
options.injustdecodebounds = true;
bitmapfactory.decodefiledescriptor(fd, null, options);
if (options.mcancel || options.outwidth == -1
|| options.outheight == -1) {
return null;
}
options.insamplesize = computesamplesize(
options, targetsize, maxpixels);
options.injustdecodebounds = false;
options.indither = false;
options.inpreferredconfig = bitmap.config.argb_8888;
bitmap = bitmapfactory.decodefiledescriptor(fd, null, options);
} catch (ioexception ex) {
log.e(tag, "", ex);
if (kind == images.thumbnails.micro_kind) {
// now we make it a "square thumbnail" for micro_kind thumbnail
bitmap = extractthumbnail(bitmap,
target_size_micro_thumbnail,
target_size_micro_thumbnail, options_recycle_input);
return bitmap;
* create a video thumbnail for a video. may return null if the video is
* corrupt or the format is not supported.
* @param filepath the path of video file
public static bitmap createvideothumbnail(string filepath, int kind) {
mediametadataretriever retriever = new mediametadataretriever();
try {
retriever.setmode(mediametadataretriever.mode_capture_frame_only);
retriever.setdatasource(filepath);
bitmap = retriever.captureframe();
} catch (illegalargumentexception ex) {
// assume this is a corrupt video file
} catch (runtimeexception ex) {
// assume this is a corrupt video file.
} finally {
retriever.release();
} catch (runtimeexception ex) {
// ignore failures while cleaning up.
if (kind == images.thumbnails.micro_kind && bitmap != null) {
options_recycle_input);
* creates a centered bitmap of the desired size.
* @param source original bitmap source
* @param width targeted width
* @param height targeted height
public static bitmap extractthumbnail(
bitmap source, int width, int height) {
return extractthumbnail(source, width, height, options_none);
* @param options options used during thumbnail extraction
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;
/*
* compute the sample size as a function of minsidelength
* and maxnumofpixels.
* minsidelength is used to specify that minimal width or height of a
* bitmap.
* maxnumofpixels is used to specify the maximal size in pixels that is
* tolerable in terms of memory usage.
* the function returns a sample size based on the constraints.
* both size and minsidelength can be passed in as iimage.unconstrained,
* which indicates no care of the corresponding constraint.
* the functions prefers returning a sample size that
* generates a smaller bitmap, unless minsidelength = iimage.unconstrained.
* also, the function rounds up the sample size to a power of 2 or multiple
* of 8 because bitmapfactory only honors sample size this way.
* for example, bitmapfactory downsamples an image by 2 even though the
* request is 3. so we round up the sample size to avoid oom.
private static int computesamplesize(bitmapfactory.options options,
int minsidelength, int maxnumofpixels) {
int initialsize = computeinitialsamplesize(options, minsidelength,
maxnumofpixels);
int roundedsize;
if (initialsize <= 8 ) {
roundedsize = 1;
while (roundedsize < initialsize) {
roundedsize <<= 1;
roundedsize = (initialsize + 7) / 8 * 8;
return roundedsize;
private static int computeinitialsamplesize(bitmapfactory.options options,
double w = options.outwidth;
double h = options.outheight;
int lowerbound = (maxnumofpixels == unconstrained) ? 1 :
(int) math.ceil(math.sqrt(w * h / maxnumofpixels));
int upperbound = (minsidelength == unconstrained) ? 128 :
(int) math.min(math.floor(w / minsidelength),
math.floor(h / minsidelength));
if (upperbound < lowerbound) {
// return the larger one when there is no overlapping zone.
return lowerbound;
if ((maxnumofpixels == unconstrained) &&
(minsidelength == unconstrained)) {
return 1;
} else if (minsidelength == unconstrained) {
return upperbound;
* make a bitmap from a given uri, minimal side length, and maximum number of pixels.
* the image data will be read from specified pfd if it's not null, otherwise
* a new input stream will be created using specified contentresolver.
* clients are allowed to pass their own bitmapfactory.options used for bitmap decoding. a
* new bitmapfactory.options will be created if options is null.
private static bitmap makebitmap(int minsidelength, int maxnumofpixels,
uri uri, contentresolver cr, parcelfiledescriptor pfd,
bitmapfactory.options options) {
bitmap b = null;
if (pfd == null) pfd = makeinputstream(uri, cr);
if (pfd == null) return null;
if (options == null) options = new bitmapfactory.options();
filedescriptor fd = pfd.getfiledescriptor();
options.insamplesize = 1;
options.injustdecodebounds = true;
bitmapfactory.decodefiledescriptor(fd, null, options);
if (options.mcancel || options.outwidth == -1
|| options.outheight == -1) {
return null;
options.insamplesize = computesamplesize(
options, minsidelength, maxnumofpixels);
options.injustdecodebounds = false;
options.indither = false;
options.inpreferredconfig = bitmap.config.argb_8888;
b = bitmapfactory.decodefiledescriptor(fd, null, options);
} catch (outofmemoryerror ex) {
log.e(tag, "got oom exception ", ex);
closesilently(pfd);
return b;
private static void closesilently(parcelfiledescriptor c) {
if (c == null) return;
try {
c.close();
} catch (throwable t) {
// do nothing
}
private static parcelfiledescriptor makeinputstream(
uri uri, contentresolver cr) {
return cr.openfiledescriptor(uri, "r");
} catch (ioexception ex) {
* transform source bitmap to targeted width and height.
private static bitmap transform(matrix scaler,
bitmap source,
int targetwidth,
int targetheight,
int options) {
boolean scaleup = (options & options_scale_up) != 0;
boolean recycle = (options & options_recycle_input) != 0;
int deltax = source.getwidth() - targetwidth;
int deltay = source.getheight() - targetheight;
if (!scaleup && (deltax < 0 || deltay < 0)) {
/*
* in this case the bitmap is smaller, at least in one dimension,
* than the target. transform it by placing as much of the image
* as possible into the target and leaving the top/bottom or
* left/right (or both) black.
*/
bitmap b2 = bitmap.createbitmap(targetwidth, targetheight,
bitmap.config.argb_8888);
canvas c = new canvas(b2);
int deltaxhalf = math.max(0, deltax / 2);
int deltayhalf = math.max(0, deltay / 2);
rect src = new rect(
deltaxhalf,
deltayhalf,
deltaxhalf + math.min(targetwidth, source.getwidth()),
deltayhalf + math.min(targetheight, source.getheight()));
int dstx = (targetwidth - src.width()) / 2;
int dsty = (targetheight - src.height()) / 2;
rect dst = new rect(
dstx,
dsty,
targetwidth - dstx,
targetheight - dsty);
c.drawbitmap(source, src, dst, null);
if (recycle) {
source.recycle();
return b2;
float bitmapwidthf = source.getwidth();
float bitmapheightf = source.getheight();
float bitmapaspect = bitmapwidthf / bitmapheightf;
float viewaspect = (float) targetwidth / targetheight;
if (bitmapaspect > viewaspect) {
float scale = targetheight / bitmapheightf;
if (scale < .9f || scale > 1f) {
scaler.setscale(scale, scale);
} else {
scaler = null;
float scale = targetwidth / bitmapwidthf;
bitmap b1;
if (scaler != null) {
// this is used for minithumb and crop, so we want to filter here.
b1 = bitmap.createbitmap(source, 0, 0,
source.getwidth(), source.getheight(), scaler, true);
b1 = source;
if (recycle && b1 != source) {
source.recycle();
int dx1 = math.max(0, b1.getwidth() - targetwidth);
int dy1 = math.max(0, b1.getheight() - targetheight);
bitmap b2 = bitmap.createbitmap(
b1,
dx1 / 2,
dy1 / 2,
targetwidth,
targetheight);
if (b2 != b1) {
if (recycle || b1 != source) {
b1.recycle();
return b2;
* sizedthumbnailbitmap contains the bitmap, which is downsampled either from
* the thumbnail in exif or the full image.
* mthumbnaildata, mthumbnailwidth and mthumbnailheight are set together only if mthumbnail
* is not null.
* the width/height of the sized bitmap may be different from mthumbnailwidth/mthumbnailheight.
private static class sizedthumbnailbitmap {
public byte[] mthumbnaildata;
public bitmap mbitmap;
public int mthumbnailwidth;
public int mthumbnailheight;
* creates a bitmap by either downsampling from the thumbnail in exif or the full image.
* the functions returns a sizedthumbnailbitmap,
* which contains a downsampled bitmap and the thumbnail data in exif if exists.
private static void createthumbnailfromexif(string filepath, int targetsize,
int maxpixels, sizedthumbnailbitmap sizedthumbbitmap) {
if (filepath == null) return;
exifinterface exif = null;
byte [] thumbdata = null;
exif = new exifinterface(filepath);
if (exif != null) {
thumbdata = exif.getthumbnail();
log.w(tag, ex);
bitmapfactory.options fulloptions = new bitmapfactory.options();
bitmapfactory.options exifoptions = new bitmapfactory.options();
int exifthumbwidth = 0;
int fullthumbwidth = 0;
// compute exifthumbwidth.
if (thumbdata != null) {
exifoptions.injustdecodebounds = true;
bitmapfactory.decodebytearray(thumbdata, 0, thumbdata.length, exifoptions);
exifoptions.insamplesize = computesamplesize(exifoptions, targetsize, maxpixels);
exifthumbwidth = exifoptions.outwidth / exifoptions.insamplesize;
// compute fullthumbwidth.
fulloptions.injustdecodebounds = true;
bitmapfactory.decodefile(filepath, fulloptions);
fulloptions.insamplesize = computesamplesize(fulloptions, targetsize, maxpixels);
fullthumbwidth = fulloptions.outwidth / fulloptions.insamplesize;
// choose the larger thumbnail as the returning sizedthumbbitmap.
if (thumbdata != null && exifthumbwidth >= fullthumbwidth) {
int width = exifoptions.outwidth;
int height = exifoptions.outheight;
exifoptions.injustdecodebounds = false;
sizedthumbbitmap.mbitmap = bitmapfactory.decodebytearray(thumbdata, 0,
thumbdata.length, exifoptions);
if (sizedthumbbitmap.mbitmap != null) {
sizedthumbbitmap.mthumbnaildata = thumbdata;
sizedthumbbitmap.mthumbnailwidth = width;
sizedthumbbitmap.mthumbnailheight = height;
fulloptions.injustdecodebounds = false;
sizedthumbbitmap.mbitmap = bitmapfactory.decodefile(filepath, fulloptions);
}