תמונות ממוזערות של מדיה מספקות למשתמשים תצוגה מקדימה ויזואלית מהירה של תמונות וסרטונים, מה שמאפשר גלישה מהירה יותר, וגם מציג את ממשק האפליקציה בצורה ויזואלית יותר מושך ומעניין. מכיוון שהתמונות הממוזערות הן קטנות יותר ממדיה בגודל מלא, הם עוזרים לחסוך בזיכרון, בנפח האחסון וברוחב הפס, ובמקביל משפרים את המדיה ביצועי הגלישה.
בהתאם לסוג הקובץ ולגישה שלכם לקובץ באפליקציה ובנכסי המדיה, תוכלו ליצור תמונות ממוזערות במגוון דרכים.
יצירת תמונה ממוזערת באמצעות ספריית הטעינה של תמונות
ספריות טעינת התמונות עושות את העבודה הקשה בשבילך, הם יכולים לטפל לשמור במטמון יחד עם הלוגיקה לאחזור מדיה המקור מהרשת או מהרשת המקומית. על סמך 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, ובמאמר טעינה יעילה של קובצי bitmap גדולים מוסבר איך לעבוד עם תמונות גדולות.
יצירת תמונה ממוזערת מקובץ תמונה מקומי
כדי לקבל תמונות ממוזערות, צריך להוריד את הגודל בצורה יעילה יותר, תוך שמירה על רכיבים חזותיים איכות, הימנעות משימוש מוגזם בזיכרון, טיפול במגוון תמונות ולהשתמש בנתוני 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);
אפשר להשתמש ב-Bitmap מראש כדי ליצור תמונות ממוזערות לאפליקציות שמטרגטות קודם. גרסאות של Android. ב-Bitmap לאירוע.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
size:
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
}