Advertisement
  1. Code
  2. Android SDK

ترميز تطبيق معرض صور Android مع بيكاسو

Scroll to top
Read Time: 17 min

() translation by (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Picasso هي مكتبة Android مفتوحة المصدر الشعبية لتحميل الصور المحلية والبعيدة. تعلم كيفية استخدامها بسهولة للتعامل مع احتياجاتك لتحميل الصور.

1. ما هو بيكاسو؟

بيكاسو (اسم مستوحى من الفنان الفرنسي الشهير بابلو بيكاسو) هي مكتبة أندرويد شعبية مفتوحة المصدر لتحميل الصور في تطبيق Android الخاص بك. وفقًا للمستندات الرسمية، تنص على ما يلي:

... تسمح بيكاسو بتحميل صور خالية من المتاعب في تطبيقك - في كثيرمن الأحيان في سطر واحد من التعليمات البرمجية!

لاحظ أن بيكاسو يستخدم OkHttp (مكتبة الشبكة من نفس المطور) تحت الغطاء لتحميل الصور عبر الإنترنت.

2. لماذا إذن استخدم بيكاسو؟

الآن لقد تعلمت ما يدور حول بيكاسو، والسؤال التالي الذي قد تسأله هو لماذا تستخدمه؟

يمكن أن يكون تطوير وظيفة تحميل وعرض الوسائط الخاصة بك في Java أو Kotlin بمثابة ألم حقيقي: يجب عليك الاهتمام بالتخزين المؤقت وفك التشفير وإدارة اتصالات الشبكة والترابط ومعالجة الاستثناءات والمزيد. بيكاسو هي مكتبة سهلة الاستخدام ومخطط لها جيدا وموثقة ومدروسة جيدا ويمكنها أن توفر لك الكثير من الوقت الثمين - وتوفر عليك بعض الصداع.

في ما يلي العديد من المخاطر الشائعة لتحميل الصور على Android والتي تم التعامل معها من أجلك بواسطة بيكاسو، وفقًا للمستندات الرسمية:

  • التعامل مع إعادة تدوير عرض الصور وحذف التحميل في المحول
  • تحويلات الصور المعقدة مع الحد الأدنى من استخدام الذاكرة
  • الذاكرة التلقائية والتخزين المؤقت للقرص

يمكن أن تؤدي إضافة صور إلى تطبيقك إلى جعل تطبيق Android الخاص بك ينبض بالحياة. لذا في هذا البرنامج التعليمي، سنتعرف على Picasso 2 من خلال إنشاء تطبيق معرض صور بسيط. سيتم تحميل الصور عبر الإنترنت وعرضها كصور مصغرة في مدور العرض، وعندما ينقر المستخدم على صورة، فإنه سيفتح نشاطًا تفصيليًا يحتوي على الصورة الأكبر.

يمكن العثور على عينة من المشروع (في Kotlin) لهذا البرنامج التعليمي في المرجع GitHub لدينا حتى تتمكن من المتابعة بسهولة دائماً.

نسخة الفنانين الجيدة، وسرقة الفنانين الكبار. - بابلو بيكاسو

3. الشروط الأساسية

لتتمكن من متابعة هذا البرنامج التعليمي، ستحتاج إلى:

  • فهم أساسي لواجهة برمجة تطبيقات Android الأساسية و Kotlin
  • Android Studio 3.1.1 أو إصدار أحدث
  • البرنامج المساعد Kotlin plugin1.2.30 أو أعلى

أطلق Android Studio وأنشئ مشروعًا جديدًا (يمكنك تسميته البرنامج التجريبي بيكاسو) بنشاط فارغ يسمى النشاط الاساسي. تأكد أيضًا من فحص خانة اختبار تضمين الدعم Kotlin.

Android Studios Add an Activity to Mobile dialogAndroid Studios Add an Activity to Mobile dialogAndroid Studios Add an Activity to Mobile dialog

4. إعلان التبعيات

بعد إنشاء مشروع جديد، حدد التبعيات التالية في build.gradle. في وقت كتابة هذا التقرير، أحدث إصدار من بيكاسو هو 2.71828.

1
dependencies {
2
    implementation 'com.android.support:recyclerview-v7:27.1.1'
3
    implementation 'com.squareup.picasso:picasso:2.71828'
4
}

او مع مافن:

1
<dependency>
2
  <groupId>com.squareup.picasso</groupId>
3
  <artifactId>picasso</artifactId>
4
  <version>2.71828</version>
5
</dependency>

تأكد من مزامنة مشروعك بعد إضافة بيكاسو ومنتجات RecyclerView v7.

5. إضافة إذن الإنترنت

نظرًا لأن بيكاسو سيقوم بتنفيذ طلب الشبكة لتحميل الصور عبر الإنترنت ، نحتاج إلى تضمين إذن الإنترنت في AndroidManifest.xml الخاص بنا.

لذا افعل ذلك الآن!

1
<uses-permission android:name="android.permission.INTERNET" />

لاحظ أن هذا مطلوب فقط إذا كنت ستقوم بتحميل الصور من الإنترنت. هذا ليس مطلوبًا إذا كنت تقوم فقط بتحميل الصور محليًا على الجهاز.

6. إنشاء تخطيط

سنبدأ بإنشاء RecyclerView الخاص بنا داخل ملف layout activity_main.xml.

1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout
3
        xmlns:android="http://schemas.android.com/apk/res/android"
4
        android:id="@+id/activity_main"
5
        android:layout_width="match_parent"
6
        android:layout_height="match_parent">
7
8
    <android.support.v7.widget.RecyclerView
9
            android:id="@+id/rv_images"
10
            android:layout_width="match_parent"
11
            android:layout_height="match_parent"/>
12
</RelativeLayout>

إنشاء تخطيط العنصر المخصص

بعد ذلك، لنقم بإنشاء تنسيق (XML (item_image.xml الذي سيتم استخدامه لكل عنصر (ImageView) ضمن RecyclerView.

1
<?xml version="1.0" encoding="utf-8"?>
2
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
              android:orientation="vertical"
4
              android:layout_width="match_parent"
5
              android:layout_height="wrap_content">
6
    
7
    <ImageView
8
            android:id="@+id/iv_photo"
9
            android:adjustViewBounds="true"
10
            android:layout_height="200dp"
11
            android:scaleType="centerCrop"
12
            android:layout_margin="2dp"
13
            android:layout_width="match_parent"/>
14
</LinearLayout>

الآن بعد أن أنشأنا التخطيطات اللازمة لتطبيق معرضنا البسيط، فإن الخطوة التالية هي إنشاء محول RecyclerView لتعبئة البيانات. قبل أن نفعل ذلك، دعونا على كل حال ننشئ نموذج البيانات البسيطة.

7. إنشاء نموذج البيانات

سنقوم بتحديد نموذج البيانات البسيط لـ RecyclerView الخاص بنا. تطبق هذه الرزمة من النموذج لنقل البيانات عالية الأداء من مكون إلى آخر في Android. في حالتنا، سيتم نقل البيانات من SunsetGalleryActivity إلى SunsetPhotoActivity.

1
data class SunsetPhoto(val url: String) : Parcelable {
2
3
    constructor(parcel: Parcel) : this(parcel.readString())
4
5
    override fun writeToParcel(parcel: Parcel, flags: Int) {
6
        parcel.writeString(url)
7
    }
8
9
    override fun describeContents(): Int {
10
        return 0
11
    }
12
13
    companion object CREATOR : Parcelable.Creator<SunsetPhoto> {
14
        override fun createFromParcel(parcel: Parcel): SunsetPhoto {
15
            return SunsetPhoto(parcel)
16
        }
17
18
        override fun newArray(size: Int): Array<SunsetPhoto?> {
19
            return arrayOfNulls(size)
20
        }
21
    }
22
}

لاحظ أن هذا النموذج SunsetPhoto يحتوي على حقل واحد فقط يسمى url (لأغراض تجريبية)، ولكن يمكنك الحصول على المزيد إذا كنت تريد. هذه الفئة تطبق Parcelable، مما يعني أن علينا تجاوز بعض الأساليب.

يمكننا الاستفادة من Android Studio IDEA لتوليد هذه الأساليب، ولكن الجانب السلبي لهذا هو الصيانة. ماذا؟ في أي وقت نضيف أي حقول جديدة إلى هذه الفئة، قد ننسى أن نقوم بتحديث المنطق وطريقة WriteToParcel بشكل صريح، مما قد يؤدي إلى بعض الأخطاء إذا لم نقم بتحديث الطرق.

الآن، للتحايل على تحديث أو كتابة هذه الأساليب المتسلسلة، قدم Kotlin 1.1.14 الشرحParcelize. سيساعدنا هذا التعليق التوضيحي في إنشاء أساليب writeTTarParcel و writeFromPolcel  describeContents تلقائيًا تحت الغطاء.

1
@Parcelize
2
data class SunsetPhoto(val url: String) : Parcelable

الآن، لدينا ترميزنا لفئة SunsetPhoto هو مجرد سطرين! رائع!

تذكر إضافة الشيفرة التالية إلى وحدة تطبيقك build.gradle:

1
androidExtensions {
2
    experimental = true
3
}

بالإضافة إلى ذلك، قمت بتضمين كائن مرافق (أو طريقة ثابتة في جافا) احصل على  SunsetPhotos () في فئة طراز SunsetPhoto التي ستقوم فقط بإرجاع قائمة مصفوفة من SunsetPhoto عند الاستدعاء.

1
@Parcelize
2
data class SunsetPhoto(val url: String) : Parcelable {
3
4
    companion object {
5
        fun getSunsetPhotos(): Array<SunsetPhoto> {
6
            return arrayOf<SunsetPhoto>(SunsetPhoto("https://goo.gl/32YN2B"),
7
                    SunsetPhoto("https://goo.gl/Wqz4Ev"),
8
                    SunsetPhoto("https://goo.gl/U7XXdF"),
9
                    SunsetPhoto("https://goo.gl/ghVPFq"),
10
                    SunsetPhoto("https://goo.gl/qEaCWe"),
11
                    SunsetPhoto("https://goo.gl/vutGmM"))
12
        }
13
    }
14
}

8. قم بإنشاء المحول

سنقوم بإنشاء محول لملء RecyclerView لدينا بالبيانات. سنقوم أيضًا بتطبيق مستمع للنقرات لفتح النشاط التفصيلي - SunsetPhotoActivity - لتمريره مثالًا لـ SunsetPhoto كهدف إضافي. سيعرض النشاط التفصيلي عرضاً مقرباً للصورة. سنقوم بإنشائها في قسم لاحق.

1
class MainActivity : AppCompatActivity() {
2
    //...

3
    private inner class ImageGalleryAdapter(val context: Context, val sunsetPhotos: Array<SunsetPhoto>) 
4
     : RecyclerView.Adapter<ImageGalleryAdapter.MyViewHolder>() {
5
6
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ImageGalleryAdapter.MyViewHolder {
7
            val context = parent.context
8
            val inflater = LayoutInflater.from(context)
9
            val photoView = inflater.inflate(R.layout.item_image, parent, false)
10
            return MyViewHolder(photoView)
11
        }
12
13
        override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {
14
            val sunsetPhoto = sunsetPhotos[position]
15
            val imageView = holder.photoImageView
16
        }
17
18
        override fun getItemCount(): Int {
19
            return sunsetPhotos.size
20
        }
21
22
        inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
23
24
            var photoImageView: ImageView = itemView.findViewById(R.id.iv_photo)
25
26
            init {
27
                itemView.setOnClickListener(this)
28
            }
29
30
            override fun onClick(view: View) {
31
                val position = adapterPosition
32
                if (position != RecyclerView.NO_POSITION) {
33
                    val sunsetPhoto = sunsetPhotos[position]
34
                    val intent = Intent(context, SunsetPhotoActivity::class.java).apply {
35
                        putExtra(SunsetPhotoActivity.EXTRA_SUNSET_PHOTO, sunsetPhoto)
36
                    }
37
                    startActivity(intent)
38
                }
39
            }
40
        }
41
    }
42
}

لاحظ أننا استخدمنا وظيفة الامتداد التطبيقي لوضع العنصر كإضافة في الهدف. وللتذكير، ترجع دالة التطبيق الكائن الذي تم تمريره إليها كوسيطة (أي الكائن المستقبل).

9. تحميل الصور من عنوان URL

سنحتاج إلى بيكاسو لأداء مهمته في هذا القسم - ليس لرسم أعمالنا الفنية، بل لجلب صور من الإنترنت وعرضها. سنقوم بعرض هذه الصور بشكل فردي في ImageViews الخاص بها داخل أسلوب RecyclerView  على BindViewHolder () الخاص بنا أثناء تمرير المستخدم للتطبيق.

1
override fun onBindViewHolder(holder: ImageGalleryAdapter.MyViewHolder, position: Int) {
2
    val sunsetPhoto = sunsetPhotos[position]
3
    val imageView = holder.photoImageView
4
5
    Picasso.get()
6
            .load(sunsetPhoto.url)
7
            .placeholder(R.drawable.placeholder)
8
            .error(R.drawable.error)
9
            .fit()
10
            .into(imageView)
11
12
}

خطوة بخطوة، إليك ما يفعله طلب بيكاسو:

طريقة الحصول()

هذا يعيد مثال Picasso العمومي (مثال singleton) المستهل به باستخدام التكوينات الافتراضية التالية:

  • ذاكرة التخزين المؤقت LRU 15 ٪ هي ذاكرة الوصول العشوائي المتاحة للتطبيق.
  • ذاكرة تخزين مؤقت للقرص تصل إلى 2٪ من مساحة التخزين حتى 50 ميجابايت ولكن لا تقل عن 5 ميغابايت. ملاحظة: هذا متاح فقط على API 14+.
  • ثلاثة سلاسل تنزيل للقرص والوصول للشبكة.

لاحظ أنه في حالة عدم استيفاء هذه الإعدادات لمتطلبات التطبيق، يمكنك إنشاء مثيل بيكاسو الخاص بك مع التحكم الكامل في هذه التهيئات باستخدام Picasso.Builder.

1
val picassoBuilder = Picasso.Builder(context)
2
// do custom configurations

3
4
// Specify the {@link Downloader} that will be used for downloading images.

5
picassoBuilder.downloader() 
6
// Specify the ExecutorService for loading images in the background.

7
picassoBuilder.executor()
8
// Specify the memory Cache used for the most recent images.

9
picassoBuilder.memoryCache()
10
// and more

11
val picasso = picassoBuilder.build()

وأخيرًا، يمكنك استدعاء الأسلوب build () لإرجاع نسخة بيكاسو مع تكويناتك الخاصة.

من المستحسن القيام بذلك في Application.onCreate الخاص بك ثم قم بتعيينه كمثيل مفرد مع إعداد مثيل مفرد بيكاسو في هذه الطريقة — للتأكد من أن مثيل Picasso هو الإصدار العام.

طريقة التحميل ()

تحميل (مسار السلسلة) يبدأ بطلب صورة باستخدام المسار المحدد. يمكن أن يكون هذا المسار عبارة عن عنوان URL بعيد أو مورد ملف أو مورد محتوى، أو مورد Android.

  • العنصر النائب (int placeholderResId): معرف مورد عنصر نائب محلي أو قابل للسحب ليتم استخدامه أثناء تحميل الصورة ثم عرضها. إنه بمثابة تجربة مستخدم جيدة لعرض صورة عنصر نائب أثناء تنزيل الصورة.

لاحظ أن بيكاسو يتحقق أولاً مما إذا كانت الصورة المطلوبة موجودة في ذاكرة التخزين المؤقت، وإذا كانت كذلك ، فإنها تعرض الصورة من هناك (سنناقش التخزين المؤقت في بيكاسو أكثر في قسم لاحق).

أساليب أخرى

  • error (int errorResId): قابل للسحب ليتم استخدامه إذا تعذر تحميل الصورة المطلوبة - ربما بسبب تعطل موقع الويب.
  • noFade (): يتلاشى بيكاسو دائمًا في الصورة ليتم عرضها في ImageView. إذا كنت لا ترغب في إنشاء حركة رسوم متحركة تتلاشى، فاتصل ببساطة بالطريقة noFade ().
  • (into (ImageView imageView: عرض الصورة الهدف الذي سيتم وضع الصورة فيه.

تغيير حجم الصورة وتحويلها

إذا كان الخادم الذي تطلبه من الصورة لا يعطيك الصورة التي تحتاجها بالحجم المطلوب، يمكنك بسهولة تغيير حجم تلك الصورة باستخدام تغيير الحجم (int targetWidth ،int targetHeight). يؤدي استدعاء هذا الأسلوب إلى تغيير حجم الصورة ثم عرضها على ImageView. لاحظ أن الأبعاد بالبكسل (px)، وليس dp.

1
Picasso.get()
2
        .load(sunsetPhoto.url)
3
        .placeholder(R.drawable.placeholder)
4
        .resize(400, 200)
5
        .into(imageView)

يمكنك المرور على مورد أبعاد Android لكل من العرض والارتفاع باستخدام الطريقة resizeDimen (int targetWidthResId، int targetHeightResId). سيقوم هذا الأسلوب بتحويل قياس الابعاد إلى البكسلات الصرفة ثم استدعاء تغيير الحجم () أسفل الغطاء - لتمرير المقاسات المحولة (بالبكسل) كوسيطة.

1
Picasso.get()
2
   //...

3
    .resizeDimen(R.dimen.list_detail_image_size, R.dimen.list_detail_image_size)
4
    //...

لاحظ أن أساليب تغيير الحجم هذه لن تحترم نسبة العرض إلى الارتفاع. بمعنى آخر، يمكن تشويه نسبة العرض إلى الارتفاع للصورة.

لحسن الحظ، تعطينا بيكاسو بعض الطرق المفيدة لحل هذه المشكلة:

  • centerCrop (): يقوم بقياس الصورة بشكل موحد (الحفاظ على نسبة العرض إلى الارتفاع للصورة) بحيث تملأ الصورة المنطقة المعينة، مع عرض أكبر قدر الامكان من الصورة. إذا لزم الأمر، سيتم اقتصاص الصورة أفقيًا أو رأسيًا لملاءمتها. يؤدي استدعاء هذا الأسلوب إلى اقتصاص صورة داخل الحدود المحددة بواسطة تغيير الحجم ().
  • centerInside (): يقوم بتحجيم الصورة بحيث يكون كلا البعدين مساويين أو أقل من الحدود المطلوبة. سيؤدي هذا إلى توسيط صورة داخل الحدود المحددة بواسطة تغيير الحجم ().
  • ScaleDownonly (): تغيير حجم صورة فقط إذا كان حجم الصورة الأصلية أكبر من الحجم المستهدف المحدد بواسطة تغيير الحجم ().
  • fit (): محاولة تغيير حجم الصورة لتتلاءم تمامًا مع حدود ImageView المستهدفة.

تدوير الصورة

لدى بيكاسو واجهة برمجة تطبيقات سهلة لتدوير صورة ثم عرض تلك الصورة. تقوم طريقة التدوير (درجات التعويم) بتدوير الصورة بالدرجات المحددة.

1
Picasso.get()
2
    //...

3
    .rotate(90f)
4
    //...

في المثال السابق، هذا سيؤدي الى تدوير الصورة بمقدار 90 درجة، طريقة تدوير  (float pivotY،float degrees, float pivotX)  تدور الصورة بالدرجات المخصصة حول النقطة المحورية .

1
Picasso.get()
2
    //...

3
    .rotate(30f, 200f, 100f)
4
    //...

سنقوم هنا بتدوير الصورة بمقدار 30 درجة حول النقطة المحورية 200، و 100 بكسل.

تحويل

وبصرف النظر عن مجرد التلاعب بالصورة من خلال تدويرها ، يمنحنا بيكاسو أيضًا خيار تطبيق تحويل مخصص على صورة قبل عرضها.

يمكنك ببساطة إنشاء فئة تقوم بتنفيذ واجهة تحويل بيكاسو. عليك بعد ذلك اجتياز طريقتين:

  • تحويل الصورة النقطية (مصدر نقطي): يؤدي هذا إلى تحويل الصورة النقطية المصدر إلى صورة نقطية جديدة.
  • أساس سلسلة (): يرجع الاساس الفريد للتحويل، يُستخدم لأغراض التخزين المؤقت.

بعد الانتهاء من إنشاء التحويل المخصص، يمكنك تنفيذه ببساطة عن طريق استدعاء التحويل (تحويل التحول) في مثيل بيكاسو الخاص بك. لاحظ أنه يمكنك أيضًا تمرير قائمة التحويل لتحويل ().

1
Picasso.get()
2
        // ...

3
        .transform(CropCircleTransformation())
4
        .into(imageView)

هنا، قمت بتطبيق تحويل مقبض دائري إلى صورة من مكتبة Android المفتوحة المصدر في تحويل بيكاسو. تحتوي هذه المكتبة على العديد من التحويلات التي يمكنك تطبيقها على صورة مع بيكاسو — بما في ذلك التحويلات لتعتيم أو تغيير حجم الصورة. اذهب للتحقق من ذلك إذا كنت ترغب في تطبيق بعض التحويلات الرائعة لصورك.

10. تهيئة المحول

هنا، نقوم ببساطة بإنشاء RecyclerView الخاص بنا مع GridLayoutManager كمدير التخطيط، نهيئ المحول الخاص بنا، ونربطه إلى RecyclerView.

1
class MainActivity : AppCompatActivity() {
2
3
    private lateinit var recyclerView: RecyclerView
4
    private lateinit var imageGalleryAdapter: ImageGalleryAdapter
5
6
    override fun onCreate(savedInstanceState: Bundle?) {
7
        //...

8
        
9
        val layoutManager = GridLayoutManager(this, 2)
10
        recyclerView = findViewById(R.id.rv_images)
11
        recyclerView.setHasFixedSize(true)
12
        recyclerView.layoutManager = layoutManager
13
        imageGalleryAdapter = ImageGalleryAdapter(this, SunsetPhoto.getSunsetPhotos())
14
    }
15
16
    override fun onStart() {
17
        super.onStart()
18
        recyclerView.adapter = imageGalleryAdapter
19
    }
20
    // ...

21
}

11. إنشاء النشاط التفصيلي

قم بإنشاء نشاط جديد وتسميته SunsetPhotoActivity. نحصل على SunsetPhoto اضافية ونحمل الصورة - داخل onStart () - مع بيكاسو كما فعلنا من قبل.

1
class SunsetPhotoActivity : AppCompatActivity() {
2
3
    companion object {
4
        const val EXTRA_SUNSET_PHOTO = "SunsetPhotoActivity.EXTRA_SUNSET_PHOTO"
5
    }
6
7
    private lateinit var imageView: ImageView
8
    private lateinit var sunsetPhoto: SunsetPhoto
9
10
    override fun onCreate(savedInstanceState: Bundle?) {
11
        super.onCreate(savedInstanceState)
12
        setContentView(R.layout.activity_sunset_photo)
13
14
        sunsetPhoto = intent.getParcelableExtra(EXTRA_SUNSET_PHOTO)
15
        imageView = findViewById(R.id.image)
16
    }
17
18
    override fun onStart() {
19
        super.onStart()
20
21
        Picasso.get()
22
                .load(sunsetPhoto.url)
23
                .placeholder(R.drawable.placeholder)
24
                .error(R.drawable.error)
25
                .fit()
26
                .into(imageView)
27
    }
28
}

التخطيط التفصيلي

فيما يلي تخطيط لعرض النشاط التفصيلي. يعرض فقط ImageView التي ستُظهر النسخة الكاملة الدقة للصورة المحملة.

1
<?xml version="1.0" encoding="utf-8"?>
2
<LinearLayout
3
        xmlns:android="http://schemas.android.com/apk/res/android"
4
        android:layout_width="match_parent"
5
        android:layout_height="match_parent">
6
7
    <ImageView
8
            android:id="@+id/image"
9
            android:layout_width="match_parent"
10
            android:layout_height="wrap_content"
11
            android:adjustViewBounds="true"
12
            android:scaleType="fitCenter"
13
            android:layout_gravity="center"/>
14
</LinearLayout>

12. آلية التخزين المؤقت في بيكاسو

إذا لاحظت بعناية، ستلاحظ أنه عند الانتقال مرة أخرى إلى صورة تم تحميلها مسبقًا، يتم تحميلها بشكل أسرع من ذي قبل. ما الذي جعلها أسرع؟ إنها آلية التخزين المؤقت بيكاسو، وهذا هو السبب.

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

باختصار، هنا ما يجري (تحت الغطاء) لطلب صورة: الذاكرة -> القرص -> الشبكة.

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

لذا كيف يمكنك تعطيل التخزين المؤقت؟

يمكنك تجنب تخزين الذاكرة المؤقت عن طريق استدعاء memoryPolicy (MemoryPolicy.NO_CACHE). سيؤدي ذلك ببساطة إلى تخطي البحث في ذاكرة التخزين المؤقت عند معالجة طلب صورة.

1
Picasso.get()
2
        .load(sunsetPhoto.url)
3
        .placeholder(R.drawable.placeholder)
4
        .error(R.drawable.error)
5
        .fit()
6
        .memoryPolicy(MemoryPolicy.NO_CACHE)
7
        .into(imageView)

لاحظ أن هناك اعدادًا آخرًا: MemoryPolicy.NO_STORE. يكون هذا مفيدًا إذا كنت متأكدًا تمامًا من أنك ستطلب صورة مرة واحدة فقط. لن يؤدي تطبيق ذلك إلى تخزين الصورة في ذاكرة التخزين المؤقت لذا لا يتم إخراج الصور النقطية الأخرى من ذاكرة التخزين المؤقت.

ولكن عليك أن تدرك تمامًا أن الصورة ستظل مخزنة مؤقتًا على القرص - لمنع ذلك أيضًا، فإنك تستخدم (@NonNull NetworkPolicy policy, @NonNull NetworkPolicy... additional)، والذي يأخذ واحدًا أو أكثر من قيم الاعداد التالية:

  • NetworkPolicy.NO_CACHE: يتخطى التحقق من ذاكرة التخزين المؤقت على القرص ويجبر التحميل عبر الشبكة.
  • NetworkPolicy.NO_STORE: يتخطى تخزين النتيجة في ذاكرة التخزين المؤقت على القرص.
  • NetworkPolicy.OFFLINE: فرض الطلب من خلال ذاكرة التخزين المؤقت على القرص فقط، وتخطي الشبكة.

لتجنب التخزين المؤقت للذاكرة والقرص تمامًا، ما عليك سوى استدعاء كلتا الطريقتين واحدة تلو الأخرى:

1
Picasso.get()
2
        .load(sunsetPhoto.url)
3
        .placeholder(R.drawable.placeholder)
4
        .error(R.drawable.error)
5
        .fit()
6
        .memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
7
        .networkPolicy(NetworkPolicy.NO_CACHE)
8
        .into(imageView)

13. ​​المستمعين للطلب

في بيكاسو، يمكنك تنفيذ مستمع أو رد اتصال لمراقبة حالة الطلب الذي قمت به أثناء تحميل الصورة. سيتم استدعاء إحدى هذه الطرق فقط إذا قمت بتطبيق واجهة الهدف على طلب ما.

  •  void onBitmapFailed(e: Exception?, errorDrawable: Drawable?)يتم تشغيلها عندما يتعذر تحميل الصورة بنجاح. هنا، يمكننا الوصول إلى الاستثناء الذي تم طرحه.
  • void onBitmapLoaded (صورة نقطية نقطية ، LoadedFrom from): يتم إطلاقها كلما تم تحميل صورة بنجاح. هنا، نحصل على صورة نقطية لإظهار المستخدم.
  • void onPrepareLoad (drawable placeHolderDrawable): استدعاء قبل تقديم طلبك.
1
Picasso.get()
2
        .load(sunsetPhoto.url)
3
        .placeholder(R.drawable.placeholder)
4
        .error(R.drawable.error)
5
        .into(object : Target {
6
            
7
            override fun onPrepareLoad(placeHolderDrawable: Drawable?) {
8
            }
9
10
            override fun onBitmapFailed(e: Exception?, errorDrawable: Drawable?) {
11
            }
12
13
            override fun onBitmapLoaded(bitmap: Bitmap?, from: Picasso.LoadedFrom?) {
14
            }
15
        })

هنا يمكنك أيضًا إظهار مربع حوار التقدم ثم إخفائه إذا كان لديك واحدًا.

يوجد مستمع آخر لاستدعاء اتصال به يمكنك تنفيذه إذا أردت، يسمى Callback. تحتوي هذه الواجهة على طريقتين فقط: onSuccess () و onError (استثناء e). يتم استدعاء الأولى عند تحميل طلب الصورة بنجاح، ويتم استدعاء الثانية عندما يكون هناك خطأ في معالجة الطلب.

بالعودة إلى تطبيق معرض الصور لدينا (داخل SunsetPhotoActivity)، دعنا نعدل العرض قليلاً باستخدام كائن Callback الذي سيؤدي إلى تعيين الصورة النقطية إلى ImageView وأيضاً تغيير لون الخلفية للتخطيط من خلال استخراج اللون المظلم والنابض بالحياة لصورتنا باستخدام واجهة برمجة تطبيقات Android Palette.

لذلك قم بتضمين لوحة الألوان في البنية الخاصة بوحدة التطبيق الخاصة بك.

1
dependencies {
2
    //... 

3
    implementation 'com.android.support:palette-v7:27.1.1'
4
}

دعنا الآن ننفذ واجهة رد الاتصال في طلب Picasso الخاص بنا.

1
override fun onStart() {
2
    super.onStart()
3
4
    Picasso.get()
5
            .load(sunsetPhoto.url)
6
            .placeholder(R.drawable.placeholder)
7
            .error(R.drawable.error)
8
            .fit()
9
            .into(imageView, object : Callback {
10
11
                override fun onSuccess() {
12
                    val bitmap = (imageView.drawable as BitmapDrawable).bitmap
13
                    onPalette(Palette.from(bitmap).generate())
14
                }
15
16
                override fun onError(e: Exception?) {
17
                }
18
            })
19
}
20
21
fun onPalette(palette: Palette?) {
22
    if (null != palette) {
23
        val parent = imageView.parent.parent as ViewGroup
24
        parent.setBackgroundColor(palette.getDarkVibrantColor(Color.GRAY))
25
    }
26
}

14. اختبار التطبيق

أخيرا، يمكنك تشغيل التطبيق! انقر على الصورة المصغرة للحصول على نسخة كاملة الحجم من الصورة.

Final app resultFinal app resultFinal app result

15. تحديد أولويات الطلبات

عندما تريد تحميل صور مختلفة في نفس الوقت على نفس الشاشة، لديك الخيار لترتيب أيهما أكثر أهمية من الآخر. بمعنى آخر، يمكنك تحميل الصور المهمة أولاً.

يمكنك ببساطة تسمية الأولوية () على مثيل طلب بيكاسو وتمريره في أي من الاعدادات: منخفض الاولوية أو أولوية عادية أو عالي الاولوية.

1
Picasso.get()
2
        .load(sunsetPhoto.url)
3
        .placeholder(R.drawable.placeholder)
4
        .error(R.drawable.error)
5
        .fit()
6
        .priority(Picasso.Priority.HIGH)
7
        .into(imageView)
8
9
Picasso.get()
10
        .load(sunsetPhoto.url)
11
        .placeholder(R.drawable.placeholder)
12
        .error(R.drawable.error)
13
        .fit()
14
        .priority(Picasso.Priority.NORMAL)
15
        .into(imageView)
16
17
Picasso.get()
18
        .load(sunsetPhoto.url)
19
        .placeholder(R.drawable.placeholder)
20
        .error(R.drawable.error)
21
        .fit()
22
        .priority(Picasso.Priority.LOW)
23
        .into(imageView)

16. وسم الطلبات

بوسم طلبات Picasso، يمكنك استئناف الطلبات أو إيقافها مؤقتًا أو إلغاءها والتي ترتبط بوسم محدد. استنادًا إلى حالة الاستخدام، يمكنك وسم  طلباتك باستخدام سلسلة أو كائنات يجب أن تحدد نطاق الطلب على أنه محتوى أو نشاط أو جزء. يمكنك بسهولة وسم طلب بيكاسو عن طريق الاتصال بالعلامة (NonNull Object tag) على أحدها. قم بتمريرها على سبيل المثال للكائن الذي يعمل كوسم.

في ما يلي العمليات التالية التي يمكنك تنفيذها على طلبات Picasso الموسومة:

  • pauseTag (وسم الكائن): إيقاف جميع الطلبات المرتبطة بالعلامة المحددة مؤقتًا.
  • resumeTag (وسم الكائن): استئناف الطلبات المتوقفة مؤقتًا باستخدام العلامة المحددة.
  • cancelTag (وسم الكائن): قم بإلغاء أي طلبات موجودة بالعلامة المحددة.
1
Picasso.get()
2
        // ...

3
        .tag(context

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

ستحتفظ بيكاسو بإشارة إلى العلامة طالما أن هذه العلامة متوقفة مؤقتًا و / أو تحتوي على طلبات نشطة. ابحث عن التسريبات المحتملة.

تحميل من نظام الملفات

من السهل تحميل الصور محليًا في تطبيقك.

1
File file = new File("your/pic/file/path/file.png")
2
Picasso.get()
3
        .load(file)
4
        .fit()
5
        .into(imageView)

استنتاج

عمل رائع! في هذا البرنامج التعليمي، قمت بإنشاء تطبيق معرض صور كامل مع بيكاسو، ومن خلال البرنامج تعلمت كيفية عمل المكتبة وكيف يمكنك دمجها في مشروعك الخاص.

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

لمعرفة المزيد عن بيكاسو، يمكنك الرجوع إلى وثائقها الرسمية. لمعرفة المزيد حول الترميز لنظام Android، تحقق من بعض الدورات والبرامج التعليمية الأخرى هنا على Envato Tuts +!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.