التصاميم في طرق العرض

تجربة طريقة "الكتابة"
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم المقترَحة لنظام التشغيل Android. تعرَّف على كيفية استخدام التنسيقات في ميزة "الكتابة الذكية".

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

الشكل 1. صورة توضيحية لهيكلية طرق العرض التي تحدّد تصميم واجهة المستخدم

يُطلق على عناصر View غالبًا اسم عناصر واجهة المستخدم، ويمكن أن تكون إحدى الفئات الفرعية العديدة، مثل Button أو TextView. تُعرف عناصر ViewGroup عادةً باسم التصاميم، ويمكن أن تكون أحد الأنواع العديدة التي توفّر بنية تصميم مختلفة، مثل LinearLayout أو ConstraintLayout.

يمكنك تعريف تنسيق بطريقتَين:

  • تحديد عناصر واجهة المستخدم في XML: يوفر نظام التشغيل Android مجموعة بسيطة من مفردات XML تتوافق مع فئات View وفئاتها الفرعية، مثل تلك الخاصة بالأدوات والتصاميم. يمكنك أيضًا استخدام أداة تعديل التنسيق في "استوديو Android" لإنشاء تصميم XML باستخدام واجهة السحب والإفلات.

  • إنشاء عناصر التنسيق في وقت التشغيل: يمكن لتطبيقك إنشاء كائنَي View وViewGroup ومعالجة خصائصهما آليًا.

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

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

كتابة XML

باستخدام مفردات XML في Android، يمكنك تصميم تخطيطات واجهة المستخدم وعناصر الشاشة التي تتضمّنها بسرعة، وذلك بالطريقة نفسها التي تنشئ بها صفحات الويب في HTML باستخدام سلسلة من العناصر المتداخلة.

يجب أن يحتوي كل ملف تخطيط على عنصر جذر واحد بالضبط، ويجب أن يكون هذا العنصر كائن View أو ViewGroup. بعد تحديد العنصر الجذر، يمكنك إضافة عناصر أو أدوات تخطيط إضافية كعناصر فرعية لإنشاء تسلسل هرمي View يحدّد التخطيط تدريجيًا. على سبيل المثال، إليك تصميم XML يستخدم LinearLayout عموديًا لاحتواء TextView وButton:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

بعد تعريف التصميم في XML، احفظ الملف بالامتداد .xml في دليل res/layout/ ضمن مشروع Android لكي يتم تجميعه بشكل صحيح.

لمزيد من المعلومات حول بنية ملف XML للتصميم، يُرجى الاطّلاع على مورد التصميم.

تحميل مرجع XML

عند تجميع تطبيقك، يتم تجميع كل ملف XML للتصميم في Viewأحد الموارد. حمِّل مورد التصميم في عملية تنفيذ معاودة الاتصال Activity.onCreate() في تطبيقك. يمكنك إجراء ذلك من خلال استدعاء setContentView()، وتمرير المرجع إلى مورد التنسيق الخاص بك بالشكل التالي: R.layout.layout_file_name. على سبيل المثال، إذا تم حفظ تنسيق XML باسم main_layout.xml، يمكنك تحميله لـ Activity على النحو التالي:

Kotlin

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

Java

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

يستدعي إطار عمل Android طريقة رد الاتصال onCreate() في Activity عند تشغيل Activity. لمزيد من المعلومات حول دورات حياة الأنشطة، يُرجى الاطّلاع على مقدمة حول الأنشطة.

السمات

يتوافق كل عنصر View وViewGroup مع مجموعة متنوعة من سمات XML. بعض السمات خاصة بكائن View. على سبيل المثال، تتيح TextView استخدام السمة textSize. ومع ذلك، يتم أيضًا توريث هذه السمات من خلال أي View عناصر توسّع هذه الفئة. بعضها مشترك بين جميع عناصر View، لأنّها موروثة من فئة View الجذر، مثل السمة id. تُعدّ السمات الأخرى مَعلمات التصميم، وهي سمات تصف بعض اتجاهات التصميم للعنصر View، كما هو محدّد بواسطة العنصر ViewGroup الأساسي لذلك العنصر.

