Nota: esistono diverse librerie che seguono best practice per il caricamento delle immagini. Puoi usare queste librerie nella tua app per per caricare le immagini nel modo più ottimizzato. Consigliamo Scorrimento che carica e visualizza le immagini nel modo più rapido e semplice possibile. Altre librerie di caricamento delle immagini popolari includono Picasso di Square, Coil di Instacart e Affresco da Facebook. Queste librerie semplificano la maggior parte delle attività complesse associate con bitmap e altri tipi di immagini su Android.
Le immagini sono disponibili in tutte le forme e dimensioni. In molti casi sono più grandi di quanto richiesto per una l'interfaccia utente di un'applicazione. Ad esempio, l'applicazione Galleria di sistema mostra le foto scattate usando le fotocamere dei tuoi dispositivi Android, che in genere hanno una risoluzione molto più alta rispetto allo schermo densità del dispositivo.
Dato che stai lavorando con memoria limitata, idealmente dovresti caricare solo una risoluzione più bassa in memoria. La versione con risoluzione più bassa deve corrispondere alle dimensioni del componente dell'interfaccia utente che la visualizza. Un'immagine con una risoluzione più alta non offre alcun vantaggio visibile, ma richiede comunque di memoria preziosa e comporta un ulteriore overhead delle prestazioni dovuto a e la scalabilità delle applicazioni.
Questa lezione illustra come decodificare bitmap di grandi dimensioni senza superare il limite per applicazione limite di memoria caricando una versione sottocampionata più piccola in memoria.
Leggi dimensioni e tipo di bitmap
La classe BitmapFactory
fornisce diversi metodi di decodifica (decodeByteArray()
, decodeFile()
, decodeResource()
e così via) per creare un Bitmap
da varie fonti. Scegli
il metodo di decodifica più appropriato in base all'origine dati immagine. Questi metodi tentano di
allocare memoria per la bitmap creata e quindi può facilmente generare un OutOfMemory
. Ogni tipo di metodo di decodifica ha firme aggiuntive che ti consentono di specificare la decodifica
opzioni tramite il corso BitmapFactory.Options
. Impostazione della proprietà inJustDecodeBounds
su true
durante la decodifica
evita l'allocazione della memoria, restituendo null
per l'oggetto bitmap ma impostando outWidth
, outHeight
e outMimeType
. Questa tecnica consente di leggere
e il tipo di dati dell'immagine prima della costruzione (e dell'allocazione della memoria)
bitmap.
Kotlin
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } BitmapFactory.decodeResource(resources, R.id.myimage, options) val imageHeight: Int = options.outHeight val imageWidth: Int = options.outWidth val imageType: String = options.outMimeType
Java
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
Per evitare java.lang.OutOfMemory
eccezioni, controlla le dimensioni di una bitmap prima
decodificarlo, a meno che tu non ti fidi completamente della fonte per fornire dati delle immagini di dimensioni prevedibili
che si inserisce comodamente nella memoria disponibile.
Carica una versione scalata in memoria
Ora che le dimensioni dell'immagine sono note, possono essere utilizzate per decidere se l'immagine intera deve in memoria o se è necessario caricare una versione sottocampionata. Di seguito sono riportati alcuni fattori da considerare considera:
- Utilizzo stimato della memoria per il caricamento dell'intera immagine in memoria.
- Quantità di memoria che intendi impegnare a caricare questa immagine in base a qualsiasi altra memoria i requisiti della tua applicazione.
- Dimensioni del componente
ImageView
o UI target a cui l'immagine in cui eseguire il caricamento. - Dimensioni e densità dello schermo del dispositivo corrente.
Ad esempio, non vale la pena caricare in memoria un'immagine di 1024 x 768 pixel se alla fine
visualizzato in una miniatura di 128 x 96 pixel in un ImageView
.
Per indicare al decoder di sottocampionare l'immagine, caricando una versione più piccola in memoria, imposta inSampleSize
su true
nell'oggetto BitmapFactory.Options
. Ad esempio, un'immagine con risoluzione 2048 x 1536 che
viene decodificato con un inSampleSize
di 4, produce una
bitmap di circa 512 x 384. Il caricamento in memoria richiede 0,75 MB anziché 12 MB per
(supponendo una configurazione bitmap di ARGB_8888
). Ecco
un metodo per calcolare un valore di dimensione del campione che abbia una potenza di 2 in base alla larghezza e al valore
altezza:
Kotlin
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { // Raw height and width of image val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight: Int = height / 2 val halfWidth: Int = width / 2 // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { inSampleSize *= 2 } } return inSampleSize }
Java
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
Nota: viene calcolata una potenza di due valori perché il decoder utilizza
un valore finale arrotondando per difetto al valore più vicino al due, come indicato nella documentazione inSampleSize
.
Per utilizzare questo metodo, prima decodifica con inJustDecodeBounds
impostato su true
, passa le opzioni
quindi la decodificano nuovamente utilizzando il nuovo valore inSampleSize
e inJustDecodeBounds
impostato su false
:
Kotlin
fun decodeSampledBitmapFromResource( res: Resources, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeResource(res, resId, this) // Calculate inSampleSize inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight) // Decode bitmap with inSampleSize set inJustDecodeBounds = false BitmapFactory.decodeResource(res, resId, this) } }
Java
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
Questo metodo semplifica il caricamento di una bitmap di dimensioni arbitrariamente grandi in un ImageView
che visualizza una miniatura di 100 x 100 pixel, come mostrato nell'esempio seguente
codice:
Kotlin
imageView.setImageBitmap( decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100) )
Java
imageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Puoi seguire una procedura simile per decodificare le bitmap da altre origini, sostituendo il metodo
metodo BitmapFactory.decode*
appropriato in base alle esigenze.