إنشاء مكوّنات العرض المخصّصة

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

يوفر Android نموذجًا متطورًا وفعّالًا ومقسَّمًا إلى مكوّنات لإنشاء واجهة المستخدم استنادًا إلى فئات التنسيق الأساسية View وViewGroup. تتضمّن المنصة مجموعة متنوعة من الفئتَين الفرعيتَين View وViewGroup اللذين تم إنشاؤهما مسبقًا، وهما تُسمى الأدوات والتنسيقات، على التوالي، والتي يمكنك استخدامها لإنشاء واجهة المستخدم.

تشتمل القائمة الجزئية من التطبيقات المصغّرة المتاحة على Button وTextView وEditText وListView وCheckBox وRadioButton وGallery Spinner وغيرها من التطبيقات المصغّرة ذات الأغراض الخاصة AutoCompleteTextView وImageSwitcher وTextSwitcher.

من بين التنسيقات المتاحة LinearLayout وFrameLayout وRelativeLayout وغيرها. للاطّلاع على مزيد من الأمثلة، راجِع التنسيقات الشائعة.

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

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

  • يمكنك إنشاء نوع View معروض مخصّص بالكامل، على سبيل المثال، مقبض مقبض "للتحكّم في الصوت"، يتم عرضه باستخدام رسومات ثنائية الأبعاد، يشبه عنصر التحكّم الإلكتروني التناظري.
  • يمكنك دمج مجموعة من مكوّنات View في مكوّن واحد جديد، ربما من أجل إنشاء عنصر مثل مربع التحرير والسرد (مزيج من قائمة منبثقة وحقل نص الإدخال المجاني) وعنصر تحكّم ثنائي اللوحة (جزء لليسار ولليمين مع قائمة في كل مكان يمكنك إعادة تخصيص العنصر فيه لكل قائمة) وما إلى ذلك.
  • يمكنك إلغاء طريقة عرض مكوّن EditText على الشاشة. ويستخدم نموذج تطبيق NotePad هذا الخيار لإنشاء صفحة مفكرة مبطّنة.
  • يمكنك تسجيل أحداث أخرى، مثل الضغطات على المفاتيح، والتعامل معها بطريقة مخصّصة، مثل وقوع لعبة.

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

تم تحديد النهج الأساسي

إليك نظرة عامة عالية المستوى حول ما تحتاج إلى معرفته لإنشاء مكوّنات View الخاصة بك:

  1. يمكنك تمديد صف أو فئة فرعية حالية View من خلال صفك الخاص.
  2. تجاوز بعض الطرق من الفئة الفائقة. تبدأ الطرق المميّزة التي يتم إلغاؤها بـ on، مثل onDraw() وonMeasure() وonKeyDown(). هذا النوع من الأحداث مشابهًا لأحداث on في Activity أو ListActivity التي تتجاهلها خلال مراحل النشاط وغيرها من عناصر الجذب والوظائف الأخرى.
  3. استخدِم فئة الإضافات الجديدة. وبعد اكتمالها، يمكنك استخدام فئة الإضافة الجديدة بدلاً من طريقة العرض التي كانت تستند إليها.

مكونات مخصصة بالكامل

يمكنك إنشاء مكونات رسومية مخصصة بالكامل تظهر بالطريقة التي تريدها. قد تحتاج إلى مقياس VU رسومي يشبه المقياس التناظري القديم، أو عرض نص يغنّي فيها كرة مرتدة من الكلمات أثناء الغناء مع آلة الكاريوكي. قد ترغب في شيء لا تستطيع المكونات المضمنة فعله، بغض النظر عن كيفية دمجها.

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

لإنشاء مكوِّن مخصص بالكامل، ضع في اعتبارك ما يلي:

  • طريقة العرض الأكثر عمومية التي يمكنك تمديدها هي View، لذا عليك عادةً البدء بتمديدها لإنشاء مكوّن Super جديد.
  • ويمكنك توفير دالة إنشاء يمكنها أخذ سمات ومعلَمات من XML، واستخدام هذه السمات والمعلَمات الخاصة بك، مثل لون مقياس VU ونطاقه، أو عرض الإبرة وتخميدها.
  • قد تحتاج إلى إنشاء أدوات معالجة الأحداث وأدوات الوصول إلى الموقع ومفاتيح التعديل الخاصة بك، بالإضافة إلى سلوك أكثر تعقيدًا في فئة المكوِّن.
  • أنت تريد بالتأكيد إلغاء السمة onMeasure() ومن المحتمل أيضًا أنك بحاجة إلى إلغاء onDraw() إذا أردت أن يعرض المكوِّن عنصرًا. وعلى الرغم من أنّ الإعدادَين يتضمّنان سلوكًا تلقائيًا، فإنّ onDraw() التلقائي لا يفعل أي شيء، وتضبط السمة التلقائية onMeasure() دائمًا الحجم 100×100، وهو أمر قد لا تحتاج إليه على الأرجح.
  • يمكنك أيضًا إلغاء طُرق on الأخرى، حسب الحاجة.

