媒體縮圖可讓使用者快速預覽圖片和影片 加快瀏覽速度,同時以視覺化的方式呈現應用程式介面 吸引力十足的內容由於縮圖小於原尺寸的媒體 協助節省記憶體、儲存空間和頻寬,同時改善媒體品質 瀏覽效能。
視應用程式的檔案類型和檔案存取權而定,以及 媒體素材資源,您能夠以多種方式建立縮圖。
使用圖片載入庫建立縮圖
圖片載入程式庫可代您處理大部分的繁瑣作業;此類程式庫可同時處理快取 (這樣您便無需多次下載圖片) 和網路邏輯,以便從本機或網路資源中擷取來源媒體。以下程式碼示範如何使用 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 載入圖片及載入大型點陣圖 ,瞭解如何處理大型圖片。
從本機圖片檔建立縮圖
如要取得縮圖,就必須有效率地縮小圖片,同時保留影像內容 避免過度使用記憶體、處理各種圖片 以及正確使用 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)
自 Android 9 (API 級別 28) 起,ImageDecoder 提供了一些 提供圖片解碼時重新取樣的固體選項,可避免產生額外的記憶體 相關單位會如何運用資料,並讓他們覺得自己 獲得充分告知,且能夠針對該使用方式表示同意
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()
請使用 BitmapFactory.Options
中的 width
和 height
設定取樣大小:
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
}