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

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

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

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

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

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

  • تعريف عناصر واجهة المستخدم في XML: يوفر نظام التشغيل Android مجموعة بسيطة من مفردات XML تتوافق مع فئات View وفئاتها الفرعية، مثل تلك الخاصة بالأدوات والتنسيقات. يمكنك أيضًا استخدام أداة Layout Editor في "استوديو 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"

يشير الرمز @ في بداية السلسلة إلى أنّ محلّل 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.