ملاحظة: هناك العديد من المكتبات التي تتبع أفضل الممارسات لتحميل الصور. يمكنك استخدام هذه المكتبات في تطبيقك لتحميل الصور بأفضل طريقة محسَّنة. ننصحك بما يلي: التمرير الذي يقوم بتحميل الصور وعرضها في أسرع وقت ممكن وسلاسة. تشمل مكتبات تحميل الصور الشائعة الأخرى مكتبة بيكاسو من Square، وCoil من Instacart، فريسكو من Facebook. تعمل هذه المكتبات على تبسيط معظم المهام المعقدة المرتبطة باستخدام الصور النقطية وأنواع أخرى من الصور على Android.
تأتي الصور بجميع الأشكال والأحجام. وفي كثير من الحالات تكون أكبر مما هو مطلوب واجهة مستخدم التطبيق. على سبيل المثال، يعرض النظام تطبيق المعرض الصور التي تم التقاطها باستخدام كاميرا أجهزة Android التي تكون عادةً أعلى دقة بكثير من الشاشة وكثافة الجهاز.
بما أنّك تعمل باستخدام ذاكرة محدودة، يُفضَّل تحميل درجة دقة أقل نسخة في الذاكرة. يجب أن يتطابق الإصدار ذي الدقة الأقل مع حجم مكوِّن واجهة المستخدم الذي يعرضها. لا تقدّم الصورة ذات الدقة الأعلى أي فائدة مرئية، ولكنها تظلّ صالحة مساحة من الذاكرة الثمينة وتتحمل نفقات إضافية في الأداء بسبب زيادة المساحة سريعًا والتحجيم.
يرشدك هذا الدرس إلى كيفية فك ترميز الصور النقطية الكبيرة بدون تجاوز عدد عناصر كل تطبيق الحد المخصص للذاكرة من خلال تحميل نسخة أصغر حجمًا مستندة إلى عينة فرعية في الذاكرة.
قراءة أبعاد الصورة النقطية ونوعها
توفّر الفئة BitmapFactory
عدة طرق فك ترميز (decodeByteArray()
أو decodeFile()
أو decodeResource()
أو غير ذلك) لإنشاء Bitmap
من مصادر مختلفة. اختيار
أنسب طريقة فك الترميز استنادًا إلى مصدر بيانات الصورة. تحاول هذه الطرق
تخصيص ذاكرة للصورة النقطية التي تم إنشاؤها، وبالتالي يمكن أن يؤدي ذلك بسهولة إلى ظهور OutOfMemory
. يحتوي كل نوع من طرق فك الترميز على توقيعات إضافية تتيح لك تحديد فك الترميز.
الخيارات عبر فئة BitmapFactory.Options
. ضبط السمة inJustDecodeBounds
على true
أثناء فك الترميز
ستتجنّب تخصيص الذاكرة، وسيتم عرض null
لكائن الصورة النقطية مع ضبط outWidth
وoutHeight
وoutMimeType
. يتيح لك هذا الأسلوب قراءة
ونوع بيانات الصورة قبل إنشاء (وتخصيص الذاكرة)
صورة نقطية.
Kotlin
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } BitmapFactory.decodeResource(resources, R.id.myimage, options) val imageHeight: Int = options.outHeight val imageWidth: Int = options.outWidth val imageType: String = options.outMimeType
Java
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;
لتجنُّب استثناءات java.lang.OutOfMemory
، راجِع أبعاد الصورة النقطية قبل
فك ترميزها، ما لم تكن واثقًا تمامًا من المصدر لتزويدك ببيانات صور بحجم يمكن توقُّعه
يتناسب بشكل مريح مع الذاكرة المتاحة.
تحميل نسخة تم تصغيرها في "الذاكرة"
بعد أن أصبحت أبعاد الصورة معروفة، يمكن استخدامها لتحديد ما إذا كان يجب تعديل الصورة بالكامل تحميلها في الذاكرة أو إذا كان ينبغي تحميل نسخة فرعية بدلاً من ذلك. فيما يلي بعض العوامل ضع في اعتبارك:
- الاستخدام المقدَّر للذاكرة لتحميل الصورة كاملة في الذاكرة.
- حجم الذاكرة التي تريد الالتزام بها لتحميل هذه الصورة نظرًا لأي ذاكرة أخرى ومتطلبات طلبك.
- أبعاد السمة
ImageView
المستهدفة أو مكوّن واجهة المستخدم الذي تستخدمه الصورة هي التحميل إليه. - حجم شاشة الجهاز الحالي وكثافته
على سبيل المثال، لا يستحق الأمر تحميل صورة بحجم 1024×768 بكسل في الذاكرة إذا كانت ستتم في النهاية
في صورة مصغّرة بحجم 128x96 بكسل في ImageView
.
لتوجيه برنامج فك الترميز إلى إعداد عيّنة فرعية للصورة، وتحميل نسخة أصغر في الذاكرة، اضبط inSampleSize
على true
في عنصر BitmapFactory.Options
. على سبيل المثال، صورة بدقة 2048x1536
فك ترميزه باستخدام inSampleSize
من 4 ينتج عنها
صورة نقطية بحجم 512×384 تقريبًا. يؤدي تحميل هذا الملف إلى الذاكرة إلى استخدام 0.75 ميغابايت بدلاً من 12 ميغابايت لحجم الملف
صورة (بافتراض إعداد الصورة النقطية لـ ARGB_8888
). إليك
طريقة لحساب قيمة حجم العينة التي تساوي اثنين بناءً على عرض الهدف
الارتفاع:
Kotlin
fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { // Raw height and width of image val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val halfHeight: Int = height / 2 val halfWidth: Int = width / 2 // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) { inSampleSize *= 2 } } return inSampleSize }
Java
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } return inSampleSize; }
ملاحظة: يتم احتساب قوة مؤلفة من قيمتَين لأنّ برنامج فك الترميز يستخدم
قيمة نهائية عن طريق التقريب إلى أقرب أس اثنين، وفقًا للمستندات inSampleSize
.
لاستخدام هذه الطريقة، يجب أولاً فك الترميز مع ضبط inJustDecodeBounds
على true
وضبط الخيارات
ثم فك الترميز مرة أخرى باستخدام قيمة inSampleSize
الجديدة وضبط inJustDecodeBounds
على false
:
Kotlin
fun decodeSampledBitmapFromResource( res: Resources, resId: Int, reqWidth: Int, reqHeight: Int ): Bitmap { // First decode with inJustDecodeBounds=true to check dimensions return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeResource(res, resId, this) // Calculate inSampleSize inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight) // Decode bitmap with inSampleSize set inJustDecodeBounds = false BitmapFactory.decodeResource(res, resId, this) } }
Java
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); }
تسهّل هذه الطريقة تحميل صورة نقطية ذات حجم كبير عشوائيًا في ImageView
تعرض صورة مصغّرة بحجم 100×100 بكسل، كما هو موضّح في المثال التالي.
الرمز:
Kotlin
imageView.setImageBitmap( decodeSampledBitmapFromResource(resources, R.id.myimage, 100, 100) )
Java
imageView.setImageBitmap( decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
ويمكنك اتباع عملية مشابهة لفك ترميز الصور النقطية من مصادر أخرى، وذلك عن طريق استبدال
BitmapFactory.decode*
المناسبة حسب الحاجة.