2010-04-05 26 views
59

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.Android cómo crear una miniatura en tiempo de ejecución

Respuesta

42

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) { 

     } 
+1

Mis archivos eran demasiado grandes, así que tuve que usar submuestreo en el paso 'BitmapFactory.decodeStream (fis);'. Consulte [docs] (http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize) para obtener más detalles sobre el submuestreo. – Diederik

+4

Es mejor utilizar la clase 'ThumbnailUils' de Android como la respuesta a continuación. – afollestad

+1

@afollestad En realidad, este enfoque no es el correcto. Usar ThumbnailUtils es solo una buena idea cuando estás 100% seguro de que estás trabajando con archivos pequeños. – GuillermoMP

6

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

Por ImageView establecer las dimensiones del diseño a algo pequeño, por ejemplo:

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

Añadir un onClickListener a la ImageView y poner en marcha una nueva actividad, en la que aparece la imagen en tamaño completo con

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

o especifique un tamaño mayor.

+11

Cuando tenga varias imágenes, debería considerar reducirlas al tamaño del pulgar de antemano. de lo contrario, podría ralentizar el rendimiento al mover el conjunto. – Moritz

+5

De hecho, y para hacer eso usaría Bitmap.createScaledBitmap (originalBitmap, newWidth, newHeight, false); –

+1

dones ¿El siguiente método también reduce el peso de la imagen? Bitmap.createScaledBitmap (originalBitmap, newWidth, newHeight, false) –

9

Aquí hay una solución más completa para reducir un mapa de bits 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 mapa de bits resultante como un blob en su base de datos Sqlite. He incluido un fragmento sobre cómo convertir el mapa de bits 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(); 
+0

¿Hay alguna razón por la que haya cambiado el orden de los argumentos para 'THUMBNAIL_HEIGHT' y' THUMBNAIL_HEIGHT' para 'createScaledBitmap'? – jayeffkay

123

Prueba este

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

Esta utilidad está disponible en API_LEVEl 8. [Source]

+1

Al utilizar este código, en realidad está cargando en la memoria una copia del mapa de bits grande, por lo que esta no es una buena manera de administrar imágenes grandes. – GuillermoMP

+0

@GuillermoMP ¿Cuál es el buen camino, entonces? – Rafouille

+5

La forma correcta de hacerlo es decodificar una versión del archivo que no está bien muestreada. Otras respuestas ofrecen este enfoque. Además, los documentos oficiales explican bien el proceso: https://developer.android.com/intl/es/training/displaying-bitmaps/load-bitmap.html. Dado que esta es la respuesta más votada (debido a su simplicidad), solo quería advertir a todos que esta no es la mejor manera. – GuillermoMP

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

donde está la transformación (matriz, fuente, ancho, alto, OPTIONS_SCALE_UP | opciones) Método – Andy

+0

https://code.google.com/p/kontalk/source/browse/src/org/kontalk/xmpp/util/ThumbnailUtils. java? repo = androidclient & name = xmpp & r = f879e85d21d6e0a8a6f736ebe90c03e851b51436 – user1546570

+0

Gracias querida. – Andy

10

La mejor solución que he encontrado 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 ancho y alto 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(); 
+2

Esto es mejor para dispositivos de memoria baja – Lunatikul

+0

Gracias. Ha resuelto el problema con la excepción de falta de memoria. –

0

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

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

No se olvide de utilizar decodificador incorporado si desea reducir proporcionalmente inferior al 50% y un resultado HQ.

0

He encontrado 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

uso Picasso dependencia

compilación 'com.squareup.picasso: Picasso: 2.5.2'

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

Referencia Picasso

0

Esta respuesta se basa en la solución presentada en https://developer.android.com/topic/performance/graphics/load-bitmap.html (sin el uso de bibliotecas externas) con un poco de cambios por mi parte para hacer que su funcionalidad sea mejor y más práctica.

Algunas notas acerca de 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 fundición y cuestiones de redondeo)

  2. Se supone que usted tiene dos valores (maxWidth & maxHeight) que desea cualquiera de las dimensiones de su mapa de bits final no excede su valor correspondiente. En otras palabras:

    finalWidth <= maxWidth && finalHeight <= maxHeight

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

  3. Se supone que desea (al menos) una de las dimensiones finales tiene exactamente el valor de su correspondiente maxValue (era posible cada uno de ellos, teniendo en cuenta los supuestos anteriores). En otras palabras:

    finalWidth == maxWidth || finalHeight == maxHeight

    El paso final adicional en comparación con la solución básica (Bitmap.createScaledBitmap(...)) es para esta restricción "exactamente".La nota más importante es . No debe dar este paso al principio (como the accepted answer), debido a su consumo significativo de memoria en el caso de imágenes de gran tamaño.

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

La aplicació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); 
} 

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

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