تصاویر کوچک رسانه را ایجاد کنید

ریز عکس‌های رسانه پیش‌نمایش بصری سریع تصاویر و ویدیوها را در اختیار کاربران قرار می‌دهند که امکان مرور سریع‌تر را فراهم می‌کند و در عین حال رابط برنامه را از نظر بصری جذاب‌تر و جذاب‌تر می‌کند. از آنجایی که تصاویر کوچکتر از رسانه های با اندازه کامل هستند، به صرفه جویی در حافظه، فضای ذخیره سازی و پهنای باند کمک می کنند و در عین حال عملکرد مرور رسانه را بهبود می بخشند.

بسته به نوع فایل و دسترسی به فایلی که در برنامه و دارایی های رسانه خود دارید، می توانید تصاویر کوچک را به روش های مختلف ایجاد کنید.

یک تصویر کوچک با استفاده از کتابخانه بارگذاری تصویر ایجاد کنید

کتابخانه های بارگذاری تصویر کارهای سنگین را برای شما انجام می دهند. آنها می توانند کش را همراه با منطق واکشی رسانه منبع از منبع محلی یا شبکه مبتنی بر Uri انجام دهند. کد زیر استفاده از کتابخانه بارگذاری تصویر کویل را برای تصاویر و ویدیوها کار می‌کند و بر روی یک منبع محلی یا شبکه کار می‌کند.

// 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)

ImageDecoder که با اندروید 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 برای ایجاد تصاویر کوچک برای برنامه‌هایی که نسخه‌های قبلی اندروید را هدف قرار می‌دهند، استفاده کنید. 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
}