إضافات أندرويد للغة البرمجة Kotlin


محمد الميداوي

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

ربط العرض View Binding

الخلفية

كل مطوري أندرويد يعرفون جيدًا الدالة ()findViewById. والتي هي من دون أدنى شك، مصدر لكثير من المتاعب والأخطاء المحتملة والشيفرات السيئة والتي يصعب قراءتها وصيانتها. صحيح أن هناك العديد من المكتبات المتاحة لتوفير حلول لهذه المشكلة، إلّا أن هذه المكتبات تتطلب حقول تأشير annotating fields لكل عنصر معروض من نوع View.
توفر لنا ملحقات أندرويد الإضافية لـ Kotlin تجربة مماثلة لما توفره بعض تلك المكتبات، دون أن نكون في حاجة إلى كتابة شيفرات إضافية.
في الأساس، هذا يسمح لنا بكتابة الشيفرة التالية:

// Using R.layout.activity_main from the 'main' source set
import kotlinx.android.synthetic.main.activity_main.*

class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // Instead of findViewById<TextView>(R.id.textView)
        textView.setText("Hello, world!")
    }
}

textView هي خاصية إضافية لـ Activity، ولها نفس النوع المعلن في activity_main.xml (أي TextView).

استخدام إضافات أندرويد لـKotlin

إعداد الارتباطات Configuring the Dependency

سنستخدم في هذا الدرس Gradle، لكن يمكنك تحقيق نفس النتائج باستخدام IntelliJ IDEA project structure أو Maven.
إضافات أندرويد هي جزء من ملحقة Kotlin الخاصة بكل من IntelliJ IDEA وAndroid Studio. لذلك لا تحتاج إلى تثبيت ملحقات إضافية.
كل ما تحتاجه هو إتاحة ملحقة Gradle لأندرويد في ملف الوحدة build.gradle:

apply plugin: 'kotlin-android-extensions'

استيراد الخصائص التركيبية synthetic properties

من الملائم استيراد جميع خصائص الودجةwidget) ) لخطاطة (layout) معينة دفعة واحدة:

import kotlinx.android.synthetic.main.<layout>.*

وهكذا إذا كان اسم ملف الخطاطة هو activity_main.xml، فسنقوم باستيراد

kotlinx.android.synthetic.main.activity_main.*

إن كنّا نريد أن نستدعي الخصائص التركيبية على View، فيجب علينا أيضًا استيراد

kotlinx.android.synthetic.main.activity_main.view.*

وبمجرد أن نفعل ذلك، يمكننا حينها استدعاء الإضافات المقابلة والتي هي اسماء خصائص سُمّيت على إثر عناصر العرضviews الموجودة في ملف XML. فعلى سبيل المثال، بالنسبة لهذا العرض:

    <TextView
        android:id="@+id/hello"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

ستكون هناك خاصية اسمها hello:

activity.hello.text = "Hello World!"

الوضع التجريبي Experimental Mode

تشمل الملحقات الإضافية لأندرويد العديد من الميزات التجريبية مثل دعم LayoutContainer ومولّدات تقديم الصنف Parcelable (Parcelable implementation generator). هذه الميزات لا تُعتبر جاهزة بعدُ للإنتاج، لذلك نحتاج إلى التحوّل للوضع التجريبي في build.gradle من أجل استخدامها:

androidExtensions {
    experimental = true
}

دعم LayoutContainer

تدعم الملحقات الإضافية لأندرويد أنواع مختلفة من الحاويات containers. وأبسط تلك الحاويات Activity، Fragment وView. ولكن يمكنك أن تحوّل (افتراضيًا) أي صنف إلى حاوية لإضافات أندرويد من خلال تطبيق الواجهةLayoutContainer ، على سبيل المثال:

import kotlinx.android.extensions.LayoutContainer

class ViewHolder(override val containerView: View) : ViewHolder(containerView), LayoutContainer {
    fun setup(title: String) {
        itemTitle.text = "Hello World!"
    }
}

لاحظ أنك تحتاج إلى التحوّل إلى الوضع التجريبي لاستخدام LayoutContainer.

دعم النكهات Flavor Support

تدعم ملحقات أندرويد الإضافية نكهات أندرويد ((Android flavors. لنفترض أن لديك نكهة اسمها free في ملف build.gradle خاصّتك:

android {
    productFlavors {
        free {
            versionName "1.0-free"
        }
    }
}

يمكنك استيراد كافة الخصائص التركيبية للخطاطة free/res/layout/activity_free.xml بإضافة هذا الاستيراد:

import kotlinx.android.synthetic.free.activity_free.*


في الوضع التجريبي، يمكنك تحديد أي اسم آخر (وليس فقط flavor)، على سبيل المثال freeDebug أو freeRelease يصلحان كذلك.

التخزين المؤقت لعناصرView

استدعاء ()findViewById يمكن أن يكون بطيئًا، خصوصًا في حالة تشعبات العرض (view hierarchies) الكبيرة، لذلك تحاول إضافات أندرويد التقليل من عدد مرّات استدعاء ()findViewById بواسطة التخزين المؤقت للعروض في الحاويات.
افتراضيًا، اضافات أندرويد تضيف دالة تخزين مؤقت مخفية وحقل تخزين إلى كل حاوية (Activity، Fragment، View أو LayoutContainer implementation) مكتوبة بـ Kotlin. التابع method)) صغير جدًا لذلك لا يزيد حجم APK كثيرًا.
في المثال التالي، يتم استدعاء ()findViewById مرة واحدة فقط:

