android图片缩放
来源:互联网 发布:乱码修复软件 编辑:程序博客网 时间:2024/06/06 18:00
Android图片缩放总结及比较
第一种是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"
- />
- <ImageView
- android:id="@+id/image2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- />
- <TextView
- android:id="@+id/text"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- 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);
- start=System.currentTimeMillis();
- imView.setImageBitmap(ThumbnailUtils.extractThumbnail(bitmap,200,100));//2.2才加进来的新类,简单易用
- // imView.setImageDrawable(resizeImage(bitmap, 30, 30));
- stop=System.currentTimeMillis();
- 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.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Matrix;
- 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 android.util.Log;
- import java.io.FileInputStream;
- import java.io.FileDescriptor;
- import java.io.IOException;
- 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.
- * @hide Only used by media framework and media provider internally.
- */
- 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
- * @param kind could be MINI_KIND or MICRO_KIND
- */
- public static Bitmap createVideoThumbnail(String filePath, int kind) {
- Bitmap bitmap = null;
- 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 {
- try {
- retriever.release();
- } catch (RuntimeException ex) {
- // Ignore failures while cleaning up.
- }
- }
- if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {
- bitmap = extractThumbnail(bitmap,
- TARGET_SIZE_MICRO_THUMBNAIL,
- TARGET_SIZE_MICRO_THUMBNAIL,
- OPTIONS_RECYCLE_INPUT);
- }
- return bitmap;
- }
- /**
- * 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);
- }
- /**
- * 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;
- }
- /*
- * 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;
- }
- } else {
- roundedSize = (initialSize + 7) / 8 * 8;
- }
- return roundedSize;
- }
- private static int computeInitialSampleSize(BitmapFactory.Options options,
- int minSideLength, int maxNumOfPixels) {
- 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 lowerBound;
- } else {
- 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;
- try {
- 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);
- return null;
- } finally {
- 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) {
- try {
- return cr.openFileDescriptor(uri, "r");
- } catch (IOException ex) {
- return null;
- }
- }
- /**
- * 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;
- }
- } else {
- float scale = targetWidth / bitmapWidthF;
- if (scale < .9F || scale > 1F) {
- scaler.setScale(scale, scale);
- } else {
- scaler = null;
- }
- }
- 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);
- } else {
- 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;
- try {
- exif = new ExifInterface(filePath);
- if (exif != null) {
- thumbData = exif.getThumbnail();
- }
- } catch (IOException ex) {
- 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;
- }
- } else {
- fullOptions.inJustDecodeBounds = false;
- sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);
- }
- }
- }
----------------------------------------------
参考2:
如何为你的Android应用缩放图片
很难为你的应用程序得到正确的图像缩放吗?是你的图片过大,造成内存问题?还是图片不正确缩放造成不良用户体验的结果?为了寻求一个好的解决方案,我们咨询了Andreas Agvard(索尼爱立信软件部门),让他分享一些关于这方面的经验。
注意:本文没有完整显示出代码示例。你可以下载本文的PDF,来看完整的代码示例。
在索尼爱立信软件部门工作,我经常遇到需要图片缩放的应用,例如:当处理别人或者网络上提供的图片。缩放是必要的,因为通常情况下的图片不是你想要呈现的那样。
典型的例子,如果你正在为你的应用开发一个LiveView™扩展。大多数人开发应用利用LiveView™和其他第二屏幕设备,可能需要重新调整图片,重要的是要保持适当的缩放比例和图像质量。当然,在很多情况下,改变图片尺寸是一个有点困难,但是很有效的途径。
ImageView解决了许多的图片缩放问题,首先,至少你在设置完一个图片源后,不用去解码或缩放图片。但有时需要你自己去解码控制,这是本教程的用武之地。随着本教程,我写了一个代码示例,下载图片缩放代码示例。在文本中呈现的效果,可以通过编译和运行该项目来看到。
孤立的问题
我做这个教程,是因为我已经有一些实用方法来实现图片的缩放,为了避免最常见的图片缩放问题。如下面的例子:
1
Bitmap unscaledBitmap = BitmapFactory.decodeResource(getResources(), mSourceId);
2
Bitmap scaledBitmap = Bitmap.createScaledBitmap(unscaledBitmap, wantedWidth, wantedHeight,
true
);
那么在上面的代码中,什么是正确的,什么是错的?让我们来看看在不同的代码行。
行1:整个源图像解码到一个位图。
- 这可能会导致内存不足的错误,如果图片太大的话。
- 这可能会导致在一个高分辨率上解码图像。这可能会很慢,但智能解码器可为解码提高性能。
- 缩放图片很多时候是,高分辨率位图缩放到低分辨率,会导致锯齿的问题。使用位图过滤(例如,通过传送`true`参数到Bitmap.createScaledBitmap(...))减少了锯齿,但是还是不够。
行2:解码的位图缩放到想要的大小。
- 源图像的尺寸和想要的图像尺寸在长宽比上可能是不一样的。这将导致图像的拉伸。
左边的图片:原始图像。右边的图片:缩放后图片。可以看出明显的失真问题,如原图的眼睛非常的鲜明,缩放后就没有了。高度出现拉伸。
创建一个解决方案
我们的解决方案,将有一个结构类似上述代码,其中的一部分将取代行1,这样为缩放做准备。另一部分将取代行2,做最后的缩放。我们将开始替换行2的部分代码,引入两个新的概念,裁剪和合适。
替换行2
在这一部分,我们将缩放位图到我们所需要的。这一步很必要,因为之前的解码能力是有限的。此外,在这一步为了避免拉伸,我们可能要重新调整图片到想要的大小。
有两种可能性可以避免拉伸。不管是那种,我们都要调整尺寸,以确保他们有相同的宽高比;即缩放图像作为源图像,直到它适合想要的尺寸,或裁剪具有相同的宽高比的源图像为想要的尺寸。
左边的图片:图像通过fit方法缩放。图片已被缩小到适合的尺寸和高度,结果是小于想要的高度。右边的图像:图像crop方法缩放。图像已被缩放到适应至少想要的尺寸。因此原图已被裁剪,切割了成左边和右边二部分。
为了缩放这样的效果,我们的实现代码如下:
1
public
static
Bitmap createScaledBitmap(Bitmap unscaledBitmap,
int
dstWidth,
int
dstHeight, ScalingLogic scalingLogic) {
2
Rect srcRect = calculateSrcRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
3
Rect dstRect = calculateDstRect(unscaledBitmap.getWidth(), unscaledBitmap.getHeight(), dstWidth, dstHeight, scalingLogic);
4
Bitmap scaledBitmap = Bitmap.createBitmap(dstRect.width(), dstRect.height(), Config.ARGB_8888);
5
Canvas canvas =
new
Canvas(scaledBitmap);
6
canvas.drawBitmap(unscaledBitmap, srcRect, dstRect,
new
Paint(Paint.FILTER_BITMAP_FLAG));
return
scaledBitmap;
7
}
在上面的代码,我们使用canvas.drawBitmap(...)做缩放。这种方法的裁剪区域是从源图像的规模面积定义画布的矩形为指定的目标矩形区域。为了避免拉伸,这两个矩形需要有相同的长宽比。我们还调用了两个实用的方法,一个为创建源矩形和另一个为创建目标矩形。方法如下:
01
public
static
Rect calculateSrcRect(
int
srcWidth,
int
srcHeight,
int
dstWidth,
int
dstHeight, ScalingLogic scalingLogic) {
02
if
(scalingLogic == ScalingLogic.CROP) {
03
final
float
srcAspect = (
float
)srcWidth / (
float
)srcHeight;
04
final
float
dstAspect = (
float
)dstWidth / (
float
)dstHeight;
05
if
(srcAspect > dstAspect) {
06
final
int
srcRectWidth = (
int
)(srcHeight * dstAspect);
07
final
int
srcRectLeft = (srcWidth - srcRectWidth) /
2
;
08
return
new
Rect(srcRectLeft,
0
, srcRectLeft + srcRectWidth, srcHeight);
09
}
else
{
10
final
int
srcRectHeight = (
int
)(srcWidth / dstAspect);
11
final
int
scrRectTop = (
int
)(srcHeight - srcRectHeight) /
2
;
12
return
new
Rect(
0
, scrRectTop, srcWidth, scrRectTop + srcRectHeight);
13
}
14
}
else
{
15
return
new
Rect(
0
,
0
, srcWidth, srcHeight);
16
}
17
}
18
public
static
Rect calculateDstRect(
int
srcWidth,
int
srcHeight,
int
dstWidth,
int
dstHeight, ScalingLogic scalingLogic) {
19
if
(scalingLogic == ScalingLogic.FIT) {
20
final
float
srcAspect = (
float
)srcWidth / (
float
)srcHeight;
21
final
float
dstAspect = (
float
)dstWidth / (
float
)dstHeight;
22
if
(srcAspect > dstAspect) {
23
return
new
Rect(
0
,
0
, dstWidth, (
int
)(dstWidth / srcAspect));
24
}
else
{
25
return
new
Rect(
0
,
0
, (
int
)(dstHeight * srcAspect), dstHeight);
26
}
27
}
else
{
28
return
new
Rect(
0
,
0
, dstWidth, dstHeight);
29
}
30
}
在刚好合适的情况下源矩形会包含整个源尺寸。在需要裁剪的情况下,它会计算好具有相同宽高比的目标图像,来裁剪源图像的宽度或高度,以达到你想要的尺寸。而刚好在合适的情况下,将有相同宽高比的源图像,调整成你想要的尺寸的宽度或高度。
替换行1
解码器很智能,特别是用于JPEG和PNG的格式。这些解码器在图片解码时可以进行缩放,并且性能也有所改善,这样锯齿问题也可以避免。此外,由于图片解码后变小了,需要的内存也会较少。
缩放解码的时候,只要简单设置上BitmapFactory.Options对象的inSampleSize参数,并把它传递给BitmapFactory。样本大小指定一个缩放图像大小的抽象因素,例如2是640×480图像在320×240图像上解码的因素。样本大小设置时,你不能保证严格按照这个数字,图像将被缩减,但至少它不会更小。例如,3倍640×480的图像可能会导致在一个320×240图像不支持值。通常情况下,至少2的一次方支持[1,2,4,8,...]。
下一步是指定一个合适的样本大小。合适的样本大小将产生最大的缩放,但仍然是大于等于你想要的图像尺寸。如下面代码:
01
public
static
Bitmap decodeFile(String pathName,
int
dstWidth,
int
dstHeight, ScalingLogic scalingLogic) {
02
Options options =
new
Options();
03
options.inJustDecodeBounds =
true
;
04
BitmapFactory.decodeFile(pathName, options);
05
options.inJustDecodeBounds =
false
;
06
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight, scalingLogic);
07
Bitmap unscaledBitmap = BitmapFactory.decodeFile(pathName, options);
08
return
unscaledBitmap;
09
}
10
public
static
int
calculateSampleSize(
int
srcWidth,
int
srcHeight,
int
dstWidth,
int
dstHeight, ScalingLogic scalingLogic) {
11
if
(scalingLogic == ScalingLogic.FIT) {
12
final
float
srcAspect = (
float
)srcWidth / (
float
)srcHeight;
13
final
float
dstAspect = (
float
)dstWidth / (
float
)dstHeight;
14
if
(srcAspect > dstAspect) {
15
return
srcWidth / dstWidth;
16
}
else
{
17
return
srcHeight / dstHeight;
18
}
19
}
else
{
20
final
float
srcAspect = (
float
)srcWidth / (
float
)srcHeight;
21
final
float
dstAspect = (
float
)dstWidth / (
float
)dstHeight;
22
if
(srcAspect > dstAspect) {
23
return
srcHeight / dstHeight;
24
}
else
{
25
return
srcWidth / dstWidth;
26
}
27
}
28
}
在decodeFile(...)方法中,我们解码一个文件进行了最终缩放尺度。这是首先要通过解码源图片尺寸,然后使用calculateSampleSize(...)计算最佳样本大小,最后使用此样本的大小解码图像。如果你有兴趣的话,你可以更深入了解calculateSampleSize(...)方法,但以上方法基本可确保图片进行缩放。
全部放在一起
根据上面我们指定的方法的,现在可以执行替换最初的代码行:
1
Bitmap unscaledBitmap = decodeFile(pathname, dstWidth, dstHeight, scalingLogic);
2
Bitmap scaledBitmap = createScaledBitmap(unscaledBitmap, dstWidth, dstHeight, scalingLogic);
左边的图像:原始解决方案,解码消耗6693 KB的内存和1/4秒左右。结果被拉长失真。中间的图像:同比缩放解决方案,解码消耗418 KB的内存和1/10秒左右。右边的图像:裁剪解决方案,解码消耗418 KB的内存和1/10秒左右。
想要了解更多信息,请下载我们的代码示例。有了这个源码项目,你可以看到你的Android手机上运行的结果。
---------------------------
参考3:
http://wenku.baidu.com/view/9ab59c0690c69ec3d5bb750e.html
http://wenku.baidu.com/link?url=8LPD7R0QEUHpybZ4xmw5WckufpCOc_N9EZTb9-lo-27aBn12vogDZfSURw-NM3HLseXrGXU1GIyq9vWbId-3P71Elh8nSC_x5hjt6SuV5RS
-----------------------------
参考4:
[Android实例] 图片察看程序,支持缩放,滚动 [复制链接]
发表于 2011-6-2 04:50 | 来自 51CTO网页
[只看他] 楼主
--------------------------------------
参考5:
最近写了一个Android单图片显示的Activity,基于Android 2.1+(API 7+),实现浮现放大缩小控制按钮进行大小缩放,双击放大缩小,移动,多点(两点)缩放功能。
这个东西还比较常用,我参看了Android源码中的Gallery和Gallery3D,主要是Gallery,这个可以实现“浮现放大缩小控制 按钮进行大小缩放,双击放大缩小,移动”,但是不支持多点缩放,而且这个是基于列表显示的,有很多无用的代码。(这两个应用单独的源码可以通过git clone https://android.googlesource.com/platform/packages/apps/Gallery.git和https://android.googlesource.com/platform/packages/apps/Gallery3D下载)
应用效果观察了:快图浏览,Gallery3D,Gallery,MIUI图库4款应用,主要体会了多点缩放。快图浏览和MIUI图库缩放效果类似,Gallery3D多点缩放仔细斟酌起来,感觉有点问题,会产生诡异的偏移。
先看一下Gallery的源码,由两个package组成,分别是com.android.camera和 com.android.camera.gallery。com.android.camera.gallery主要包含了封装的一些Image类,层次 结构在IImageList中给出了:
// ImageList and Image classes have one-to-one correspondence.
// The class hierarchy (* = abstract class):
//
// IImageList
// - BaseImageList (*)
// - VideoList
// - ImageList
// - DrmImageList
// - SingleImageList (contains UriImage)
// - ImageListUber
//
// IImage
// - BaseImage (*)
// - VideoObject
// - Image
// - DrmImage
// - UriImage
//
还包含了一个LruCache,Least Recently Used缓存算法的一个实现。
重点是com.android.camera包,主要涉及到ViewImage,ImageViewTouchBase,RotateBitmap,ImageGetter等类。
ImageViewTouchBase类是一个抽象类,继承自ImageView,实现了放大缩小zoom,平移pan等方法。主要用到Matrix类,起初我还不是很明白这个是个什么东西,看了文档也不清楚。官方文档如下:
The Matrix class holds a 3x3 matrix for transforming coordinates. Matrix does not have a constructor, so it must be explicitly initialized using either reset() - to construct an identity matrix, or one of the set..() functions (e.g. setTranslate, setRotate, etc.).
后来看了一段时间源代码,突然想起了图形学中的变换矩阵,瞬间明白了这个东西就是图形的变换矩阵啊。
变换矩阵可以参看维基百科内容http://zh.wikipedia.org/wiki/%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5#.E5.9C.A8.E4.BA.8C.E7.BB.B4.E5.9B.BE.E5.BD.A2.E4.B8.AD.E7.9A.84.E5.BA.94.E7.94.A8.E7.A4.BA.E4.BE.8B
ImageViewTouchBase用到了3个Matrix,分别是 mBaseMatrix,mSuppMatrix,mDisplayMatrix。其中mBaseMatrix用于显示原始image的基础变换矩 阵,mSuppMatrix存储用户对图形产生的缩放平移变换的外加的矩阵。mDisplayMatrix是基本矩阵加上额外的变换矩阵产生的最终的矩 阵。
然后剩下的一些放大缩小平移的方法,就是改变mSuppMatrix的值,应用缩放平移矩阵变换,然后算出最终的mDisplayMatrix。然 后调用父类ImageView的setImageMatrix方法,应用矩阵变换。(ImageViewTouchBase的ScaleType已经被设 定为MATRIX)
然后ViewImage是用于显示图片的这个Activity,里面大多是处理交互的逻辑,处理双击缩放,平移,缩放按钮的显示隐藏等。缩放按钮 Android提供了ZoomButtonsController,这个是继承自Object类的,不是一个View,构造的时候需要传入一个 ownerView,构造函数是:ZoomButtonsController(View ownerView)
ZoomButtonsController的实现按钮点击的机制有点坑,它捕获了ownerView的onTouch事件,所以当ZoomButtonsController可见后,我在处理ownerView的事件时,发现都不会触发了,纠结了很久。
然后稍加修正一个可以放大缩小平移的用于显示图片的Activity就成型了。然后就是添加多点缩放功能,Gallery没有提供多点缩放的功能。 Gallery3D有多点缩放,但是使用Surface实现的,但是还是有一些帮助,翻了代码找到ScaleGestureDetector类,发现 Android在API Level 8引入了这个类,但是看了源代码发现,里面实现的细节还是有很多出入的。我把API中的ScaleGestureDetector抠了出来,可以用。实现 多点缩放的话,IamgeViewTouchBash类中的一些方法可以用,缩放方法是一样的,就是计算缩放中心点和缩放大小,Android自API Level 5引入了多点的API,也就是说Android 1.6(即API 4)及以下没法多点缩放了。ScaleGestureDetector没有提供两点中点坐标的方法,但是可以轻松添加,提供了缩放大小方法。还涉及到一些 小细节的改变,然后让Activity的rootLayout监听onTouch事件。有一个小bug,就是多点变一点后有可能触发平移。看 Gallery3D代码也是用GestureDetector和ScaleGestureDetector来实现的,而且又重载了onTouch事件处理 函数,感觉有点乱。我只用了GestureDetector和ScaleGestureDetector,也有点乱,这个稍后要重构一下。
现在基本能用,代码在github上,地址:https://github.com/qhm123/SimpleTouchImageView (后来又加了写别的东西,单个图片查看Activity的地址是https://github.com/qhm123/SimpleTouchImageView/tree/v_feedback_fixed)
显示图片的Activity是TouchImageActivity,传入图片地址url参数即可。
考虑到的后期优化:
增加ImageGetter缩略图效果。
增加放大缩小平移超出范围时的回放效果。(已加)
增加多点旋转。
过程中找到一篇Blog《Touch事件派发过程详解》,感觉不错。
参考6:
http://blog.csdn.net/stonecao/article/details/6759189
- Android 图片缩放
- Android 图片缩放-Matrix
- android图片的缩放
- android图片的缩放
- Android 图片缩放-Matrix
- android图片的缩放 .
- android 两点缩放图片
- android ImageView图片缩放
- android 图片缩放
- android 图片缩放
- android 两点缩放图片
- Android图片缩放方法
- android 图片缩放
- Android 缩放图片
- android缩放图片
- Android 图片缩放
- android图片手势缩放
- android ImageView图片缩放
- 生成代码
- HTML入门之2.1
- 常用类库-01图片:生成缩略图
- 简介 WatiN是一个开源的用于Web测试自动化的类库,从watir中获得的灵感,使用C#开发。WatiN通过与浏览器的交互来实现自动化,使用起来具有轻便,简单的特点。目前最新版本为2.0,加入了对
- C#多线程处理文件的简单例子
- android图片缩放
- js 用数组初始化下拉框
- ASP.NET状态管理的总结
- codeforce Sereja and Coat Rack(简单排序)
- PHP CodeIgniter向数据库插入数据时报错Undefined property: User::$db
- spring整合jdbc
- ant的安装以及使用
- PHP----Ajax异步请求
- Watin概述