Miniatury multimediów zapewniają użytkownikom szybki podgląd obrazów i filmów, co pozwala na szybsze przeglądanie, a także sprawia, że interfejs aplikacji jest bardziej atrakcyjny i zachęca do korzystania. Miniatury są mniejsze niż pełnowymiarowe multimedia, pozwalają zaoszczędzić pamięć, miejsce na dane i przepustowość łącza, jednocześnie ulepszając multimedia wydajność przeglądania.
W zależności od typu pliku i dostępu do plików w aplikacji oraz zasobach multimedialnych możesz tworzyć miniatury na różne sposoby.
Tworzenie miniatury za pomocą biblioteki wczytywania obrazów
Biblioteki do wczytywania obrazów wykonują za Ciebie wiele ciężkiej pracy. Mogą obsługiwać buforowanie wraz z logiką pobierania mediów źródłowych z zasobu lokalnego lub sieciowego na podstawie identyfikatora URI. Poniższy kod ilustruje użycie funkcji Biblioteka wczytywania obrazów Coil działa zarówno w przypadku obrazów, jak i filmów, i działa na zasobie lokalnym lub sieciowym.
// 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
)
Jeśli to możliwe, utwórz miniatury po stronie serwera. Więcej informacji o wczytywaniu obrazów za pomocą Compose znajdziesz w artykule Wczytywanie obrazów, a wskazówki dotyczące pracy z dużymi obrazami – w artykule Efektywne wczytywanie dużych bitmap.
Tworzenie miniatury z lokalnego pliku graficznego
Uzyskiwanie miniatur obrazów umożliwia efektywne skalowanie przy jednoczesnym zachowaniu obrazów jakość, unikanie nadmiernego wykorzystania pamięci, obsługa różnych obrazów oraz poprawne korzystanie z danych Exif.
Metoda createImageThumbnail
wykonuje wszystkie te czynności, o ile masz dostęp do ścieżki do pliku obrazu.
val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)
Jeśli masz tylko metodę Uri
, metody loadThumbnail
możesz użyć w
Content resolver od Androida 10 i poziomu interfejsu API 29.
val thumbnail: Bitmap =
applicationContext.contentResolver.loadThumbnail(
content-uri, Size(640, 480), null)
W narzędziu ImageDecoder, który jest dostępny w wersji od Androida 9 (poziom interfejsu API 28), występuje pełne opcje umożliwiające ponowne próbkowanie obrazu podczas dekodowania, co pozwala uniknąć dodatkowej pamięci. i ich używanie.
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);
Możesz użyć BitmapFactory, aby utworzyć miniatury aplikacji, na które są kierowane reklamy wcześniej Wersje Androida. BitmapFactory.Options ma ustawienie, które umożliwia dekodowanie tylko granic obrazu na potrzeby próbkowania.
Najpierw zdekoduj granice bitmapy do elementu 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()
Aby ustawić próbkę, użyj znaczników width
i height
z usługi BitmapFactory.Options
rozmiar:
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
}
}
zdekodować strumień; Rozmiar wynikowego obrazu jest próbkowany przez potęgi 2
na podstawie: inSampleSize
.
options.inJustDecodeBounds = false
val decodeStream = context.contentResolver.openInputStream(uri)
val bitmap = BitmapFactory.decodeStream(decodeStream, null, options)
decodeStream?.close()
return bitmap
}
Tworzenie miniatury na podstawie lokalnego pliku wideo
Robienie miniatur filmów wiąże się z wieloma takimi samymi wyzwaniami, jak w przypadku ale ich rozmiary mogą być znacznie większe i reprezentatywna klatka filmu nie zawsze jest tak prosta, jak wybranie pierwszej każdej klatki filmu.
Metoda createVideoThumbnail
jest dobrym rozwiązaniem, jeśli masz dostęp do
ścieżkę pliku wideo.
val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)
Jeśli masz dostęp tylko do identyfikatora URI treści, możesz używać
MediaMetadataRetriever
Najpierw sprawdź, czy film zawiera wbudowaną miniaturę. Jeśli to możliwe, użyj jej:
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));
}
Pobierz szerokość i wysokość filmu od MediaMetadataRetriever
do
oblicz współczynnik skalowania:
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)
W Androidzie 9 i nowszych (poziom interfejsu API 28) MediaMetadataRetriever
może zwracać skalowane wartości
ramka:
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
}
}
W przeciwnym razie zwróć pierwszą klatkę bez przeskalowania:
// consider scaling this after the fact
val frame = mediaMetadataRetriever.frameAtTime
mediaMetadataRetriever.close()
return frame
}