Thumbnail media memberi pengguna pratinjau visual cepat tentang gambar dan video, sehingga memungkinkan penjelajahan yang lebih cepat sekaligus membuat antarmuka aplikasi lebih menarik dan menarik secara visual. Karena thumbnail lebih kecil daripada media berukuran penuh, thumbnail membantu menghemat memori, ruang penyimpanan, dan bandwidth sekaligus meningkatkan performa penjelajahan media.
Tergantung pada jenis file dan akses file yang Anda miliki dalam aplikasi dan aset media, Anda dapat membuat thumbnail dengan berbagai cara.
Membuat thumbnail menggunakan library pemuatan gambar
Library pemuatan gambar melakukan banyak bagian pekerjaan yang sulit untuk Anda; mereka dapat menangani ke cache bersama dengan logika untuk mengambil sumber media dari lokal atau jaringan resource berdasarkan Uri. Kode berikut menunjukkan penggunaan library pemuatan gambar Coil yang berfungsi untuk gambar dan video, dan berfungsi pada resource lokal atau jaringan.
// 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
)
Jika memungkinkan, buat thumbnail di sisi server. Lihat Memuat gambar untuk mengetahui detail tentang cara memuat gambar menggunakan Compose, dan Memuat bitmap besar secara efisien untuk mendapatkan panduan tentang cara menggunakan gambar berukuran besar.
Membuat thumbnail dari file gambar lokal
Mendapatkan gambar thumbnail melibatkan penskalaan ke bawah yang efisien sekaligus mempertahankan kualitas visual, menghindari penggunaan memori yang berlebihan, menangani berbagai format gambar, dan menggunakan data Exif dengan benar.
Metode createImageThumbnail melakukan semua ini, asalkan Anda memiliki
akses ke jalur file gambar.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Jika hanya memiliki Uri, Anda dapat menggunakan metode loadThumbnail di
ContentResolver mulai dari Android 10, API level 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
ImageDecoder, yang tersedia mulai Android 9, level API 28, memiliki beberapa opsi padat untuk melakukan resampling gambar saat Anda mendekodenya untuk mencegah memori tambahan gunakan.
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);
Anda dapat menggunakan BitmapFactory untuk membuat thumbnail bagi aplikasi yang menargetkan rilis Android sebelumnya. BitmapFactory.Options memiliki setelan untuk hanya mendekode batas tertentu pada gambar untuk pengambilan sampel ulang.
Pertama, dekode batas bitmap saja ke dalam 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()
Gunakan width dan height dari BitmapFactory.Options untuk menetapkan contoh
ukuran:
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
}
}
Mendekode streaming. Ukuran gambar yang dihasilkan diambil sampelnya dengan pangkat dua
berdasarkan inSampleSize.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Membuat thumbnail dari file video lokal
Mendapatkan gambar thumbnail video memunculkan banyak tantangan yang sama seperti mendapatkan {i>thumbnail<i} gambar, tetapi ukuran file bisa jauh lebih besar dan mendapatkan frame video yang representatif tidak selalu semudah memilih frame video.
Metode createVideoThumbnail adalah pilihan yang tepat jika Anda memiliki akses ke
dari jalur file video.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Jika hanya memiliki akses ke URI konten, Anda dapat menggunakan
MediaMetadataRetriever
Pertama, periksa apakah video memiliki thumbnail yang disematkan, dan gunakan mungkin:
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));
}
Ambil lebar dan tinggi video dari MediaMetadataRetriever untuk
menghitung faktor penskalaan:
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)
Di Android 9+ (API level 28), MediaMetadataRetriever dapat menampilkan frame
yang diskalakan:
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
}
}
Jika tidak, tampilkan frame pertama tanpa diskalakan:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}