رقم التعريف

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

android:id="@+id/my_button"

يشير الرمز at (@) في بداية السلسلة إلى أنّ محلّل XML يحلّل بقية سلسلة المعرّف ويوسّعها ويحدّدها على أنّها مورد معرّف. يشير الرمز زائد (+) إلى أنّ هذا الاسم هو اسم مورد جديد يجب إنشاؤه وإضافته إلى الموارد في ملف R.java.

يوفّر إطار عمل Android العديد من موارد أرقام التعريف الأخرى. عند الإشارة إلى معرّف مورد Android، لن تحتاج إلى الرمز زائد، ولكن عليك إضافة مساحة اسم الحزمة android على النحو التالي:

android:id="@android:id/empty"

يشير مساحة الاسم للحزمة android إلى أنّك تشير إلى معرّف من فئة الموارد android.R، وليس من فئة الموارد المحلية.

لإنشاء طرق عرض والإشارة إليها من تطبيقك، يمكنك استخدام نمط شائع كما يلي:

  1. حدِّد طريقة العرض في ملف التنسيق وأسنِد إليها رقم تعريف فريدًا، كما في المثال التالي:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
  2. أنشئ مثيلاً لعنصر العرض واحصل عليه من التصميم، عادةً في الإجراء onCreate() كما هو موضّح في المثال التالي:

    Kotlin

    val myButton: Button = findViewById(R.id.my_button)

    Java

    Button myButton = (Button) findViewById(R.id.my_button);

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

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

مَعلمات التنسيق

تحدّد سمات تنسيق XML التي تحمل الاسم layout_something مَعلمات التنسيق الخاصة بـ View والتي تكون مناسبة لـ ViewGroup الذي يقع فيه.

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

الشكل 2. تصوّر هيكلية طرق العرض مع مَعلمات التنسيق المرتبطة بكل طريقة عرض.

لكل فئة فرعية LayoutParamsبنية خاصة لتحديد القيم. يجب أن يحدّد كل عنصر ثانوي نوع LayoutParams مناسبًا للعنصر الرئيسي، مع أنّه قد يحدّد أيضًا نوع LayoutParams مختلفًا للعناصر الثانوية الخاصة به.

تتضمّن جميع مجموعات طرق العرض عرضًا وارتفاعًا، باستخدام layout_width وlayout_height، ويجب أن تحدّد كل طريقة عرض هذه القيم. يتضمّن العديد من LayoutParams هوامش وحدودًا اختيارية.

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

  • wrap_content: يطلب من العرض تغيير حجمه ليناسب الأبعاد التي يتطلبها المحتوى.
  • match_parent: يطلب من العرض أن يصبح كبيرًا قدر ما تسمح به مجموعة طرق العرض الرئيسية.

بشكل عام، لا ننصح بتحديد عرض وارتفاع التنسيق باستخدام وحدات مطلقة، مثل وحدات البكسل. والطريقة الأفضل هي استخدام القياسات النسبية، مثل وحدات البكسل المستقلة الكثافة (dp) أو wrap_content أو match_parent، لأنّها تساعد تطبيقك على العرض بشكل صحيح على مجموعة متنوعة من أحجام شاشات الأجهزة. يتم تحديد أنواع القياس المقبولة في مصدر التنسيق.

موضع التنسيق

يحتوي العرض على شكل هندسي مستطيل. ويحتوي على موقع جغرافي، يتم التعبير عنه كزوج من إحداثيات اليسار والأعلى، وبُعدَين، يتم التعبير عنهما كعرض وارتفاع. وحدة الموقع الجغرافي والأبعاد هي البكسل.

