Миниатюры мультимедиа предоставляют пользователям быстрый визуальный предварительный просмотр изображений и видео, что позволяет ускорить просмотр и сделать интерфейс приложения более визуально привлекательным и привлекательным. Поскольку миниатюры меньше полноразмерных носителей, они помогают экономить память, место для хранения и пропускную способность, одновременно повышая производительность просмотра мультимедиа.
В зависимости от типа файла и доступа к файлу, который у вас есть в вашем приложении и ваших медиаресурсах, вы можете создавать миниатюры различными способами.
Создайте миниатюру, используя библиотеку загрузки изображений.
Библиотеки загрузки изображений делают за вас большую часть тяжелой работы; они могут обрабатывать кэширование вместе с логикой для извлечения исходного мультимедиа из локального или сетевого ресурса на основе Uri . Следующий код демонстрирует, как использование библиотеки загрузки изображений Coil работает как с изображениями, так и с видео, а также работает с локальным или сетевым ресурсом.
// 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
)
Если возможно, создайте миниатюры на стороне сервера . Подробные сведения о том, как загружать изображения с помощью Compose, см. в разделах « Загрузка изображений» , а инструкции по работе с большими изображениями — «Эффективная загрузка больших растровых изображений» .
Создание миниатюры из локального файла изображения
Получение миниатюр изображений предполагает эффективное уменьшение масштаба при сохранении визуального качества, избежание чрезмерного использования памяти, работу с различными форматами изображений и правильное использование данных Exif .
Все это выполняет метод createImageThumbnail
, при условии, что у вас есть доступ к пути к файлу изображения.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Если у вас есть только Uri
, вы можете использовать метод loadThumbnail
в ContentResolver, начиная с Android 10, уровень API 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
ImageDecoder , доступный начиная с Android 9, уровень API 28, имеет несколько надежных опций для повторной выборки изображения по мере его декодирования, чтобы предотвратить дополнительное использование памяти.
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);
Вы можете использовать BitmapFactory для создания миниатюр для приложений, предназначенных для более ранних выпусков Android. BitmapFactory.Options имеет настройку для декодирования только границ изображения с целью повторной выборки.
Сначала декодируйте только границы растрового изображения в 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()
Используйте width
и height
из BitmapFactory.Options
, чтобы установить размер выборки:
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
}
}
Расшифруйте поток. Размер результирующего изображения выбирается по степени двойки на основе inSampleSize
.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Создание миниатюры из локального видеофайла
Получение миниатюр видеоизображений сопряжено со многими из тех же проблем, что и получение миниатюр изображений, но размеры файлов могут быть намного больше, и получить репрезентативный видеокадр не всегда так просто, как выбрать первый кадр видео.
Метод createVideoThumbnail
— хороший выбор, если у вас есть доступ к пути к видеофайлу.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Если у вас есть доступ только к Uri контента, вы можете использовать MediaMetadataRetriever
.
Сначала проверьте, есть ли в видео встроенная миниатюра, и по возможности используйте ее:
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));
}
Получите ширину и высоту видео из MediaMetadataRetriever
, чтобы рассчитать коэффициент масштабирования:
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)
В Android 9+ (уровень API 28) MediaMetadataRetriever
может возвращать масштабированный кадр:
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
}
}
В противном случае верните первый кадр немасштабированным:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}