Android cómo crear miniatura en tiempo de ejecución

Tengo una imagen de gran tamaño. En tiempo de ejecución, quiero leer la imagen del almacenamiento y escalarla para que su peso y tamaño se reduzcan y pueda usarlo como una miniatura. Cuando un usuario hace clic en la miniatura, quiero mostrar la imagen de tamaño completo.

Mi solución

byte[] imageData = null; try { final int THUMBNAIL_SIZE = 64; FileInputStream fis = new FileInputStream(fileName); Bitmap imageBitmap = BitmapFactory.decodeStream(fis); imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false); ByteArrayOutputStream baos = new ByteArrayOutputStream(); imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos); imageData = baos.toByteArray(); } catch(Exception ex) { } 

Prueba esto

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

Esta utilidad está disponible desde API_LEVEl 8. [Fuente]

La mejor solución que encontré es la siguiente. Comparado con las otras soluciones, este no necesita cargar la imagen completa para crear una miniatura, ¡así que es más eficiente! Su límite es que no puede tener una miniatura con el ancho y la altura exactos, pero la solución lo más cerca posible.

 File file = ...; // the image file Options bitmapOptions = new Options(); bitmapOptions.inJustDecodeBounds = true; // obtain the size of the image, without loading it in memory BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions); // find the best scaling factor for the desired dimensions int desiredWidth = 400; int desiredHeight = 300; float widthScale = (float)bitmapOptions.outWidth/desiredWidth; float heightScale = (float)bitmapOptions.outHeight/desiredHeight; float scale = Math.min(widthScale, heightScale); int sampleSize = 1; while (sampleSize < scale) { sampleSize *= 2; } bitmapOptions.inSampleSize = sampleSize; // this value must be a power of 2, // this is why you can not have an image scaled as you would like bitmapOptions.inJustDecodeBounds = false; // now we want to load the image // Let's load just the part of the image necessary for creating the thumbnail, not the whole image Bitmap thumbnail = BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions); // Save the thumbnail File thumbnailFile = ...; FileOutputStream fos = new FileOutputStream(thumbnailFile); thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, fos); fos.flush(); fos.close(); // Use the thumbail on an ImageView or recycle it! thumbnail.recycle(); 

Aquí hay una solución más completa para reducir un bitmap a tamaño de miniatura. Se expande en la solución Bitmap.createScaledBitmap manteniendo la relación de aspecto de las imágenes y acolchándolas al mismo ancho para que se vean bien en un ListView.

Además, sería mejor hacer este escalado una vez y almacenar el bitmap resultante como un blob en su base de datos Sqlite. He incluido un fragmento sobre cómo convertir el bitmap a una matriz de bytes para este propósito.

 public static final int THUMBNAIL_HEIGHT = 48; public static final int THUMBNAIL_WIDTH = 66; imageBitmap = BitmapFactory.decodeByteArray(mImageData, 0, mImageData.length); Float width = new Float(imageBitmap.getWidth()); Float height = new Float(imageBitmap.getHeight()); Float ratio = width/height; imageBitmap = Bitmap.createScaledBitmap(imageBitmap, (int)(THUMBNAIL_HEIGHT*ratio), THUMBNAIL_HEIGHT, false); int padding = (THUMBNAIL_WIDTH - imageBitmap.getWidth())/2; imageView.setPadding(padding, 0, padding, 0); imageView.setImageBitmap(imageBitmap); ByteArrayOutputStream baos = new ByteArrayOutputStream(); imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] byteArray = baos.toByteArray(); 

Utilice BitmapFactory.decodeFile(...) para obtener su objeto Bitmap y BitmapFactory.decodeFile(...) en un ImageView con ImageView.setImageBitmap() .

En ImageView establezca las dimensiones del diseño en algo pequeño, por ejemplo:

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

Agregue un onClickListener a ImageView e inicie una nueva actividad, donde visualiza la imagen en tamaño completo con

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