يمكنك استرداد الموقع الجغرافي لعنصر عرض من خلال استدعاء الطريقتَين getLeft() و getTop(). تعرض الدالة الأولى إحداثي اليسار (x) للمستطيل الذي يمثّل العرض. تعرض السمة الأخيرة إحداثي أعلى (y) المستطيل الذي يمثّل طريقة العرض. تعرض هذه الطرق الموقع الجغرافي للعرض بالنسبة إلى العنصر الرئيسي. على سبيل المثال، عندما تعرض getLeft() القيمة 20، يعني ذلك أنّ العرض يقع على بُعد 20 بكسل إلى يسار الحافة اليسرى للعنصر الأصل المباشر.

بالإضافة إلى ذلك، هناك طرق ملائمة لتجنُّب العمليات الحسابية غير الضرورية، وهي getRight() و getBottom(). تعرض هاتان الطريقتان إحداثيات الحافتين اليمنى والسفلى للمستطيل الذي يمثّل طريقة العرض. على سبيل المثال، استدعاء getRight() يشبه العملية الحسابية التالية: getLeft() + getWidth().

الحجم والمساحة المتروكة والهوامش

يتم التعبير عن حجم العرض باستخدام العرض والارتفاع. يحتوي العرض على مجموعتَين من قيم العرض والارتفاع.

يُعرف الزوج الأول باسم العرض المقاس والارتفاع المقاس. تحدّد هذه السمات حجم العرض المطلوب ضمن العنصر الرئيسي. يمكنك الحصول على السمات التي تم قياسها من خلال استدعاء getMeasuredWidth() و getMeasuredHeight().

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

لقياس أبعاد العرض، يتم أخذ المساحة المتروكة في الاعتبار. يتم التعبير عن المساحة المتروكة بالبكسل للأجزاء الأيمن والأيسر والعلوي والسفلي من العرض. يمكنك استخدام المساحة المتروكة لإزاحة محتوى العرض بمقدار محدّد من وحدات البكسل. على سبيل المثال، يؤدي ترك مساحة فارغة على اليمين بمقدار وحدتَين إلى إزاحة محتوى العرض بمقدار وحدتَين إلى يسار الحافة اليمنى. يمكنك ضبط مساحة العرض باستخدام الطريقة setPadding(int, int, int, int) والاستعلام عنها من خلال استدعاء getPaddingLeft() وgetPaddingTop() وgetPaddingRight() وgetPaddingBottom().

على الرغم من أنّ طريقة العرض يمكنها تحديد مساحة متروكة، إلا أنّها لا تتوافق مع الهوامش. ومع ذلك، تتيح مجموعات العرض استخدام الهوامش. يمكنك الاطّلاع على ViewGroup و ViewGroup.MarginLayoutParams لمزيد من المعلومات.

لمزيد من المعلومات عن السمات، يُرجى الاطّلاع على السمة.

بالإضافة إلى ضبط الهوامش الداخلية والخارجية آليًا، يمكنك أيضًا ضبطها في تنسيقات XML، كما هو موضّح في المثال التالي:

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
      <TextView android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

يوضّح المثال السابق كيفية تطبيق الهامش الداخلي والخارجي. يتم تطبيق هوامش وتعبئة موحّدة على جميع الجوانب في TextView، بينما يوضّح Button كيفية تطبيقها بشكل مستقل على حواف مختلفة.

التنسيقات الشائعة

تقدّم كل فئة فرعية من الفئة ViewGroup طريقة فريدة لعرض طرق العرض التي تضمّنها. ‫ConstraintLayout هو نوع التنسيق الأكثر مرونة، وهو يوفّر أفضل الأدوات للحفاظ على تدرّج هرمي بسيط للتنسيق.

في ما يلي بعض أنواع التصميمات الشائعة المضمّنة في منصة Android.

إنشاء تنسيق خطي

تنظّم العناصر الثانوية في صف أفقي أو عمودي واحد وتنشئ شريط تمرير إذا تجاوز طول النافذة طول الشاشة.

إنشاء تطبيقات ويب في WebView

تعرِض هذه السمة صفحات الويب.

إنشاء قوائم ديناميكية

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

تشمل التنسيقات الشائعة المتاحة باستخدام RecyclerView وAdapterView ما يلي:

القائمة

تعرض قائمة بعمود واحد يمكن التنقّل فيه.

الشبكة

تعرِض هذه السمة شبكة قابلة للتمرير تتضمّن أعمدة وصفوفًا.

يتيح RecyclerView المزيد من الإمكانات وإنشاء أداة مخصّصة لإدارة التنسيق.

ملء طريقة عرض المحوّل بالبيانات

يمكنك ملء AdapterView مثل ListView أو GridView من خلال ربط مثيل AdapterView بـ Adapter، الذي يسترد البيانات من مصدر خارجي وينشئ View يمثّل كل إدخال بيانات.

يوفّر Android عدة فئات فرعية من Adapter مفيدة لاسترداد أنواع مختلفة من البيانات وإنشاء طرق عرض لعنصر AdapterView. المحوّلان الأكثر شيوعًا هما:

ArrayAdapter
استخدِم هذا المحوّل عندما يكون مصدر البيانات عبارة عن مصفوفة. تنشئ ArrayAdapter تلقائيًا طريقة عرض لكل عنصر من عناصر المصفوفة من خلال استدعاء toString() لكل عنصر ووضع المحتوى في TextView.

على سبيل المثال، إذا كانت لديك مصفوفة من السلاسل النصية التي تريد عرضها في ListView، عليك تهيئة ArrayAdapter جديد باستخدام دالة إنشاء لتحديد التنسيق لكل سلسلة نصية ومصفوفة السلاسل النصية:

Kotlin

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    

Java

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

في ما يلي وسيطات الدالة الإنشائية:

  • تطبيقك Context
  • التنسيق الذي يحتوي على TextView لكل سلسلة في المصفوفة
  • مصفوفة السلاسل النصية

بعد ذلك، اتّصِل بالرقم setAdapter() على جهازك ListView:

Kotlin

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    

Java

    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

لتخصيص مظهر كل عنصر، يمكنك إلغاء طريقة toString() للعناصر في المصفوفة. أو لإنشاء طريقة عرض لكل عنصر ليس TextView، مثلاً إذا كنت تريد ImageView لكل عنصر في المصفوفة، يمكنك توسيع الفئة ArrayAdapter وتجاوز getView() لعرض نوع طريقة العرض التي تريدها لكل عنصر.

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

Kotlin

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    

Java

    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

عند إنشاء مثيل SimpleCursorAdapter، مرِّر التنسيق الذي سيتم استخدامه لكل نتيجة، وCursor الذي يحتوي على النتائج، وهاتين المصفوفتين:

Kotlin

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    

Java

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    ListView listView = getListView();
    listView.setAdapter(adapter);
    

بعد ذلك، ينشئ SimpleCursorAdapter طريقة عرض لكل صف في Cursor باستخدام التنسيق المقدَّم من خلال إدراج كل عنصر fromColumns في طريقة العرض toViews المقابلة.

إذا غيّرت خلال فترة استخدام تطبيقك البيانات الأساسية التي يقرأها المحوّل، عليك استدعاء notifyDataSetChanged(). يُعلم هذا الإجراء العرض المرفق بأنّه تم تغيير البيانات، ويعيد تحميلها.

التعامل مع أحداث النقر

يمكنك الاستجابة لأحداث النقر على كل عنصر في AdapterView من خلال تنفيذ واجهة AdapterView.OnItemClickListener. على سبيل المثال:

Kotlin

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}

Java

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click.
    }
};

listView.setOnItemClickListener(messageClickedHandler);

مراجع إضافية

يمكنك الاطّلاع على كيفية استخدام التنسيقات في تطبيق Sunflower التجريبي على GitHub.