إنشاء صور مصغّرة للوسائط

تتيح الصور المصغّرة للوسائط للمستخدمين معاينة مرئية سريعة للصور والفيديوهات السماح بالتصفح بشكل أسرع مع جعل واجهة التطبيق أكثر وضوحًا جذابة وجذابة. بما أنّ الصور المصغّرة أصغر حجمًا من الوسائط ذات الحجم الكامل، فهي تساعد على توفير الذاكرة ومساحة التخزين ومعدل نقل البيانات مع تحسين أداء الوسائط أداء التصفّح

استنادًا إلى نوع الملف وإمكانية الوصول إلى الملف في تطبيقك ومواد عرض الوسائط، يمكنك إنشاء صور مصغّرة بطرق مختلفة.

إنشاء صورة مصغّرة باستخدام مكتبة لتحميل الصور

تتولّى مكتبات تحميل الصور إنجاز المهام الصعبة نيابةً عنك. يمكنه التعامل مع التخزين المؤقت إلى جانب المنطق لجلب وسائط المصدر من الشبكة المحلية أو الشبكة بناءً على 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
)

إنشاء صور مصغّرة من جهة الخادم، إن أمكن، اطّلِع على مقالة تحميل الصور لمعرفة التفاصيل حول كيفية تحميل الصور باستخدام أداة "الإنشاء"، وعلى مقالة تحميل ملفات بترميز bitmap كبيرة بكفاءة للحصول على إرشادات حول كيفية التعامل مع الصور الكبيرة.

إنشاء صورة مصغّرة من ملف صورة محلي

يتضمن الحصول على صور مصغّرة تقليل الحجم الفعال مع الحفاظ على العناصر المرئية الجودة، وتجنب الاستخدام المفرط للذاكرة، والتعامل مع مجموعة متنوعة من الصور والاستخدام الصحيح لبيانات Exif.

وتنفّذ الطريقة createImageThumbnail كل هذا، وتوفر لك الوصول إلى مسار ملف الصورة.

val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)

إذا كان لديك Uri فقط، يمكنك استخدام الطريقة loadThumbnail في ContentLearnr بدءًا من Android 10، المستوى 29 من واجهة برمجة التطبيقات

val thumbnail: Bitmap =
        applicationContext.contentResolver.loadThumbnail(
        content-uri, Size(640, 480), null)

يتضمن ImageDecoder، المتاح بدءًا من Android 9، والمستوى 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);

ويمكنك استخدام Bitmapfactor لإنشاء صور مصغّرة للتطبيقات التي تستهدف العملاء في مرحلة مبكرة. إصدارات 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)

في الإصدار 9 من نظام التشغيل Android والإصدارات الأحدث (المستوى 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
}