As miniaturas de mídia fornecem aos usuários uma prévia visual rápida de imagens e vídeos, permitindo uma navegação mais rápida e tornando a interface do aplicativo mais visualmente atraente e envolvente. Como as miniaturas são menores do que as mídias de tamanho normal, elas ajudam a economizar memória, espaço de armazenamento e largura de banda, além de melhorar a performance da navegação de mídia.
Dependendo do tipo de arquivo e do acesso a arquivos que você tem em seu aplicativo e seus recursos de mídia, é possível criar miniaturas de várias formas.
Criar uma miniatura usando uma biblioteca de carregamento de imagens
As bibliotecas de carregamento de imagens fazem grande parte do trabalho pesado para você. podem lidar com armazenamento em cache, além da lógica para buscar a mídia de origem do servidor com base em um Uri. O código a seguir demonstra o uso da A biblioteca de carregamento de imagem Coil funciona para imagens e vídeos, e funciona em um recurso local ou de rede.
// 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 possível, crie miniaturas do lado do servidor. Consulte Como carregar imagens. para detalhes sobre como carregar imagens usando o Compose e Como carregar bitmaps grandes eficiente para saber como trabalhar com imagens grandes.
Criar uma miniatura de um arquivo de imagem local
A obtenção de imagens em miniatura envolve uma redução eficiente da escala, preservando a imagem qualidade, evitando o uso excessivo de memória, lidando com diversas imagens formatos e fazer o uso correto dos dados Exif.
O método createImageThumbnail
faz tudo isso, desde que você tenha
acesso ao caminho do arquivo de imagem.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Se você tiver apenas o Uri
, poderá usar o método loadThumbnail
na
ContentResolver a partir do Android 10, API de nível 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
O ImageDecoder, disponível a partir do Android 9, API de nível 28, tem alguns opções sólidas para reamostrar a imagem à medida que você a decodifica para evitar memória extra 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);
Você pode usar BitmapFactory para criar miniaturas para apps destinados anteriormente Versões do Android. BitmapFactory.Options tem uma configuração para decodificar apenas os limites de uma imagem para fins de reamostragem.
Primeiro, decodifique apenas os limites do bitmap no 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()
Usar width
e height
de BitmapFactory.Options
para definir a amostra
tamanho:
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
}
}
Decodifique o stream. O tamanho da imagem resultante é amostrado por potências de dois
com base no inSampleSize
.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Criar uma miniatura a partir de um arquivo de vídeo local
Conseguir imagens de miniaturas de vídeo envolve muitos dos mesmos desafios que o de imagens, mas os tamanhos de arquivo podem ser muito maiores e conseguir um frame de vídeo representativo nem sempre é tão simples quanto escolher o primeiro frame do vídeo.
O método createVideoThumbnail
é uma ótima escolha se você tiver acesso a
o caminho do arquivo de vídeo.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Se você só tiver acesso a um URI de conteúdo, poderá usar
MediaMetadataRetriever
Primeiro, verifique se o vídeo tem uma miniatura incorporada e use-a se possível:
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));
}
Busque a largura e a altura do vídeo do MediaMetadataRetriever
para
calcule o fator de escalonamento:
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)
No Android 9 e versões mais recentes (nível 28 da API), a MediaMetadataRetriever
pode retornar
frame:
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
}
}
Caso contrário, retorna o primeiro frame sem escalonamento:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}