Las miniaturas de medios brindan a los usuarios una vista previa rápida de imágenes y videos, lo que permite una navegación más rápida y hace que la interfaz de la aplicación sea más visual. atractivos y atractivos. Debido a que las miniaturas son más pequeñas que el contenido multimedia de tamaño completo, ayudan a ahorrar memoria, espacio de almacenamiento y ancho de banda, a la vez que mejoran el rendimiento el rendimiento de navegación.
Según el tipo de archivo y el acceso a archivos que tengas en tu aplicación, tus recursos multimedia, puedes crear miniaturas de diversas formas.
Crea una miniatura con una biblioteca de carga de imágenes
Las bibliotecas de carga de imágenes hacen gran parte del trabajo pesado por ti: pueden controlar la caché junto con la lógica para recuperar el contenido multimedia de origen del recurso local o de red según un Uri. En el siguiente código, se muestra el uso de la biblioteca de carga de imágenes Coil, que funciona para imágenes y videos, y en un recurso local o de red.
// 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
)
Si es posible, crea miniaturas del servidor. Consulta Cómo cargar imágenes. para obtener detalles sobre cómo cargar imágenes con Compose y Cómo cargar mapas de bits grandes de forma eficiente para obtener orientación sobre cómo trabajar con imágenes grandes.
Crea una miniatura a partir de un archivo de imagen local
Para obtener las imágenes de las miniaturas, se reduce la escala de forma eficiente y se conservan calidad, evitar el uso excesivo de memoria, lidiar con una variedad de imágenes formatos y hacer un uso correcto de los datos Exif.
El método createImageThumbnail hace todo esto, siempre que tengas acceso a la ruta de acceso del archivo de imagen.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Si solo tienes el Uri, puedes usar el método loadThumbnail en
ContentResolver a partir de Android 10 (nivel de API 29).
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
ImageDecoder, disponible a partir de Android 9, nivel de API 28, tiene algunas opciones sólidas para volver a muestrear la imagen a medida que la decodificas para evitar memoria adicional usar.
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);
Puedes usar BitmapFactory para crear miniaturas de apps orientadas a versiones anteriores de Android. BitmapFactory.Options tiene un parámetro de configuración para decodificar solo los límites de una imagen con el fin de volver a muestrear.
Primero, decodifica solo los límites del mapa de bits en 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()
Usa width y height de BitmapFactory.Options para establecer el tamaño de la muestra:
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 la transmisión. El tamaño de la imagen resultante se muestrea por potencias de dos según inSampleSize.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Crea una miniatura a partir de un archivo de video local
Obtener imágenes de miniaturas de video implica muchos de los mismos desafíos que obtener miniaturas de imágenes, pero los tamaños de archivo pueden ser mucho más grandes y obtener un fotograma de video representativo no siempre es tan sencillo como elegir el primer fotograma del video.
El método createVideoThumbnail es una opción sólida si tienes acceso a la ruta de acceso del archivo de video.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Si solo tienes acceso a un URI de contenido, puedes usar
MediaMetadataRetriever
Primero, verifica si el video tiene una miniatura incorporada y, si es posible, úsala:
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));
}
Recupera el ancho y la altura del video de MediaMetadataRetriever para calcular el factor de escalamiento:
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)
En Android 9 y versiones posteriores (nivel de API 28), MediaMetadataRetriever puede mostrar un ajuste
marco:
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
}
}
De lo contrario, muestra el primer fotograma sin escalar:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}