• ThumbnailUtils Android2.2新增类


    从Android 2.2开始系统新增了一个缩略图ThumbnailUtils类,位于framework的android.media.ThumbnailUtils位置,可以帮助我们从mediaprovider中获取系统中的视频或图片文件的缩略图,该类提供了三种静态方法可以直接调用获取。

       static Bitmap  createVideoThumbnail(String filePath, int kind)  //获取视频文件的缩略图,第一个参数为视频文件的位置,比如/sdcard/android123.3gp,而第二个参数可以为MINI_KIND或 MICRO_KIND最终和分辨率有关 
       static Bitmap  extractThumbnail(Bitmap source, int width, int height, int options)  //直接对Bitmap进行缩略操作,最后一个参数定义为OPTIONS_RECYCLE_INPUT ,来回收资源
       static Bitmap  extractThumbnail(Bitmap source, int width, int height) // 这个和上面的方法一样,无options选项

      最后Android开发网再次提醒大家,ThumbnailUtils类是API Level从8或更高才开始支持的。

    Android缩略图类源代码

    Android 2.2开始新增的缩略图类ThumbnailUtils的主要方法是静态的,对于Android 2.2或API Level8以下的工程可以直接使用,本类相对于我们常规的缩略图类考虑更周全,除了尺寸比例优化外,针对OOM的内存管理方面有更周全的处理方式,完整代码如下:

       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:       * {@link #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:  }
  • 相关阅读:
    栈的压入、弹出序列
    CSS min-height 属性
    ie6 png 透明的解决方法,大网站都在用
    div覆盖div DIV相互重叠如何解决
    div代码大全 DIV代码使用说明
    CSS中ul li居中的问题
    复选框单选框与文字对齐问题的研究与解决
    CSS利用filter/opacity实现背景透明
    什么是块级元素和内联级元素(二)
    网页上PNG透明图片的运用(ie6+滤镜)
  • 原文地址:https://www.cnblogs.com/GnagWang/p/1917658.html
Copyright © 2020-2023  润新知