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

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