o especifica un tamaño más grande.

 /** * 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; } 

Si desea un resultado de alta calidad, utilice la biblioteca [RapidDecoder] [1]. Es simple como sigue:

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

No te olvides de usar el decodificador incorporado si deseas reducir la escala a menos del 50% y un resultado HQ.

Encontré una manera fácil de hacer esto

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

Sintaxis

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

O

usar la dependencia de Picasso

comstackr ‘com.squareup.picasso: picasso: 2.5.2’

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

Referencia Picasso

Esta respuesta se basa en la solución presentada en https://developer.android.com/topic/performance/graphics/load-bitmap.html (sin usar bibliotecas externas) con algunos cambios por mi parte para mejorar su funcionalidad y hacerla más práctica. .

Algunas notas sobre esta solución:

  1. Se supone que desea mantener la relación de aspecto . En otras palabras:

    finalWidth / finalHeight == sourceBitmap.getWidth() / sourceBitmap.getWidth() (Independientemente de los problemas de conversión y redondeo)

  2. Se supone que tiene dos valores ( maxWidth y maxHeight ) que desea que cualquiera de las dimensiones de su bitmap final no exceda su valor correspondiente . En otras palabras:

    finalWidth <= maxWidth && finalHeight <= maxHeight

    Así que minRatio se ha colocado como la base de los cálculos (ver la implementación). A MENOS la solución básica que ha colocado a maxRatio como la base de los cálculos en tiempo real . Además, el cálculo de inSampleSize ha sido mucho mejor (más lógica, breve y eficiente).

  3. Se supone que desea (al menos) que una de las dimensiones finales tenga exactamente el valor de su valor máximo correspondiente (cada una fue posible, considerando los supuestos anteriores) . En otras palabras:

    finalWidth == maxWidth || finalHeight == maxHeight

    El último paso adicional en comparación con la solución básica ( Bitmap.createScaledBitmap(...) ) es para esta restricción " exactamente ". La nota muy importante es que no debe dar este paso al principio (como la respuesta aceptada ), ¡debido a su consumo significativo de memoria en el caso de imágenes enormes!

  4. Es para decodificar un file . Puede cambiarlo como la solución básica para decodificar un resource (o todo lo que admite BitmapFactory ).

La implementación:

 public static Bitmap decodeSampledBitmap(String pathName, int maxWidth, int maxHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, options); final float wRatio_inv = (float) options.outWidth / maxWidth, hRatio_inv = (float) options.outHeight / maxHeight; // Working with inverse ratios is more comfortable final int finalW, finalH, minRatio_inv /* = max{Ratio_inv} */; if (wRatio_inv > hRatio_inv) { minRatio_inv = (int) wRatio_inv; finalW = maxWidth; finalH = Math.round(options.outHeight / wRatio_inv); } else { minRatio_inv = (int) hRatio_inv; finalH = maxHeight; finalW = Math.round(options.outWidth / hRatio_inv); } options.inSampleSize = pow2Ceil(minRatio_inv); // pow2Ceil: A utility function that comes later options.inJustDecodeBounds = false; // Decode bitmap with inSampleSize set return Bitmap.createScaledBitmap(BitmapFactory.decodeFile(pathName, options), finalW, finalH, true); } /** * @return the largest power of 2 that is smaller than or equal to number. * WARNING: return {0b1000000...000} for ZERO input. */ public static int pow2Ceil(int number) { return 1 << -(Integer.numberOfLeadingZeros(number) + 1); // is equivalent to: // return Integer.rotateRight(1, Integer.numberOfLeadingZeros(number) + 1); } 

Sample Usage, en caso de que tengas una imageView con un valor determinado para layout_width ( match_parent o un valor explícito) y un valor indeterminado para layout_height ( wrap_content ) y en su lugar un valor determinado para maxHeight :

 imageView.setImageBitmap(decodeSampledBitmap(filePath, imageView.getWidth(), imageView.getMaxHeight()));