الفرق بين المراجعتين لصفحة: «ReactNative/native components android»
جميل-بيلوني (نقاش | مساهمات) طلا ملخص تعديل |
|||
(3 مراجعات متوسطة بواسطة 3 مستخدمين غير معروضة) | |||
سطر 1: | سطر 1: | ||
<noinclude>{{DISPLAYTITLE: مكونات واجهة المستخدم الأصيلة | <noinclude>{{DISPLAYTITLE: مكونات واجهة المستخدم الأصيلة لنظام Android في React Native}}</noinclude> | ||
هناك الكثير من أدوات واجهة المستخدم (UI widgets) الأصيلة الجاهزة للاستخدام في أحدث التطبيقات، بعضها جزء من المنصّة، والبعض الآخر متاح كمكتبات تابعة لجهات طرف ثالث (third-party libraries)، إضافة إلى ما تستخدمه أنت كذلك. يحتوي React Native على العديد من مكوّنات المنصّة الأكثر أهمية، وهي جاهزة للاستخدام، مثل <code>[[ReactNative/scrollview|ScrollView]]</code> و <code>[[ReactNative/textinput|TextInput]]</code>، ولكن ليس جميعها، وبالطبع، فهذا لا يشمل تلك التي قد تكون كتبتها بنفسك. لحسن الحظ، من السهل جدًا تغليف هذه المكونات الحالية لإدماجها بسلاسة مع تطبيق React Native الخاص بك. | |||
هناك الكثير من أدوات واجهة المستخدم (UI widgets) الأصيلة الجاهزة للاستخدام في أحدث التطبيقات، بعضها جزء من المنصّة، والبعض الآخر متاح كمكتبات تابعة لجهات طرف ثالث (third-party libraries)، إضافة إلى ما تستخدمه أنت كذلك. يحتوي React Native على العديد من مكوّنات المنصّة الأكثر أهمية، وهي جاهزة للاستخدام، مثل <code>[[ReactNative/scrollview|ScrollView]]</code> و<code>[[ReactNative/textinput|TextInput]]</code>، ولكن ليس جميعها، وبالطبع، فهذا لا يشمل تلك التي قد تكون كتبتها بنفسك. لحسن الحظ، من السهل جدًا تغليف هذه المكونات الحالية لإدماجها بسلاسة مع تطبيق React Native الخاص بك. | |||
على غرار صفحة [[ReactNative/native modules android|الوحدات الأصيلة]]، فهذا الدليل كذلك أكثر تقدمًا، ويَفترِض أنك معتاد إلى حد ما على برمجة Android SDK. سيوضح لك هذا الدليل كيفية إنشاء مكون واجهة مستخدم أصيل، وسيرشدك إلى كتابة مجموعة فرعيّة من مكون <code>ImageView</code> الموجود في مكتبة React Native الأساسية (the core React Native library). | على غرار صفحة [[ReactNative/native modules android|الوحدات الأصيلة]]، فهذا الدليل كذلك أكثر تقدمًا، ويَفترِض أنك معتاد إلى حد ما على برمجة Android SDK. سيوضح لك هذا الدليل كيفية إنشاء مكون واجهة مستخدم أصيل، وسيرشدك إلى كتابة مجموعة فرعيّة من مكون <code>ImageView</code> الموجود في مكتبة React Native الأساسية (the core React Native library). | ||
==ImageView كمثال== | ==الواجهة ImageView كمثال== | ||
في هذا المثال، سنتعرف على متطلبات الإجراء (implementation requirements) للسماح باستخدام | في هذا المثال، سنتعرف على متطلبات الإجراء (implementation requirements) للسماح باستخدام ImageView في JavaScript. | ||
تُنشأ | تُنشأ الواجهات الأصيلة Native views وتُعالَج عن طريق توسيع <code>ViewManager</code> أو <code>SimpleViewManager</code> بشكل أكثر شيوعًا. يعتبر <code>SimpleViewManager</code> مناسبًا في هذه الحالة لأنه يطبق خاصيات شائعة مثل لون الخلفية والتعتيم وتخطيط Flexbox. | ||
هذه الأصناف الفرعية هي مفردات (singletons) أساسا، أي تُنشأ نسخة واحدة فقط لكل منها بواسطة الجسر. وهي تُزوِّد | هذه الأصناف الفرعية هي مفردات (singletons) أساسا، أي تُنشأ نسخة واحدة فقط لكل منها بواسطة الجسر. وهي تُزوِّد الواجهات الأصيلة إلى <code>NativeViewHierarchyManager</code>، والذي يُفوِّض لها مرة أخرى لتعيين وتحديث خاصيات الواجهات حسب الضرورة. عادةً ما يكون مدراء الواجهات (أصناف <code>ViewManager</code>) مفوَّضين للواجهات، مُرسلةً الأحداث مرة أخرى إلى JavaScript عبر الجسر. | ||
تزويدُ | تزويدُ واجهة (Vending a view) أمرٌ بسيط: | ||
# إنشاء الصنف الفرعي لمدير | # إنشاء الصنف الفرعي لمدير الواجهة ViewManager | ||
# كتابة التابع <code>createViewInstance</code> | # كتابة التابع <code>createViewInstance</code> | ||
# توفير الوصول إلى توابع setter لضبط خاصيّات | # توفير الوصول إلى توابع setter لضبط خاصيّات الواجهة باستخدام التوصيف <code>@ReactProp</code> (أو <code>@ReactPropGroup</code>) | ||
# تسجيل المدير في <code>createViewManagers</code> الخاص بحزمة التطبيقات. | # تسجيل المدير في <code>createViewManagers</code> الخاص بحزمة التطبيقات. | ||
# كتابة وحدة JavaScript | # كتابة وحدة JavaScript | ||
===1. إنشاء الصنف الفرعي لمدير | ===1. إنشاء الصنف الفرعي لمدير الواجهة <code>ViewManager</code>=== | ||
في هذا المثال، سننشئ صنف مدير | في هذا المثال، سننشئ صنف مدير الواجهة <code>ReactImageManager</code> الذي يوسِّع <code>SimpleViewManager</code> من النوع <code>ReactImageView</code>. النوع <code>ReactImageView</code> هو نوع الكائن الذي يديره المدير، وسيكون هذا هو الواجهة الأصيلة المخصصة. يُستخدَم الاسم المعاد من طرف <code>getName</code> لإحالة (to reference) نوع الواجهة الأصيلة من JavaScript. | ||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
... | ... | ||
سطر 27: | سطر 26: | ||
public static final String REACT_CLASS = "RCTImageView"; | public static final String REACT_CLASS = "RCTImageView"; | ||
ReactApplicationContext mCallerContext; | |||
public ReactImageManager(ReactApplicationContext reactContext) { | |||
mCallerContext = reactContext; | |||
} | |||
@Override | @Override | ||
سطر 35: | سطر 39: | ||
===2. كتابة التابع <code>createViewInstance</code>=== | ===2. كتابة التابع <code>createViewInstance</code>=== | ||
تنشأ | تنشأ الواجهات في تابع <code>createViewInstance</code>، يجب أن يُهيِّئَ الواجهة نفسها في حالته الافتراضية (default state)، وستُعيَّن الخاصيات عبر استدعاء <code>updateView</code> لاحقًا. | ||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
@Override | @Override | ||
public ReactImageView createViewInstance(ThemedReactContext context) { | public ReactImageView createViewInstance(ThemedReactContext context) { | ||
return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), mCallerContext); | return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), null, mCallerContext); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
===3. توفير الوصول إلى توابع | ===3. توفير الوصول إلى توابع ضابطة لضبط خاصيات الواجهة باستخدام التوصيف <code>@ReactProp</code> (أو <code>@ReactPropGroup</code>)=== | ||
يجب توفير الوصول للخاصيّات التي يجب أن تنعكس في JavaScript كتابع setter موصوف (annotated) باستخدام التوصيف <code>@ReactProp</code> (أو <code>@ReactPropGroup</code>). يجب أن يأخذ تابع Setter | يجب توفير الوصول للخاصيّات التي يجب أن تنعكس في JavaScript كتابع setter موصوف (annotated) باستخدام التوصيف <code>@ReactProp</code> (أو <code>@ReactPropGroup</code>). يجب أن يأخذ تابع ضابط Setter الواجهة المراد تحديثها (من نوع الواجهة الحالية) كأوّل معامل، وقيمة الخاصية كمعامل ثانٍ. يجب إعلان الضابط Setter كتابع <code>void</code> ويجب أن يكون عامًّا (<code>public</code>). يُحدّد نوع الخاصية المرسلة إلى JavaScript تلقائيًا استنادًا إلى نوع المعامل القيمة الخاص بتابع setter. أنواع القيم المدعومة حاليًا هي: | ||
يحتاج التوصيف <code>@ReactProp</code> معاملًا إلزاميًّا واحدًا يُسمّى <code>name</code> من النوع <code>String</code> يمثّل اسم التوصيف. يُستخدَم الاسم المعيَّن للتوصيف <code>@ReactProp</code> المرتبط بالتابع setter للإشارة إلى الخاصية في جانب JavaScript. | <syntaxhighlight lang="js"> | ||
boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap | |||
</syntaxhighlight>يحتاج التوصيف <code>@ReactProp</code> معاملًا إلزاميًّا واحدًا يُسمّى <code>name</code> من النوع <code>String</code> يمثّل اسم التوصيف. يُستخدَم الاسم المعيَّن للتوصيف <code>@ReactProp</code> المرتبط بالتابع الضابط setter للإشارة إلى الخاصية في جانب JavaScript. | |||
باستثناء <code>name</code>، يقبل التوصيف <code>@ReactProp</code> المعاملات الاختيارية التالية: <code>defaultBoolean</code>، و<code>defaultInt</code>، و<code>defaultFloat</code>. يجب أن تكون هذه المعاملات من النوع البدائي المقابل (بالتالي <code>boolean</code>، و<code>int</code>، و<code>float</code>) وستمرَّر القيمة المعطاة إلى تابع setter في حالة إزالة الخاصية التي يشير إليها تابع setter من المكون. لاحظ أن القيم "الافتراضية ( | باستثناء <code>name</code>، يقبل التوصيف <code>@ReactProp</code> المعاملات الاختيارية التالية: <code>defaultBoolean</code>، و<code>defaultInt</code>، و<code>defaultFloat</code>. يجب أن تكون هذه المعاملات من النوع البدائي المقابل (بالتالي <code>boolean</code>، و<code>int</code>، و<code>float</code>) وستمرَّر القيمة المعطاة إلى تابع ضابط setter في حالة إزالة الخاصية التي يشير إليها تابع ضابط setter من المكون. لاحظ أن القيم "الافتراضية ("default") موفَّرة فقط للأنواع البدائية، إذا كان التابع الضابط setter نوعًا معقدًا، ستوفَّر القيمة <code>null</code> كقيمة افتراضية في حالة إزالة الخاصية المطابقة. | ||
متطلبات إعلان (declaration) تابع Setter للتوابع الموصوفة بالتوصيف <code>@ReactPropGroup</code> مختلفة عن متطلبات <code>@ReactProp</code>، يرجى الرجوع إلى توثيقات صنف التوصيف <code>@ReactPropGroup</code> للمزيد من المعلومات حول هذا الموضوع. | متطلبات إعلان (declaration) تابع ضابط Setter للتوابع الموصوفة بالتوصيف <code>@ReactPropGroup</code> مختلفة عن متطلبات <code>@ReactProp</code>، يرجى الرجوع إلى توثيقات صنف التوصيف <code>@ReactPropGroup</code> للمزيد من المعلومات حول هذا الموضوع. | ||
'''ملاحظة مهمّة | '''ملاحظة مهمّة:''' في ReactJS تحديث قيمة الخاصية سيؤدي إلى استدعاء تابع ضباط setter. لاحظ أن إحدى الطرق لتحديث المكون هي بإزالة الخاصيات التي عُيِّنت من قبل. في هذه الحالة، سيستدعى تابع ضابط setter كذلك لإشعار مدير الواجهة بأن الخاصية قد تغيرت. في هذه الحالة، ستوفَّر القيمة "الافتراضية" (للأنواع البدائية، يُمكن تحديد القيم "الافتراضيّة" باستخدام <code>defaultBoolean</code>، و<code>defaultFloat</code>، إلخ. والتي هي معاملات التوصيف <code>@ReactProp</code>، للأنواع المعقَّدة، سيُستدعى الضابط setter مع القيمة <code>null</code>). | ||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
@ReactProp(name = "src") | |||
public void setSrc(ReactImageView view, @Nullable ReadableArray sources) { | public void setSrc(ReactImageView view, @Nullable ReadableArray sources) { | ||
view.setSource(sources); | view.setSource(sources); | ||
سطر 70: | سطر 76: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
===4. تسجيل مدير | ===4. تسجيل مدير الواجهة <code>ViewManager</code>=== | ||
الخطوة الأخيرة في Java هي تسجيل مدير | الخطوة الأخيرة في Java هي تسجيل مدير الواجهة للتطبيق، وهذا يحدث بطريقة مشابهة [https://wiki.hsoub.com/ReactNative/native_modules_android للوحدات الأصيلة] عبر دالة عضو حزمة (package member function) التطبيق <code>createViewManagers</code>. | ||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
@Override | |||
public List<ViewManager> createViewManagers( | public List<ViewManager> createViewManagers( | ||
ReactApplicationContext reactContext) { | ReactApplicationContext reactContext) { | ||
return Arrays.<ViewManager>asList( | return Arrays.<ViewManager>asList( | ||
new ReactImageManager() | new ReactImageManager(reactContext) | ||
); | ); | ||
} | } | ||
سطر 83: | سطر 89: | ||
===5. إنشاء وحدة JavaScript=== | ===5. إنشاء وحدة JavaScript=== | ||
الخطوة الأخيرة هي إنشاء وحدة JavaScript تحدد طبقة الواجهة (interface layer) بين Java و JavaScript لمستخدمي | الخطوة الأخيرة هي إنشاء وحدة JavaScript تحدد طبقة الواجهة (interface layer) بين Java و JavaScript لمستخدمي الواجهة الجديدة. يوصى بتوثيق واجهة المكون في هذه الوحدة (باستخدام Flow أو [[TypeScript]] أو التعليقات مثلًا). | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
// ImageView.js | // ImageView.js | ||
import {requireNativeComponent} from 'react-native'; | import { requireNativeComponent } from 'react-native'; | ||
/** | /** | ||
سطر 98: | سطر 104: | ||
module.exports = requireNativeComponent('RCTImageView'); | module.exports = requireNativeComponent('RCTImageView'); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
تأخذ الدالة <code>requireNativeComponent</code> اسم | تأخذ الدالة <code>requireNativeComponent</code> اسم الواجهة الأصيلة. لاحظ أنه إذا احتاج مكونك إلى القيام بأي شيء أعقد (مثل معالجة الأحداث المخصصة)، فعليك تغليف المكون الأصيل في مكون React آخر. هذا موضح في مثال <code>MyCustomView</code> أدناه. | ||
==الأحداث (Events)== | ==الأحداث (Events)== | ||
نعرف الآن كيف نوفر الوصول لمكونات | نعرف الآن كيف نوفر الوصول لمكونات الواجهة الأصيلة التي يمكننا التحكم فيها بسهولة من JavaScript، لكن كيف نتعامل مع الأحداث من المستخدم، مثل القرص للتكبير أو للتصغير أو المسح (panning). عند حدوثِ حدثٍ أصيل، يجب أن تصدر الشيفرة الأصيلة حدثًا للواجهة في JavaScript، مع ربط الواجهتين بالقيمة المُعادة من تابع <code>getId()</code>. | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
class MyCustomView extends View { | class MyCustomView extends View { | ||
سطر 117: | سطر 123: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
لربط اسم حدث <code>topChange</code> | لربط اسم حدث <code>topChange</code> مع خاصية دالة رد النداء <code>onChange</code> في JavaScript، سجِّله عن طريق تجاوز تابع <code>getExportedCustomBubblingEventTypeConstants</code> في مدير الواجهة <code>ViewManager</code> الخاص بك: | ||
<syntaxhighlight lang="java"> | <syntaxhighlight lang="java"> | ||
public class ReactImageManager extends SimpleViewManager<MyCustomView> { | public class ReactImageManager extends SimpleViewManager<MyCustomView> { | ||
سطر 135: | سطر 141: | ||
تُستدعَى دالة رد النداء هذه مع الحدث الخام (raw event)، والذي نقوم بمعالجته عادةً في المكون المُغلِّف (wrapper component) لإنشاء واجهة برمجة تطبيقات أبسط: | تُستدعَى دالة رد النداء هذه مع الحدث الخام (raw event)، والذي نقوم بمعالجته عادةً في المكون المُغلِّف (wrapper component) لإنشاء واجهة برمجة تطبيقات أبسط: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
// MyCustomView.js | // MyCustomView.js | ||
سطر 162: | سطر 169: | ||
var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`); | var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== مصادر == | == مصادر == | ||
* [https://facebook.github.io/react-native/docs/native-components-android صفحة Native UI Components في توثيق React Native الرسمي.] | * [https://facebook.github.io/react-native/docs/native-components-android صفحة Native UI Components في توثيق React Native الرسمي.] | ||
[[تصنيف:ReactNative]] | [[تصنيف:ReactNative]] | ||
[[تصنيف:React Native Docs]] |
المراجعة الحالية بتاريخ 13:50، 9 أكتوبر 2021
هناك الكثير من أدوات واجهة المستخدم (UI widgets) الأصيلة الجاهزة للاستخدام في أحدث التطبيقات، بعضها جزء من المنصّة، والبعض الآخر متاح كمكتبات تابعة لجهات طرف ثالث (third-party libraries)، إضافة إلى ما تستخدمه أنت كذلك. يحتوي React Native على العديد من مكوّنات المنصّة الأكثر أهمية، وهي جاهزة للاستخدام، مثل ScrollView
و TextInput
، ولكن ليس جميعها، وبالطبع، فهذا لا يشمل تلك التي قد تكون كتبتها بنفسك. لحسن الحظ، من السهل جدًا تغليف هذه المكونات الحالية لإدماجها بسلاسة مع تطبيق React Native الخاص بك.
على غرار صفحة الوحدات الأصيلة، فهذا الدليل كذلك أكثر تقدمًا، ويَفترِض أنك معتاد إلى حد ما على برمجة Android SDK. سيوضح لك هذا الدليل كيفية إنشاء مكون واجهة مستخدم أصيل، وسيرشدك إلى كتابة مجموعة فرعيّة من مكون ImageView
الموجود في مكتبة React Native الأساسية (the core React Native library).
الواجهة ImageView كمثال
في هذا المثال، سنتعرف على متطلبات الإجراء (implementation requirements) للسماح باستخدام ImageView في JavaScript.
تُنشأ الواجهات الأصيلة Native views وتُعالَج عن طريق توسيع ViewManager
أو SimpleViewManager
بشكل أكثر شيوعًا. يعتبر SimpleViewManager
مناسبًا في هذه الحالة لأنه يطبق خاصيات شائعة مثل لون الخلفية والتعتيم وتخطيط Flexbox.
هذه الأصناف الفرعية هي مفردات (singletons) أساسا، أي تُنشأ نسخة واحدة فقط لكل منها بواسطة الجسر. وهي تُزوِّد الواجهات الأصيلة إلى NativeViewHierarchyManager
، والذي يُفوِّض لها مرة أخرى لتعيين وتحديث خاصيات الواجهات حسب الضرورة. عادةً ما يكون مدراء الواجهات (أصناف ViewManager
) مفوَّضين للواجهات، مُرسلةً الأحداث مرة أخرى إلى JavaScript عبر الجسر.
تزويدُ واجهة (Vending a view) أمرٌ بسيط:
- إنشاء الصنف الفرعي لمدير الواجهة ViewManager
- كتابة التابع
createViewInstance
- توفير الوصول إلى توابع setter لضبط خاصيّات الواجهة باستخدام التوصيف
@ReactProp
(أو @ReactPropGroup
) - تسجيل المدير في
createViewManagers
الخاص بحزمة التطبيقات. - كتابة وحدة JavaScript
1. إنشاء الصنف الفرعي لمدير الواجهة ViewManager
في هذا المثال، سننشئ صنف مدير الواجهة ReactImageManager
الذي يوسِّع SimpleViewManager
من النوع ReactImageView
. النوع ReactImageView
هو نوع الكائن الذي يديره المدير، وسيكون هذا هو الواجهة الأصيلة المخصصة. يُستخدَم الاسم المعاد من طرف getName
لإحالة (to reference) نوع الواجهة الأصيلة من JavaScript.
...
public class ReactImageManager extends SimpleViewManager<ReactImageView> {
public static final String REACT_CLASS = "RCTImageView";
ReactApplicationContext mCallerContext;
public ReactImageManager(ReactApplicationContext reactContext) {
mCallerContext = reactContext;
}
@Override
public String getName() {
return REACT_CLASS;
}
2. كتابة التابع createViewInstance
تنشأ الواجهات في تابع createViewInstance
، يجب أن يُهيِّئَ الواجهة نفسها في حالته الافتراضية (default state)، وستُعيَّن الخاصيات عبر استدعاء updateView
لاحقًا.
@Override
public ReactImageView createViewInstance(ThemedReactContext context) {
return new ReactImageView(context, Fresco.newDraweeControllerBuilder(), null, mCallerContext);
}
3. توفير الوصول إلى توابع ضابطة لضبط خاصيات الواجهة باستخدام التوصيف @ReactProp
(أو @ReactPropGroup
)
يجب توفير الوصول للخاصيّات التي يجب أن تنعكس في JavaScript كتابع setter موصوف (annotated) باستخدام التوصيف @ReactProp
(أو @ReactPropGroup
). يجب أن يأخذ تابع ضابط Setter الواجهة المراد تحديثها (من نوع الواجهة الحالية) كأوّل معامل، وقيمة الخاصية كمعامل ثانٍ. يجب إعلان الضابط Setter كتابع void
ويجب أن يكون عامًّا (public
). يُحدّد نوع الخاصية المرسلة إلى JavaScript تلقائيًا استنادًا إلى نوع المعامل القيمة الخاص بتابع setter. أنواع القيم المدعومة حاليًا هي:
boolean, int, float, double, String, Boolean, Integer, ReadableArray, ReadableMap
يحتاج التوصيف @ReactProp
معاملًا إلزاميًّا واحدًا يُسمّى name
من النوع String
يمثّل اسم التوصيف. يُستخدَم الاسم المعيَّن للتوصيف @ReactProp
المرتبط بالتابع الضابط setter للإشارة إلى الخاصية في جانب JavaScript.
باستثناء name
، يقبل التوصيف @ReactProp
المعاملات الاختيارية التالية: defaultBoolean
، وdefaultInt
، وdefaultFloat
. يجب أن تكون هذه المعاملات من النوع البدائي المقابل (بالتالي boolean
، وint
، وfloat
) وستمرَّر القيمة المعطاة إلى تابع ضابط setter في حالة إزالة الخاصية التي يشير إليها تابع ضابط setter من المكون. لاحظ أن القيم "الافتراضية ("default") موفَّرة فقط للأنواع البدائية، إذا كان التابع الضابط setter نوعًا معقدًا، ستوفَّر القيمة null
كقيمة افتراضية في حالة إزالة الخاصية المطابقة.
متطلبات إعلان (declaration) تابع ضابط Setter للتوابع الموصوفة بالتوصيف @ReactPropGroup
مختلفة عن متطلبات @ReactProp
، يرجى الرجوع إلى توثيقات صنف التوصيف @ReactPropGroup
للمزيد من المعلومات حول هذا الموضوع.
ملاحظة مهمّة: في ReactJS تحديث قيمة الخاصية سيؤدي إلى استدعاء تابع ضباط setter. لاحظ أن إحدى الطرق لتحديث المكون هي بإزالة الخاصيات التي عُيِّنت من قبل. في هذه الحالة، سيستدعى تابع ضابط setter كذلك لإشعار مدير الواجهة بأن الخاصية قد تغيرت. في هذه الحالة، ستوفَّر القيمة "الافتراضية" (للأنواع البدائية، يُمكن تحديد القيم "الافتراضيّة" باستخدام defaultBoolean
، وdefaultFloat
، إلخ. والتي هي معاملات التوصيف @ReactProp
، للأنواع المعقَّدة، سيُستدعى الضابط setter مع القيمة null
).
@ReactProp(name = "src")
public void setSrc(ReactImageView view, @Nullable ReadableArray sources) {
view.setSource(sources);
}
@ReactProp(name = "borderRadius", defaultFloat = 0f)
public void setBorderRadius(ReactImageView view, float borderRadius) {
view.setBorderRadius(borderRadius);
}
@ReactProp(name = ViewProps.RESIZE_MODE)
public void setResizeMode(ReactImageView view, @Nullable String resizeMode) {
view.setScaleType(ImageResizeMode.toScaleType(resizeMode));
}
4. تسجيل مدير الواجهة ViewManager
الخطوة الأخيرة في Java هي تسجيل مدير الواجهة للتطبيق، وهذا يحدث بطريقة مشابهة للوحدات الأصيلة عبر دالة عضو حزمة (package member function) التطبيق createViewManagers
.
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new ReactImageManager(reactContext)
);
}
5. إنشاء وحدة JavaScript
الخطوة الأخيرة هي إنشاء وحدة JavaScript تحدد طبقة الواجهة (interface layer) بين Java و JavaScript لمستخدمي الواجهة الجديدة. يوصى بتوثيق واجهة المكون في هذه الوحدة (باستخدام Flow أو TypeScript أو التعليقات مثلًا).
// ImageView.js
import { requireNativeComponent } from 'react-native';
/**
* Composes `View`.
*
* - src: string
* - borderRadius: number
* - resizeMode: 'cover' | 'contain' | 'stretch'
*/
module.exports = requireNativeComponent('RCTImageView');
تأخذ الدالة requireNativeComponent
اسم الواجهة الأصيلة. لاحظ أنه إذا احتاج مكونك إلى القيام بأي شيء أعقد (مثل معالجة الأحداث المخصصة)، فعليك تغليف المكون الأصيل في مكون React آخر. هذا موضح في مثال MyCustomView
أدناه.
الأحداث (Events)
نعرف الآن كيف نوفر الوصول لمكونات الواجهة الأصيلة التي يمكننا التحكم فيها بسهولة من JavaScript، لكن كيف نتعامل مع الأحداث من المستخدم، مثل القرص للتكبير أو للتصغير أو المسح (panning). عند حدوثِ حدثٍ أصيل، يجب أن تصدر الشيفرة الأصيلة حدثًا للواجهة في JavaScript، مع ربط الواجهتين بالقيمة المُعادة من تابع getId()
.
class MyCustomView extends View {
...
public void onReceiveNativeEvent() {
WritableMap event = Arguments.createMap();
event.putString("message", "MyMessage");
ReactContext reactContext = (ReactContext)getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topChange",
event);
}
}
لربط اسم حدث topChange
مع خاصية دالة رد النداء onChange
في JavaScript، سجِّله عن طريق تجاوز تابع getExportedCustomBubblingEventTypeConstants
في مدير الواجهة ViewManager
الخاص بك:
public class ReactImageManager extends SimpleViewManager<MyCustomView> {
...
public Map getExportedCustomBubblingEventTypeConstants() {
return MapBuilder.builder()
.put(
"topChange",
MapBuilder.of(
"phasedRegistrationNames",
MapBuilder.of("bubbled", "onChange")))
.build();
}
}
تُستدعَى دالة رد النداء هذه مع الحدث الخام (raw event)، والذي نقوم بمعالجته عادةً في المكون المُغلِّف (wrapper component) لإنشاء واجهة برمجة تطبيقات أبسط:
// MyCustomView.js
class MyCustomView extends React.Component {
constructor(props) {
super(props);
this._onChange = this._onChange.bind(this);
}
_onChange(event: Event) {
if (!this.props.onChangeMessage) {
return;
}
this.props.onChangeMessage(event.nativeEvent.message);
}
render() {
return <RCTMyCustomView {...this.props} onChange={this._onChange} />;
}
}
MyCustomView.propTypes = {
/**
* دالة رد نداء تُستدعى باستمرار عندما يتفاعل المستخدم مع المكوّن
*/
onChangeMessage: PropTypes.func,
...
};
var RCTMyCustomView = requireNativeComponent(`RCTMyCustomView`);