ภาพขนาดย่อของสื่อช่วยให้ผู้ใช้ดูตัวอย่างรูปภาพและวิดีโอได้อย่างรวดเร็ว ทำให้ท่องเว็บได้เร็วขึ้นในขณะที่ทำให้มองเห็นอินเทอร์เฟซของแอปได้ชัดเจนขึ้น น่าดึงดูดและน่าสนใจ เนื่องจากภาพขนาดย่อมีขนาดเล็กกว่าสื่อขนาดเต็ม ซึ่งจะช่วยประหยัดหน่วยความจำ พื้นที่เก็บข้อมูล และแบนด์วิดท์ พร้อมทั้งปรับปรุงสื่อ ประสิทธิภาพในการท่องเว็บ
คุณสามารถสร้างภาพขนาดย่อได้หลายวิธีโดยขึ้นอยู่กับประเภทไฟล์และการเข้าถึงไฟล์ที่คุณมีในแอปพลิเคชันและชิ้นงานสื่อ
สร้างภาพขนาดย่อโดยใช้ไลบรารีการโหลดรูปภาพ
ไลบรารีการโหลดรูปภาพจะทํางานหนักให้คุณมากมาย ไลบรารีสามารถจัดการแคชพร้อมกับตรรกะในการดึงข้อมูลสื่อต้นทางจากทรัพยากรในเครื่องหรือเครือข่ายตาม 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
)
หากเป็นไปได้ ให้สร้างภาพขนาดย่อฝั่งเซิร์ฟเวอร์ ดูการโหลดรูปภาพ เพื่อดูรายละเอียดเกี่ยวกับวิธีโหลดรูปภาพโดยใช้การเขียน และการโหลดบิตแมปขนาดใหญ่ อย่างมีประสิทธิภาพสำหรับคำแนะนำเกี่ยวกับวิธีทำงานกับรูปภาพขนาดใหญ่
สร้างภาพปกจากไฟล์ภาพในเครื่อง
การสร้างภาพขนาดย่อเกี่ยวข้องกับการปรับขนาดให้เล็กลงอย่างมีประสิทธิภาพในขณะที่ยังคงคุณภาพของภาพไว้ หลีกเลี่ยงการใช้หน่วยความจำมากเกินไป จัดการกับรูปแบบรูปภาพที่หลากหลาย และใช้ข้อมูล 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
}