Le miniature dei contenuti multimediali offrono agli utenti una rapida anteprima visiva di immagini e video, consentendo una navigazione più veloce e rendendo più visivamente l'interfaccia dell'app accattivanti e coinvolgenti. Poiché le miniature sono più piccole rispetto ai contenuti multimediali a grandezza originale, consentono di risparmiare memoria, spazio di archiviazione e larghezza di banda, migliorando al contempo le prestazioni di navigazione.
A seconda del tipo di file e dell'accesso al file di cui disponi nell'applicazione e i tuoi asset multimediali, puoi creare miniature in vari modi.
Creare una miniatura utilizzando una libreria di caricamento delle immagini
Le librerie per il caricamento delle immagini svolgono gran parte del lavoro: possono gestire la memorizzazione nella cache e la logica per recuperare i contenuti multimediali di origine dall'ambiente locale una risorsa basata su un URI. Il seguente codice illustra l'uso dell'API La libreria di caricamento delle immagini Coil funziona sia per le immagini che per i video, e funziona su una risorsa locale o di rete.
// Use Coil to create and display a thumbnail of a video or image with a specific height
// ImageLoader has its own memory and storage cache, and this one is configured to also
// load frames from videos
val videoEnabledLoader = ImageLoader.Builder(context)
.components {
add(VideoFrameDecoder.Factory())
}.build()
// Coil requests images that match the size of the AsyncImage composable, but this allows
// for precise control of the height
val request = ImageRequest.Builder(context)
.data(mediaUri)
.size(Int.MAX_VALUE, THUMBNAIL_HEIGHT)
.build()
AsyncImage(
model = request,
imageLoader = videoEnabledLoader,
modifier = Modifier
.clip(RoundedCornerShape(20)) ,
contentDescription = null
)
Se possibile, crea le miniature lato server. Consulta Caricare immagini per informazioni dettagliate su come caricare le immagini utilizzando Compose e Caricare bitmap di grandi dimensioni in modo efficiente per indicazioni su come utilizzare le immagini di grandi dimensioni.
Creare una miniatura da un file immagine locale
Ottenere immagini in miniatura implica uno scale down efficiente, senza compromettere l'immagine la qualità dell'immagine, evitando un uso eccessivo di memoria e la gestione di una varietà di formati e un corretto utilizzo dei dati Exif.
Il metodo createImageThumbnail esegue tutte queste operazioni, fornendo
l'accesso al percorso del file immagine.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Se hai solo Uri, puoi utilizzare il metodo loadThumbnail in
ContentResolver a partire da Android 10, livello API 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
ImageDecoder, disponibile a partire da Android 9, livello API 28, include alcune opzioni continue per ricampionare l'immagine mentre la decodifica per evitare memoria aggiuntiva per gli utilizzi odierni.
class DecodeResampler(val size: Size, val signal: CancellationSignal?) : OnHeaderDecodedListener {
private val size: Size
override fun onHeaderDecoded(decoder: ImageDecoder, info: ImageInfo, source:
// sample down if needed.
val widthSample = info.size.width / size.width
val heightSample = info.size.height / size.height
val sample = min(widthSample, heightSample)
if (sample > 1) {
decoder.setTargetSampleSize(sample)
}
}
}
val resampler = DecoderResampler(size, null)
val source = ImageDecoder.createSource(context.contentResolver, imageUri)
val bitmap = ImageDecoder.decodeBitmap(source, resampler);
Puoi utilizzare BitmapFactory per creare miniature per le app che hanno come target release Android precedenti. Bitmapfabbrica.Options ha un'impostazione per decodificare solo limiti di un'immagine ai fini del ricampionamento.
Innanzitutto, decodifica solo i limiti della bitmap in BitmapFactory.Options:
private fun decodeResizedBitmap(context: Context, uri: Uri, size: Size): Bitmap?{
val boundsStream = context.contentResolver.openInputStream(uri)
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(boundsStream, null, options)
boundsStream?.close()
Utilizza width e height da BitmapFactory.Options per impostare le dimensioni del campione:
if ( options.outHeight != 0 ) {
// we've got bounds
val widthSample = options.outWidth / size.width
val heightSample = options.outHeight / size.height
val sample = min(widthSample, heightSample)
if (sample > 1) {
options.inSampleSize = sample
}
}
Decodifica lo stream. Le dimensioni dell'immagine risultante vengono campionate per potenze di due
in base al inSampleSize.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Creare una miniatura da un file video locale
Ottenere le miniature dei video comporta molte delle stesse difficoltà che si incontrano per le miniature delle immagini, ma le dimensioni dei file possono essere molto più grandi e ottenere un fotogramma rappresentativo del video non è sempre così semplice come scegliere il primo fotogramma del video.
Il metodo createVideoThumbnail è una scelta solida se hai accesso al percorso del file video.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Se hai accesso solo a un'URI dei contenuti, puoi utilizzare
MediaMetadataRetriever
Innanzitutto, controlla se il video ha una miniatura incorporata e, se possibile, utilizzala:
private suspend fun getVideoThumbnailFromMediaMetadataRetriever(context: Context, uri: Uri, size: Size): Bitmap? {
val mediaMetadataRetriever = MediaMetadataRetriever()
mediaMetadataRetriever.setDataSource(context, uri)
val thumbnailBytes = mediaMetadataRetriever.embeddedPicture
val resizer = Resizer(size, null)
ImageDecoder.createSource(context.contentResolver, uri)
// use a built-in thumbnail if the media file has it
thumbnailBytes?.let {
return ImageDecoder.decodeBitmap(ImageDecoder.createSource(it));
}
Visualizza la larghezza e l'altezza del video da MediaMetadataRetriever a
Calcola il fattore di scala:
val width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
?.toFloat() ?: size.width.toFloat()
val height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
?.toFloat() ?: size.height.toFloat()
val widthRatio = size.width.toFloat() / width
val heightRatio = size.height.toFloat() / height
val ratio = max(widthRatio, heightRatio)
Su Android 9 e versioni successive (livello API 28), MediaMetadataRetriever può restituire un frame scalato:
if (ratio > 1) {
val requestedWidth = width * ratio
val requestedHeight = height * ratio
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val frame = mediaMetadataRetriever.getScaledFrameAtTime(
-1, OPTION_PREVIOUS_SYNC,
requestedWidth.toInt(), requestedHeight.toInt())
mediaMetadataRetriever.close()
return frame
}
}
In caso contrario, restituisce il primo frame non scalato:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}