Extend onDraw() وonmeasure()

وتعرض الطريقة onDraw() Canvas يمكنك من خلاله تنفيذ أي شيء تريده: رسومات ثنائية الأبعاد أو مكوّنات أخرى عادية أو مخصّصة أو نصوص ذات نمط معيّن أو أي شيء آخر يخطر في بالك.

onMeasure() يشارك أكثر قليلاً. onMeasure() هو جزء مهم من عقد العرض بين المكوِّن وحاويةه. يجب إلغاء السمة onMeasure() للإبلاغ بكفاءة ودقة عن قياسات الأجزاء المضمّنة فيها. ويصبح هذا الأمر أكثر تعقيدًا إلى حدٍّ ما بسبب متطلبات الحد الأقصى التي يتم تطبيقها من المؤسسة الرئيسية، والتي يتم تمريرها إلى طريقة onMeasure()، ومن خلال اشتراط استدعاء طريقة setMeasuredDimension() بالعرض والارتفاع اللذين تم قياسهما بعد احتسابهما. إذا لم يتم استدعاء هذه الطريقة من طريقة onMeasure() تم تجاوزها، ينتج عنها استثناء في وقت القياس.

على مستوى عالٍ، يبدو تنفيذ onMeasure() على النحو التالي:

  • ويُطلق على طريقة onMeasure() التي تم تجاوزها مواصفات العرض والارتفاع التي يتم التعامل معها باعتبارها متطلبات للقيود المفروضة على قياسات العرض والارتفاع التي تنتجها. المَعلمتَان widthMeasureSpec وheightMeasureSpec هما رمزان صحيحان يمثّلان السمات. ويمكن العثور على مرجع كامل لنوع القيود التي يمكن أن تتطلبها هذه المواصفات في المستندات المرجعية ضمن View.onMeasure(int, int) يشرح هذا المستند المرجعي أيضًا عملية القياس بالكامل.
  • تحسب طريقة onMeasure() الخاصة بالمكوِّن قيمة عرض القياس وارتفاعه، وذلك لكي يتم عرض المكوِّن. يجب أن يحاول الالتزام بالمواصفات التي تم إدخالها، حتى لو كان يتجاوز المواصفات المحددة. في هذه الحالة، يمكن لأحد الوالدَين اختيار الإجراء المطلوب، بما في ذلك اقتصاص المحتوى أو الانتقال للأسفل أو طرح استثناء أو طلب إعادة المحاولة من onMeasure() باستخدام مواصفات قياس مختلفة.
  • عند احتساب العرض والارتفاع، يمكنك استدعاء الطريقة setMeasuredDimension(int width, int height) مع القياسات المحسوبة. ويؤدي عدم تنفيذ هذا الإجراء إلى الحصول على استثناء.

في ما يلي ملخّص للطرق العادية الأخرى التي يستدعيها إطار العمل لعدد المشاهدات:

الفئة الطرق الوصف
العائلة والمجتمع الشركات المصنِّعة هناك شكل من أشكال الدالة الإنشائية يتم استدعاؤه عند إنشاء طريقة العرض من الرمز البرمجي، ونموذج يتم استدعاءه عند تضخيم العرض من ملف تنسيق. ويحلّل النموذج الثاني السمات المحددة في ملف التنسيق ويطبّقها.
onFinishInflate() يتم استدعاؤه بعد رؤية كل العناصر المرئية وكل عناصرها الثانوية من ملف XML.
التنسيق onMeasure(int, int) تم استدعاء هذه الدالة لتحديد متطلبات الحجم لطريقة العرض هذه وكل عناصرها الثانوية.
onLayout(boolean, int, int, int, int) يتم استدعاء هذا الإجراء عندما يجب أن تحدِّد طريقة العرض هذه حجمًا وموضعًا لكل عناصرها الثانوية.
onSizeChanged(int, int, int, int) يتم استدعاء هذا الإجراء عند تغيير حجم طريقة العرض هذه.
رسم onDraw(Canvas) يتم استدعاء عندما يجب أن تعرض طريقة العرض المحتوى.
معالجة الأحداث onKeyDown(int, KeyEvent) يتم استدعاء عند وقوع حدث رئيسي.
onKeyUp(int, KeyEvent) يتم استدعاء هذا الإجراء عند وقوع حدث رئيسي.
onTrackballEvent(MotionEvent) يتم استدعاء عند حدوث حدث حركة كرة تعقب.
onTouchEvent(MotionEvent) يتم استدعاء هذا الإجراء عند حدوث حركة شاشة تعمل باللمس.
الموضوع الذي يركّز عليه الفيديو onFocusChanged(boolean, int, Rect) يتم استدعاء هذا الإجراء عند زيادة التركيز أو فقدانه في المشاهدة.
onWindowFocusChanged(boolean) يتم استدعاء عندما تكتسب النافذة التي تحتوي على طريقة العرض التركيز أو تفقد التركيز.
جارٍ الإرفاق onAttachedToWindow() يتم استدعاء هذا الإجراء عند تثبيت طبقة العرض بنافذة.
onDetachedFromWindow() يتم استدعاء هذا الإجراء عند فصل العرض عن نافذته.
onWindowVisibilityChanged(int) يتم استدعاء هذه الوظيفة عند تغيير مستوى رؤية النافذة التي تحتوي على طريقة العرض.

