Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> Android縮略圖類源代碼

Android縮略圖類源代碼

編輯:Android開發實例

Android 2.2開始新增的縮略圖類ThumbnailUtils的主要方法是靜態的,對於Android 2.2或API Level8以下的工程可以直接使用,本類相對於我們常規的縮略圖類考慮更周全,除了尺寸比例優化外,針對OOM的內存管理方面有更周全的處理方式,完整代碼如下,使用方法和介紹請查看 ThumbnailUtils - Android2.2新增類 一文
 

  1. public class ThumbnailUtils {  
  2.     private static final String TAG = "ThumbnailUtils";  
  3.  
  4.     /* Maximum pixels size for created bitmap. */ 
  5.     private static final int MAX_NUM_PIXELS_THUMBNAIL = 512 * 384;  
  6.     private static final int MAX_NUM_PIXELS_MICRO_THUMBNAIL = 128 * 128;  
  7.     private static final int UNCONSTRAINED = -1;  
  8.  
  9.     /* Options used internally. */ 
  10.     private static final int OPTIONS_NONE = 0x0;  
  11.     private static final int OPTIONS_SCALE_UP = 0x1;  
  12.  
  13.     /**  
  14.      * Constant used to indicate we should recycle the input in  
  15.      * [email protected] #extractThumbnail(Bitmap, int, int, int)} unless the output is the input.  
  16.      */ 
  17.     public static final int OPTIONS_RECYCLE_INPUT = 0x2;  
  18.  
  19.     /**  
  20.      * Constant used to indicate the dimension of mini thumbnail.  
  21.      * @hide Only used by media framework and media provider internally.  
  22.      */ 
  23.     public static final int TARGET_SIZE_MINI_THUMBNAIL = 320;  
  24.  
  25.     /**  
  26.      * Constant used to indicate the dimension of micro thumbnail.  
  27.      * @hide Only used by media framework and media provider internally.  
  28.      */ 
  29.     public static final int TARGET_SIZE_MICRO_THUMBNAIL = 96;  
  30.  
  31.     /**  
  32.      * This method first examines if the thumbnail embedded in EXIF is bigger than our target  
  33.      * size. If not, then it'll create a thumbnail from original image. Due to efficiency  
  34.      * consideration, we want to let MediaThumbRequest avoid calling this method twice for  
  35.      * both kinds, so it only requests for MICRO_KIND and set saveImage to true.  
  36.      *  
  37.      * This method always returns a "square thumbnail" for MICRO_KIND thumbnail.  
  38.      *  
  39.      * @param filePath the path of image file  
  40.      * @param kind could be MINI_KIND or MICRO_KIND  
  41.      * @return Bitmap  
  42.      *  
  43.      * @hide This method is only used by media framework and media provider internally.  
  44.      */ 
  45.     public static Bitmap createImageThumbnail(String filePath, int kind) {  
  46.         boolean wantMini = (kind == Images.Thumbnails.MINI_KIND);  
  47.         int targetSize = wantMini  
  48.                 ? TARGET_SIZE_MINI_THUMBNAIL  
  49.                 : TARGET_SIZE_MICRO_THUMBNAIL;  
  50.         int maxPixels = wantMini  
  51.                 ? MAX_NUM_PIXELS_THUMBNAIL  
  52.                 : MAX_NUM_PIXELS_MICRO_THUMBNAIL;  
  53.         SizedThumbnailBitmap sizedThumbnailBitmap = new SizedThumbnailBitmap();  
  54.         Bitmap bitmap = null;  
  55.         MediaFileType fileType = MediaFile.getFileType(filePath);  
  56.         if (fileType != null && fileType.fileType == MediaFile.FILE_TYPE_JPEG) {  
  57.             createThumbnailFromEXIF(filePath, targetSize, maxPixels, sizedThumbnailBitmap);  
  58.             bitmap = sizedThumbnailBitmap.mBitmap;  
  59.         }  
  60.  
  61.         if (bitmap == null) {  
  62.             try {  
  63.                 FileDescriptor fd = new FileInputStream(filePath).getFD();  
  64.                 BitmapFactory.Options options = new BitmapFactory.Options();  
  65.                 options.inSampleSize = 1;  
  66.                 options.inJustDecodeBounds = true;  
  67.                 BitmapFactory.decodeFileDescriptor(fd, null, options);  
  68.                 if (options.mCancel || options.outWidth == -1 
  69.                         || options.outHeight == -1) {  
  70.                     return null;  
  71.                 }  
  72.                 options.inSampleSize = computeSampleSize(  
  73.                         options, targetSize, maxPixels);  
  74.                 options.inJustDecodeBounds = false;  
  75.  
  76.                 options.inDither = false;  
  77.                 options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
  78.                 bitmap = BitmapFactory.decodeFileDescriptor(fd, null, options);  
  79.             } catch (IOException ex) {  
  80.                 Log.e(TAG, "", ex);  
  81.             }  
  82.         }  
  83.  
  84.         if (kind == Images.Thumbnails.MICRO_KIND) {  
  85.             // now we make it a "square thumbnail" for MICRO_KIND thumbnail  
  86.             bitmap = extractThumbnail(bitmap,  
  87.                     TARGET_SIZE_MICRO_THUMBNAIL,  
  88.                     TARGET_SIZE_MICRO_THUMBNAIL, OPTIONS_RECYCLE_INPUT);  
  89.         }  
  90.         return bitmap;  
  91.     }  
  92.  
  93.     /**  
  94.      * Create a video thumbnail for a video. May return null if the video is  
  95.      * corrupt or the format is not supported.  
  96.      *  
  97.      * @param filePath the path of video file  
  98.      * @param kind could be MINI_KIND or MICRO_KIND  
  99.      */ 
  100.     public static Bitmap createVideoThumbnail(String filePath, int kind) {  
  101.         Bitmap bitmap = null;  
  102.         MediaMetadataRetriever retriever = new MediaMetadataRetriever();  
  103.         try {  
  104.             retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);  
  105.             retriever.setDataSource(filePath);  
  106.             bitmap = retriever.captureFrame();  
  107.         } catch (IllegalArgumentException ex) {  
  108.             // Assume this is a corrupt video file  
  109.         } catch (RuntimeException ex) {  
  110.             // Assume this is a corrupt video file.  
  111.         } finally {  
  112.             try {  
  113.                 retriever.release();  
  114.             } catch (RuntimeException ex) {  
  115.                 // Ignore failures while cleaning up.  
  116.             }  
  117.         }  
  118.         if (kind == Images.Thumbnails.MICRO_KIND && bitmap != null) {  
  119.             bitmap = extractThumbnail(bitmap,  
  120.                     TARGET_SIZE_MICRO_THUMBNAIL,  
  121.                     TARGET_SIZE_MICRO_THUMBNAIL,  
  122.                     OPTIONS_RECYCLE_INPUT);  
  123.         }  
  124.         return bitmap;  
  125.     }  
  126.  
  127.     /**  
  128.      * Creates a centered bitmap of the desired size.  
  129.      *  
  130.      * @param source original bitmap source  
  131.      * @param width targeted width  
  132.      * @param height targeted height  
  133.      */ 
  134.     public static Bitmap extractThumbnail(  
  135.             Bitmap source, int width, int height) {  
  136.         return extractThumbnail(source, width, height, OPTIONS_NONE);  
  137.     }  
  138.  
  139.     /**  
  140.      * Creates a centered bitmap of the desired size.  
  141.      *  
  142.      * @param source original bitmap source  
  143.      * @param width targeted width  
  144.      * @param height targeted height  
  145.      * @param options options used during thumbnail extraction  
  146.      */ 
  147.     public static Bitmap extractThumbnail(  
  148.             Bitmap source, int width, int height, int options) {  
  149.         if (source == null) {  
  150.             return null;  
  151.         }  
  152.  
  153.         float scale;  
  154.         if (source.getWidth() < source.getHeight()) {  
  155.             scale = width / (float) source.getWidth();  
  156.         } else {  
  157.             scale = height / (float) source.getHeight();  
  158.         }  
  159.         Matrix matrix = new Matrix();  
  160.         matrix.setScale(scale, scale);  
  161.         Bitmap thumbnail = transform(matrix, source, width, height,  
  162.                 OPTIONS_SCALE_UP | options);  
  163.         return thumbnail;  
  164.     }  
  165.  
  166.     /*  
  167.      * Compute the sample size as a function of minSideLength  
  168.      * and maxNumOfPixels.  
  169.      * minSideLength is used to specify that minimal width or height of a  
  170.      * bitmap.  
  171.      * maxNumOfPixels is used to specify the maximal size in pixels that is  
  172.      * tolerable in terms of memory usage.  
  173.      *  
  174.      * The function returns a sample size based on the constraints.  
  175.      * Both size and minSideLength can be passed in as IImage.UNCONSTRAINED,  
  176.      * which indicates no care of the corresponding constraint.  
  177.      * The functions prefers returning a sample size that  
  178.      * generates a smaller bitmap, unless minSideLength = IImage.UNCONSTRAINED.  
  179.      *  
  180.      * Also, the function rounds up the sample size to a power of 2 or multiple  
  181.      * of 8 because BitmapFactory only honors sample size this way.  
  182.      * For example, BitmapFactory downsamples an image by 2 even though the  
  183.      * request is 3. So we round up the sample size to avoid OOM.  
  184.      */ 
  185.     private static int computeSampleSize(BitmapFactory.Options options,  
  186.             int minSideLength, int maxNumOfPixels) {  
  187.         int initialSize = computeInitialSampleSize(options, minSideLength,  
  188.                 maxNumOfPixels);  
  189.  
  190.         int roundedSize;  
  191.         if (initialSize <= 8 ) {  
  192.             roundedSize = 1;  
  193.             while (roundedSize < initialSize) {  
  194.                 roundedSize <<= 1;  
  195.             }  
  196.         } else {  
  197.             roundedSize = (initialSize + 7) / 8 * 8;  
  198.         }  
  199.  
  200.         return roundedSize;  
  201.     }  
  202.  
  203.     private static int computeInitialSampleSize(BitmapFactory.Options options,  
  204.             int minSideLength, int maxNumOfPixels) {  
  205.         double w = options.outWidth;  
  206.         double h = options.outHeight;  
  207.  
  208.         int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :  
  209.                 (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));  
  210.         int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :  
  211.                 (int) Math.min(Math.floor(w / minSideLength),  
  212.                 Math.floor(h / minSideLength));  
  213.  
  214.         if (upperBound < lowerBound) {  
  215.             // return the larger one when there is no overlapping zone.  
  216.             return lowerBound;  
  217.         }  
  218.  
  219.         if ((maxNumOfPixels == UNCONSTRAINED) &&  
  220.                 (minSideLength == UNCONSTRAINED)) {  
  221.             return 1;  
  222.         } else if (minSideLength == UNCONSTRAINED) {  
  223.             return lowerBound;  
  224.         } else {  
  225.             return upperBound;  
  226.         }  
  227.     }  
  228.  
  229.     /**  
  230.      * Make a bitmap from a given Uri, minimal side length, and maximum number of pixels.  
  231.      * The image data will be read from specified pfd if it's not null, otherwise  
  232.      * a new input stream will be created using specified ContentResolver.  
  233.      *  
  234.      * Clients are allowed to pass their own BitmapFactory.Options used for bitmap decoding. A  
  235.      * new BitmapFactory.Options will be created if options is null.  
  236.      */ 
  237.     private static Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,  
  238.             Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,  
  239.             BitmapFactory.Options options) {  
  240.             Bitmap b = null;  
  241.         try {  
  242.             if (pfd == null) pfd = makeInputStream(uri, cr);  
  243.             if (pfd == null) return null;  
  244.             if (options == null) options = new BitmapFactory.Options();  
  245.  
  246.             FileDescriptor fd = pfd.getFileDescriptor();  
  247.             options.inSampleSize = 1;  
  248.             options.inJustDecodeBounds = true;  
  249.             BitmapFactory.decodeFileDescriptor(fd, null, options);  
  250.             if (options.mCancel || options.outWidth == -1 
  251.                     || options.outHeight == -1) {  
  252.                 return null;  
  253.             }  
  254.             options.inSampleSize = computeSampleSize(  
  255.                     options, minSideLength, maxNumOfPixels);  
  256.             options.inJustDecodeBounds = false;  
  257.  
  258.             options.inDither = false;  
  259.             options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
  260.             b = BitmapFactory.decodeFileDescriptor(fd, null, options);  
  261.         } catch (OutOfMemoryError ex) {  
  262.             Log.e(TAG, "Got oom exception ", ex);  
  263.             return null;  
  264.         } finally {  
  265.             closeSilently(pfd);  
  266.         }  
  267.         return b;  
  268.     }  
  269.  
  270.     private static void closeSilently(ParcelFileDescriptor c) {  
  271.       if (c == null) return;  
  272.       try {  
  273.           c.close();  
  274.       } catch (Throwable t) {  
  275.           // do nothing  
  276.       }  
  277.     }  
  278.  
  279.     private static ParcelFileDescriptor makeInputStream(  
  280.             Uri uri, ContentResolver cr) {  
  281.         try {  
  282.             return cr.openFileDescriptor(uri, "r");  
  283.         } catch (IOException ex) {  
  284.             return null;  
  285.         }  
  286.     }  
  287.  
  288.     /**  
  289.      * Transform source Bitmap to targeted width and height.  
  290.      */ 
  291.     private static Bitmap transform(Matrix scaler,  
  292.             Bitmap source,  
  293.             int targetWidth,  
  294.             int targetHeight,  
  295.             int options) {  
  296.         boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0;  
  297.         boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;  
  298.  
  299.         int deltaX = source.getWidth() - targetWidth;  
  300.         int deltaY = source.getHeight() - targetHeight;  
  301.         if (!scaleUp && (deltaX < 0 || deltaY < 0)) {  
  302.             /*  
  303.             * In this case the bitmap is smaller, at least in one dimension,  
  304.             * than the target.  Transform it by placing as much of the image  
  305.             * as possible into the target and leaving the top/bottom or  
  306.             * left/right (or both) black.  
  307.             */ 
  308.             Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,  
  309.             Bitmap.Config.ARGB_8888);  
  310.             Canvas c = new Canvas(b2);  
  311.  
  312.             int deltaXHalf = Math.max(0, deltaX / 2);  
  313.             int deltaYHalf = Math.max(0, deltaY / 2);  
  314.             Rect src = new Rect(  
  315.             deltaXHalf,  
  316.             deltaYHalf,  
  317.             deltaXHalf + Math.min(targetWidth, source.getWidth()),  
  318.             deltaYHalf + Math.min(targetHeight, source.getHeight()));  
  319.             int dstX = (targetWidth  - src.width())  / 2;  
  320.             int dstY = (targetHeight - src.height()) / 2;  
  321.             Rect dst = new Rect(  
  322.                     dstX,  
  323.                     dstY,  
  324.                     targetWidth - dstX,  
  325.                     targetHeight - dstY);  
  326.             c.drawBitmap(source, src, dst, null);  
  327.             if (recycle) {  
  328.                 source.recycle();  
  329.             }  
  330.             return b2;  
  331.         }  
  332.         float bitmapWidthF = source.getWidth();  
  333.         float bitmapHeightF = source.getHeight();  
  334.  
  335.         float bitmapAspect = bitmapWidthF / bitmapHeightF;  
  336.         float viewAspect   = (float) targetWidth / targetHeight;  
  337.  
  338.         if (bitmapAspect > viewAspect) {  
  339.             float scale = targetHeight / bitmapHeightF;  
  340.             if (scale < .9F || scale > 1F) {  
  341.                 scaler.setScale(scale, scale);  
  342.             } else {  
  343.                 scaler = null;  
  344.             }  
  345.         } else {  
  346.             float scale = targetWidth / bitmapWidthF;  
  347.             if (scale < .9F || scale > 1F) {  
  348.                 scaler.setScale(scale, scale);  
  349.             } else {  
  350.                 scaler = null;  
  351.             }  
  352.         }  
  353.  
  354.         Bitmap b1;  
  355.         if (scaler != null) {  
  356.             // this is used for minithumb and crop, so we want to filter here.  
  357.             b1 = Bitmap.createBitmap(source, 0, 0,  
  358.             source.getWidth(), source.getHeight(), scaler, true);  
  359.         } else {  
  360.             b1 = source;  
  361.         }  
  362.  
  363.         if (recycle && b1 != source) {  
  364.             source.recycle();  
  365.         }  
  366.  
  367.         int dx1 = Math.max(0, b1.getWidth() - targetWidth);  
  368.         int dy1 = Math.max(0, b1.getHeight() - targetHeight);  
  369.  
  370.         Bitmap b2 = Bitmap.createBitmap(  
  371.                 b1,  
  372.                 dx1 / 2,  
  373.                 dy1 / 2,  
  374.                 targetWidth,  
  375.                 targetHeight);  
  376.  
  377.         if (b2 != b1) {  
  378.             if (recycle || b1 != source) {  
  379.                 b1.recycle();  
  380.             }  
  381.         }  
  382.  
  383.         return b2;  
  384.     }  
  385.  
  386.     /**  
  387.      * SizedThumbnailBitmap contains the bitmap, which is downsampled either from  
  388.      * the thumbnail in exif or the full image.  
  389.      * mThumbnailData, mThumbnailWidth and mThumbnailHeight are set together only if mThumbnail  
  390.      * is not null.  
  391.      *  
  392.      * The width/height of the sized bitmap may be different from mThumbnailWidth/mThumbnailHeight.  
  393.      */ 
  394.     private static class SizedThumbnailBitmap {  
  395.         public byte[] mThumbnailData;  
  396.         public Bitmap mBitmap;  
  397.         public int mThumbnailWidth;  
  398.         public int mThumbnailHeight;  
  399.     }  
  400.  
  401.     /**  
  402.      * Creates a bitmap by either downsampling from the thumbnail in EXIF or the full image.  
  403.      * The functions returns a SizedThumbnailBitmap,  
  404.      * which contains a downsampled bitmap and the thumbnail data in EXIF if exists.  
  405.      */ 
  406.     private static void createThumbnailFromEXIF(String filePath, int targetSize,  
  407.             int maxPixels, SizedThumbnailBitmap sizedThumbBitmap) {  
  408.         if (filePath == null) return;  
  409.  
  410.         ExifInterface exif = null;  
  411.         byte [] thumbData = null;  
  412.         try {  
  413.             exif = new ExifInterface(filePath);  
  414.             if (exif != null) {  
  415.                 thumbData = exif.getThumbnail();  
  416.             }  
  417.         } catch (IOException ex) {  
  418.             Log.w(TAG, ex);  
  419.         }  
  420.  
  421.         BitmapFactory.Options fullOptions = new BitmapFactory.Options();  
  422.         BitmapFactory.Options exifOptions = new BitmapFactory.Options();  
  423.         int exifThumbWidth = 0;  
  424.         int fullThumbWidth = 0;  
  425.  
  426.         // Compute exifThumbWidth.  
  427.         if (thumbData != null) {  
  428.             exifOptions.inJustDecodeBounds = true;  
  429.             BitmapFactory.decodeByteArray(thumbData, 0, thumbData.length, exifOptions);  
  430.             exifOptions.inSampleSize = computeSampleSize(exifOptions, targetSize, maxPixels);  
  431.             exifThumbWidth = exifOptions.outWidth / exifOptions.inSampleSize;  
  432.         }  
  433.  
  434.         // Compute fullThumbWidth.  
  435.         fullOptions.inJustDecodeBounds = true;  
  436.         BitmapFactory.decodeFile(filePath, fullOptions);  
  437.         fullOptions.inSampleSize = computeSampleSize(fullOptions, targetSize, maxPixels);  
  438.         fullThumbWidth = fullOptions.outWidth / fullOptions.inSampleSize;  
  439.  
  440.         // Choose the larger thumbnail as the returning sizedThumbBitmap.  
  441.         if (thumbData != null && exifThumbWidth >= fullThumbWidth) {  
  442.             int width = exifOptions.outWidth;  
  443.             int height = exifOptions.outHeight;  
  444.             exifOptions.inJustDecodeBounds = false;  
  445.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeByteArray(thumbData, 0,  
  446.                     thumbData.length, exifOptions);  
  447.             if (sizedThumbBitmap.mBitmap != null) {  
  448.                 sizedThumbBitmap.mThumbnailData = thumbData;  
  449.                 sizedThumbBitmap.mThumbnailWidth = width;  
  450.                 sizedThumbBitmap.mThumbnailHeight = height;  
  451.             }  
  452.         } else {  
  453.             fullOptions.inJustDecodeBounds = false;  
  454.             sizedThumbBitmap.mBitmap = BitmapFactory.decodeFile(filePath, fullOptions);  
  455.         }  
  456.     }  
  457. }  
  458.  

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved