Medien-Thumbnails bieten Nutzern eine schnelle visuelle Vorschau von Bildern und Videos, was das Surfen beschleunigt und die App-Oberfläche visuell ansprechender und ansprechender macht. Da Miniaturansichten kleiner sind als Medien in voller Größe, Sie sparen Arbeitsspeicher, Speicherplatz und Bandbreite und optimieren die Medienwiedergabe die Browserleistung.
Je nach Dateityp und Dateizugriff in Ihrer Anwendung und Ihrer Medien-Assets können Sie auf unterschiedliche Weise Miniaturansichten erstellen.
Thumbnails mit einer Bildladebibliothek erstellen
Bibliotheken zum Laden von Bildern übernehmen einen Großteil der Arbeit für Sie. Sie können das Caching und die Logik zum Abrufen der Quellmedien aus der lokalen oder Netzwerkressource basierend auf einem Uri verarbeiten. Im folgenden Code wird die Verwendung der Bildladebibliothek Coil veranschaulicht, die sowohl für Bilder als auch für Videos funktioniert und auf einer lokalen oder Netzwerkressource ausgeführt werden kann.
// 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
)
Erstelle Thumbnails wenn möglich serverseitig. Unter Bilder laden finden Sie weitere Informationen zum Laden von Bildern mit Compose. Effizientes Laden großer Bitmaps enthält eine Anleitung zum Arbeiten mit großen Bildern.
Miniaturansicht aus einer lokalen Bilddatei erstellen
Für die Erstellung von Thumbnails ist ein effizientes Downscaling bei gleichzeitiger Wahrung der visuellen Qualität erforderlich. Außerdem muss die Speichernutzung begrenzt, mit einer Vielzahl von Bildformaten umgegangen und Exif-Daten richtig verwendet werden.
Die Methode createImageThumbnail führt all diese Schritte aus, sofern Sie Zugriff auf den Pfad der Bilddatei haben.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Wenn Sie nur das Uri haben, können Sie die Methode loadThumbnail in
ContentResolver ab Android 10, API-Level 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
Der ImageDecoder, der ab Android 9 (API-Level 28) verfügbar ist, bietet einige gute Optionen, um das Bild beim Decodieren neu zu bemustern und so eine zusätzliche Speichernutzung zu vermeiden.
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);
Mit BitmapFactory können Sie Thumbnails für Apps erstellen, die auf ältere Android-Releases ausgerichtet sind. BitmapFactory.Options verfügt über eine Einstellung, mit der nur das Grenzen eines Bildes für das Sampling.
Decodieren Sie zuerst nur die Grenzen der 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()
Verwenden Sie width und height aus BitmapFactory.Options, um das Beispiel festzulegen.
Größe:
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
}
}
Decodieren Sie den Stream. Die Größe des resultierenden Bilds wird basierend auf der inSampleSize mit Potenzen von 2 abgetastet.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Thumbnail aus einer lokalen Videodatei erstellen
Das Erstellen von Video-Thumbnail-Bildern beinhaltet viele der gleichen Herausforderungen wie aber die Dateigrößen können viel größer sein und eines repräsentativen Videoframes ist nicht immer so einfach wie die Auswahl des ersten Frame des Videos.
Die Methode createVideoThumbnail ist eine gute Wahl, wenn du Zugriff auf den Pfad der Videodatei hast.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Wenn du nur auf eine Inhalts-URI zugreifen kannst, kannst du MediaMetadataRetriever verwenden.
Prüfe zuerst, ob das Video ein Thumbnail hat, und verwende dieses, möglich:
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));
}
Rufen Sie die Breite und Höhe des Videos von MediaMetadataRetriever ab,
Skalierungsfaktor berechnen:
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)
Unter Android 9 und höher (API-Level 28) kann MediaMetadataRetriever einen skalierten Frame zurückgeben:
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
}
}
Andernfalls wird der erste Frame ohne Skalierung zurückgegeben:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}