عناصر التحكم المركبة

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

في Android، تتوفّر طريقتا عرض أخريان لإجراء ذلك: Spinner وAutoCompleteTextView. بغض النظر، فإن هذا المفهوم الخاص بمربع التحرير والسرد يعد مثالاً جيدًا.

لإنشاء مكون مركب، قم بما يلي:

  • كما هي الحال في Activity، يمكنك استخدام أسلوب البيان (المستند إلى XML) لإنشاء المكوّنات المضمّنة أو دمجها آليًا من الرمز البرمجي. وتُعدّ نقطة البداية المعتادة Layout من نوع ما، لذا أنشئ فئة تمتد إلى Layout. في حال استخدام مربّع التحرير والسرد، يمكنك استخدام LinearLayout باتجاه أفقي. ويمكنك دمج تنسيقات أخرى داخلها، كي يكون المكوِّن المركّب معقدًا ومنظمًا بشكل عشوائي.
  • في الدالة الإنشائية للفئة الجديدة، استخدِم المَعلمات التي تتوقعها الفئة الفائقة وأدخِلها أولاً إلى الدالة الإنشائية للفئة الفائقة. بعد ذلك، يمكنك إعداد طرق العرض الأخرى لاستخدامها في المكوّن الجديد. في هذه الصفحة، يمكنك إنشاء الحقل EditText والقائمة المنبثقة. يمكنك إدخال سماتك ومعلَماتك الخاصة في XML الذي يمكن للأداة الإنشائية سحبها واستخدامها.
  • يمكنك اختياريًا إنشاء أدوات معالجة الأحداث للأحداث التي قد تنشئها طرق العرض المتضمّنة. ومن الأمثلة على ذلك طريقة الاستماع الخاصة بأداة استماع للنقرة على عنصر قائمة لتعديل محتوى EditText في حال اختيار قائمة.
  • يمكنك اختياريًا إنشاء مواقعك الإلكترونية باستخدام أدوات ربط ومفاتيح تعديل. على سبيل المثال، يمكنك السماح بضبط القيمة EditText بشكل مبدئي في المكوِّن وطلب البحث لمحتواه عند اللزوم.
  • يمكنك اختياريًا إلغاء onDraw() وonMeasure(). وهذا الإجراء ليس ضروريًا عادةً عند تمديد الوقت Layout، لأنّ التنسيق يتضمّن سلوكًا تلقائيًا ومن المرجّح أن يعمل بشكل جيد.
  • يمكنك اختياريًا إلغاء طُرق on الأخرى، مثل onKeyDown()، على سبيل المثال لاختيار قيم تلقائية معيّنة من القائمة المنبثقة لمربّع التحرير والسرد عند النقر على مفتاح معيّن.

هناك مزايا لاستخدام Layout كأساس لعنصر تحكّم مخصّص، بما في ذلك ما يلي:

  • يمكنك تحديد التنسيق باستخدام ملفات XML التعريفية، كما هو الحال في شاشة النشاط، أو يمكنك إنشاء طرق عرض بشكل آلي ودمجها في التنسيق من الرمز البرمجي الخاص بك.
  • تتميّز الطريقة onDraw() وonMeasure()، بالإضافة إلى معظم طُرق on الأخرى، بسلوك مناسب، لذلك لا تحتاج إلى إلغائها.
  • يمكنك سريعًا إنشاء طرق عرض مركّبة وتعقيدية عشوائية وإعادة استخدامها كما لو كانت مكوّنًا واحدًا.

تعديل نوع عرض حالي

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

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

يجب استيراد نموذج NotePad إلى "استوديو Android" إذا لم يسبق لك فعل ذلك أو الاطّلاع على المصدر باستخدام الرابط المتوفّر. وعلى وجه التحديد، يمكنك الاطّلاع على تعريف LinedEditText في الملف NoteEditor.java.

إليك بعض الأشياء التي يجب مراعاتها في هذا الملف:

  1. التعريف

    يتم تحديد الفئة باستخدام السطر التالي:
    public static class LinedEditText extends EditText

    يتم تعريف LinedEditText على أنّه فئة داخلية ضمن نشاط NoteEditor، ولكنّه متاح للجميع، وبالتالي يمكن الوصول إليه على أنّه NoteEditor.LinedEditText من خارج الفئة NoteEditor.

    والسمة LinedEditText أيضًا هي static، ما يعني أنّها لا تنشئ ما يُعرف باسم "الأساليب الاصطناعية" التي تسمح لها بالوصول إلى البيانات من الفئة الرئيسية. ويعني هذا أنّها تعمل كفئة منفصلة وليس كفئة ذات صلة كبيرة بـ NoteEditor. وهذه طريقة أوضح لإنشاء الفئات الداخلية إذا لم تكن بحاجة إلى الوصول إلى الحالة من الفئة الخارجية. تُبقي الفئة التي تم إنشاؤها صغيرة الحجم وتتيح إمكانية استخدامها بسهولة من الفئات الأخرى.

    توسِّع LinedEditText العنصر EditText، وهو طريقة العرض التي يجب تخصيصها في هذه الحالة. عند الانتهاء، يمكن للفئة الجديدة استبدال طريقة عرض EditText العادية.

  2. إعداد الصف

    كالعادة، اسم Supers يُدعى أولاً. هذه ليست دالة إنشائية تلقائية، ولكنها دالة إنشاء معلَمة. يتم إنشاء EditText باستخدام هذه المَعلمات عند تضخيمها من ملف تنسيق XML. وبالتالي، على الدالة الإنشائية أخذها وتمريرها إلى الدالة الإنشائية من الفئة الفائقة أيضًا.

  3. الطرق التي تم إلغاؤها

    يلغي هذا المثال طريقة onDraw() فقط، ولكن قد تحتاج إلى تجاهل الطرق الأخرى أثناء إنشاء المكوّنات المخصّصة.

    في هذا النموذج، يتيح لك إلغاء الطريقة onDraw() رسم الخطوط الزرقاء على لوحة عرض EditText. ويتم تمرير اللوحة إلى طريقة onDraw() التي تم تجاوزها. ويتم استدعاء الطريقة super.onDraw() قبل انتهاء الطريقة. يجب استدعاء طريقة الطبقة الفائقة. في هذه الحالة، عليك استدعاؤها في النهاية بعد رسم الخطوط التي تريد تضمينها.

  4. المكوِّن المخصّص

    لديك الآن المكون المخصص، ولكن كيف يمكنك استخدامه؟ في مثال NotePad، يتم استخدام المكوِّن المخصّص مباشرةً من تنسيق التعريف، لذا اطّلِع على note_editor.xml في المجلد res/layout:

    <view xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.example.android.notepad.NoteEditor$LinedEditText"
        android:id="@+id/note"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:padding="5dp"
        android:scrollbars="vertical"
        android:fadingEdge="vertical"
        android:gravity="top"
        android:textSize="22sp"
        android:capitalize="sentences"
    />
    

    يتم إنشاء المكوِّن المخصّص كطريقة عرض عامة في XML، ويتم تحديد الفئة باستخدام الحزمة الكاملة. وتتم الإشارة إلى الفئة الداخلية التي تحدّدها باستخدام الترميز NoteEditor$LinedEditText، وهي طريقة عادية للإشارة إلى الفئات الداخلية في لغة برمجة Java.

    إذا لم يتم تعريف مكوّن العرض المخصّص كفئة داخلية، يمكنك تعريف مكوّن العرض باسم عنصر XML واستبعاد السمة class. على سبيل المثال:

    <com.example.android.notepad.LinedEditText
      id="@+id/note"
      ... />
    

    يُرجى العلم أنّ الفئة LinedEditText أصبحت الآن ملف صف منفصل. ولا تنجح هذه الطريقة عندما يتم دمج الفئة في الفئة NoteEditor.

    والسمات والمعلَمات الأخرى في التعريف هي تلك التي يتم تمريرها إلى الدالة الإنشائية المخصّصة ثم تمريرها إلى الدالة الإنشائية EditText، وبالتالي فهي المعلَمات نفسها التي تستخدمها لملف شخصي EditText. ويمكنك أيضًا إضافة مَعلماتك الخاصة.

إنشاء المكونات المخصصة معقد بالقدر الذي تحتاجه فقط.

ويمكن للمكوِّن الأكثر تطورًا إلغاء المزيد من طُرق on وتقديم طرق مساعدة خاصة به، ما يؤدي إلى تخصيص خصائصه وسلوكه بشكل كبير. الحد الوحيد هو خيالك وما تحتاج إلى المكون للقيام به.