class MyActivity : Activity()

fun MyActivity.a() { 
    textView.text = "Hidden view"
    textView.visibility = View.INVISIBLE
}

لكن في الحالة التالية:

fun Activity.b() { 
    textView.text = "Hidden view"
    textView.visibility = View.INVISIBLE
}

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

تغيير استراتيجية التخزين المؤقت للصنف View

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

androidExtensions {
    defaultCacheImplementation = "HASH_MAP" // also SPARSE_ARRAY, NONE
}

افتراضيًا، الملحقات الإضافية لأندرويد تستخدم HashMap كمرجع احتياطي للتخزين، ولكن يمكنك التبديل لتطبيق SparseArray، أو إيقاف التخزين المؤقت وحسب. هذا الأخير مفيد بشكل خاص إن أردت الاكتفاء باستخدام الجزء المقسّم Parcelable من إضافات Android.
يمكنك أيضًا التأشير على حاوية ما بـ ContainerOptions@ لتغيير استراتيجية التخزين المؤقت:

import kotlinx.android.extensions.ContainerOptions

@ContainerOptions(cache = CacheImplementation.NO_CACHE)
class MyActivity : Activity()

fun MyActivity.a() { 
    // findViewById() will be called twice
    textView.text = "Hidden view"
    textView.visibility = View.INVISIBLE
}

Parcelable

بدءًا من الإصدار Kotlin 1.1.4، وفّرت الملحقات الإضافية لأندرويد مولّدات تطبيق للصنف Parcelable كميزة تجريبية.

إتاحة دعم Parcelable

قم بتطبيق ملحقة Gradle المسمّاة kotlin-android-extensions كما هو موضح [أعلاه] (#إعداد الارتباطات) وقم بتشغيل الوضع التجريبي.

كيفية الاستخدام

قم بالتأشير على الصنف بـ Parcelize@، وسيتم إنشاء تطبيق Parcelable تلقائيًا.

import kotlinx.android.parcel.Parcelize

@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable

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

@Parcelize
data class Value(val firstName: String, val lastName: String, val age: Int) : Parcelable {
    private companion object : Parceler<User> {
        override fun User.write(parcel: Parcel, flags: Int) {
            // Custom write implementation
        }

        override fun create(parcel: Parcel): User {
            // Custom read implementation
        }
    }
}

الأنواع المدعومة

يدعم Parcelize@ طيفًا واسعًا من الأنواع:

  • الأنواع الأولية Primitive types (ونسخها المغلّفة boxed versions).
  • Objects وenums .
  • String، CharSequence.
  • Exception.
  • Size، SizeF، Bundle، IBinder، IInterface، FileDescriptor
  • SparseArray، SparseIntArray، SparseLongArray، SparseBooleanArray.
  • كل الأنواع المتسلسلة Serializable (حتى Date مدعوم) وتطبيقات Parcelable.
  • تجميعات كل الأنواع المدعومة: List (مُحالة على ArrayList)، و Set (مُحالة على LinkedHashSet)، و Map (مُحالة على LinkedHashMap).
  • بالإضافة إلى عدد من التطبيقات الملموسة: ArrayList، LinkedList، SortedSet، NavigableSet، HashSet، LinkedHashSet، TreeSet، SortedMap، NavigableMap، HashMap، LinkedHashMap، TreeMap، ConcurrentHashMap.
  • الجداول التي تحتوي الأنواع المدعومة.
  • النسخ الفارغة Nullable versions من كل الأنواع المدعومة.

تخصيص الـ Parcelers

حتى إن لم يكن النوع مدعوما مباشرة، يمكنك كتابة كائن Parceler لأجل دعمه.

class ExternalClass(val value: Int)

object ExternalClassParceler : Parceler<ExternalClass> {
    override fun create(parcel: Parcel) = ExternalClass(parcel.readInt())

    override fun ExternalClass.write(parcel: Parcel, flags: Int) {
        parcel.writeInt(value)
    }
}

أمّا عناصر Parcelers الخارجية يمكن تطبيقها باستخدام التأشيرات TypeParceler@ أو WriteWith@:

// Class-local parceler
@Parcelable
@TypeParceler<ExternalClass, ExternalClassParceler>()
class MyClass(val external: ExternalClass)

// Property-local parceler
@Parcelable
class MyClass(@TypeParceler<ExternalClass, ExternalClassParceler>() val external: ExternalClass)

// Type-local parceler
@Parcelable
class MyClass(val external: @WriteWith<ExternalClassParceler>() ExternalClass)

ترجمة -وبتصرّف- للمقال Kotlin Android Extensions لصاحبه Yan Zhulanow





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن