التنسيقات في طرق العرض

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

يحدّد التنسيق بنية واجهة المستخدم في تطبيقك، كما هو الحال في نشاط. يتم إنشاء جميع العناصر في التنسيق باستخدام تسلسل هرمي للكائنات 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().

يُعرف الزوج الثاني باسم width وheight، أو أحيانًا باسم drawing width وdrawing height. تحدد هذه الأبعاد الحجم الفعلي لطريقة العرض على الشاشة، في وقت الرسم وبعد التخطيط. قد تختلف هذه القيم، ولكن ليس من الضروري، عن العرض والارتفاع اللذين تم قياسهما. يمكنك الحصول على العرض والارتفاع عن طريق طلب بيانات من 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.