البحث في الموقع
المحتوى عن 'هواتف ذكية'.
-
في عالم اليوم، لم يعد بالإمكان قصر اهتمامنا على الهواتف المحمولة والحواسيب، فلدينا أجهزة لوحية وأخرى "تُرتدى" كالسّاعات والنّظارات الذكيّة. سيكون موضوعنا اليوم عن التصميم لمختلف أنواع الأجهزة. فهرس سلسلة مدخل إلى تجربة المستخدم: مدخل إلى تجربة المستخدم User Experience فهم ودراسة المستخدمين في مجال تجربة المستخدم دراسة الشريحة المستهدفة في مجال تجربة المستخدم كيفية التصميم للأجهزة المختلفة (هذا الدرس) هندسة المعلومات في تجربة المستخدم تعرف على أنماط التصميم في مجال تجربة المستخدم أشياء لا يمكن اعتبارها رسوما تخطيطية (Wireframes) في مجال تجربة المستخدم تعرف على الرسوم التخطيطية (Wireframes) في مجال تجربة المستخدم مفهوم الثقل المرئي (Visual Weight) والألوان في مجال تجربة المستخدم التكرار ومخالفة الأنماط في مجال تجربة المستخدم المحاذاة والقرب في مجال تجربة المستخدم تعرف على أساليب مسح الواجهة والتراتب المرئي في مجال تجربة المستخدم أساليب الإطلاع في مجال تجربة المستخدم: التصفح، البحث والاكتشاف تصميم هيكل صفحة الويب والعناصر الأساسية في مجال تجربة المستخدم الأزرار، النماذج والدعوات إلى الإجراء في مجال تجربة المستخدم استخدام علم النفس في مجال تجربة المستخدم لتكييف المستخدم وإقناعه كيف تغير الخبرة من تجربة المستخدم؟ تصميم تجربة المستخدم من خلال بيانات وإحصائيات المستخدمين تعرف على أنواع المخططات الإحصائية في مجال تجربة المستخدم اختبارات أ/ب (A/B Test) في مجال تجربة المستخدم الخطوة الأولى: كيف سيكون التعامل مع الواجهة؟ باللمس بالإصبع أم بمؤشّر الفأرة؟ الخطوة الثانية: ابدأ بالأجهزة الصغيرة يعتقد البعض أن عبارة "mobile first" الشّائعة تأتي من صعود شعبيّة الهواتف الذّكية، وهذا جزء من الحقيقة، أمّا الجزء الآخر فهو قائم على أن التّصميم للأجهزة الصّغيرة محدودة القدرات يُجبر المصمّم على التّركيز على المحتوى والوظيفة الأساسيّة للمشروع، مؤدّيًا بدوره إلى تطبيقات بسيطة وجميلة؛ أمّا العكس (أي البدء بالأجهزة القويّة) فهو أشبه بإقحام قطّ في قفص عصفور، أمر ليس بسيطًا ولا جميلًا! الخطوة الثالثة: ما الإمكانيات المميزة لهذا الجهاز؟ تتنقّل الهواتف الذّكية معنا طيلة اليوم، وهذا يعني أنّنا نقضي وقتًا طويلًا في استخدامها، وأن باستطاعتنا استخدام الموقع في تطبيقاتنا، كما أنّها أجهزة صغيرة الحجم ويسهل نقلها، أمّا الحواسيب المحمولة فهي أقل سهولةً في النّقل ولكنّها أكثر قدرةً، وشاشاتها أكبر حجمًا، وفيها لوحة مفاتيح مُريحة، ومؤشّر يسمح بتحديد أكثر دقّة ووظائف أكثر. لا تُصرَّ كثيرًا على فكرة "وحدة الواجهة" بين الأجهزة المختلفة، بل فكّر بأسلوب مختلف لكلّ جهاز. الخطوة الرابعة: لا تنس البيئة التي يعمل فيها التطبيق هناك اختلاف في الخطوط العامّة لتجربة المستخدم بين Mac OS X وWindows، وكذلك يختلف Windows Vista عن Windows 8، وiOS 7 عن iOS 6، وقد تُضطّر لاختيار إصدارات محدودة لاستهدافها، وأخرى تتجاهلها، ففي كلّ مرّة توفّر تطبيقك لإصدار جديد، يتضاعف جهد التصّميم والتّطوير والصّيانة في المستقبل. كن بعيد النّظر! الخطوة الخامسة: كن مستجيبا هل ستوفّر تطبيقك على الويب؟ هل يدعم بضعة أنواع من الهواتف فقط؟ كيف سيعمل على الأجهزة القادمة؟ كل الأجهزة تستطيع التّواصل مع الإنترنت اليوم، لذا احرص على أن باستطاعة تطبيقك التلاؤم مع مختلف الأجهزة التي قد يرغب مُستخدمو تطبيقك باستعمالها. الخطوة السادسة: فكر بأكثر من شاشة واحدة في الوقت نفسه قد يكون هذا الموضوع متقدّمًا، ولكنّ بإمكانك بشيء من الجهد تحقيقه. هل يمكن استخدام هاتفك وحاسوبك سويّة كما يمكن التّحكم بالتّلفاز عن بُعد؟ هل يمكن لمجموعة من الهواتف أن تتحكّم بلعبة على حاسوب لوحيّ في غرفة واحدة؟ وإذا كنت تستخدم جهازين في وقت واحد، فهل يمكن نقل البيانات بينهما؟ ماذا عن مزامنة البيانات؟ هل ستؤدّي إلى مشاكل في الاستعمال؟ فكّر في الأمر! سنتعرّف في الدّرس القادم على أنماط التّصميم، وهي مجموعة من الأساليب الشّائعة لحلّ المشكلات المُتكّررة في تصميم تجربة المُستخدم. ترجمة بتصرّف للدرس Designing For Devices من سلسلة Daily UX Crash Course لصاحبها Joel Marsh. اقرأ أيضًا النسخة العربية الكاملة لكتاب مدخل إلى تجربة المستخدم (User Experience - UX) 1.0.0 كيف تعيد تصميم موقع إلكتروني قائم بالشكل الصحيح عشرة أمور أساسية يجب أخذها في الحسبان لدى تصميم النوافذ المنبثقة عشر مبادئ رئيسية لتصميم تجربة مستخدم على الهواتف الذكية التصميم للهواتف: التصميم البصري
-
- هواتف ذكية
- واجهة
-
(و 7 أكثر)
موسوم في:
-
تنتشر الهواتف المحمولة والحواسيب اللوحية العاملة بنظام تشغيل أندرويد في أحجام مختلفة ودقة شاشات مختلفة، لذلك من الأفضل دائمًا إذا استطاع التطبيق أن يتأقلم مع حجم ودقة الشاشة لتقديم أفضل تجربة استخدام مع الاستغلال الأمثل لشاشة. ويمكن أن تبني تطبيقًا تتغير الواجهة الخاصة به بشكل ديناميكي تبعًا للمساحة المتاحة في الشاشة، فإذا لم تتواجد مساحة كافية، يظهر جزء من الواجهة فقط في الشاشة ومنه يمكن الانتقال للواجهة الأخرى مثل الصورة التالية: أما إذا توافرت المساحة الكافية فيمكن دمج أكثر من واجهة في واجهة واحدة لسهولة التنقل بينها كما في الصورة التالية: لذا يوفر أندرويد عدة طرق للقيام بذلك من ضمنها استخدام الـ Fragment داخل التطبيق. Fragment يُعبر مصطلح Fragment عن تجزئة واجهة المستخدم إلى وحدات أصغر تُمكن المطوّر من دمج أو فصل هذه الوحدات تبعًا لحجم الشاشة، ولا تتكون تلك الوحدة من عناصر واجهة المستخدم فقط ولكنها أيضًا تتكون من شيفرات تتحكم في وظيفة هذه الواجهة وسلوكها. لكن هذه الوحدات ليست مستقلة بذاتها أي لا يمكن أن تُعرض بداخل التطبيق إلا من خلال نشاط “Activity” يستضيفها بداخله. ولهذا المفهوم العديد من المزايا التي توفرها عند تطوير التطبيقات ومنها: تقسيم الشيفرات المعقدة التي تتواجد في واجهة نشاط واحد إلى عدة وحدات لكل منها شيفرة بسيطة يتم دمجها وترتيبها بعد ذلك داخل واجهة واحدة. إمكانية إعادة استخدام الوحدة عدة مرات في أنشطة مختلفة ودمجها مع وحدات أخرى مختلفة. التكييف مع الواجهات المختلفة والأحجام المختلفة. دورة حياة التجزئة Fragment للـ Fragment دورة حياة مشابهة لدورة حياة النشاط ويتم استدعاء التوابع الخاصة بدورة الحياة عند حدوث حدث معين كإنشاء أو إزالة الـ Fragment. ()onAttach: ويتم استدعاؤه عندما يتم ربط الـ Fragment بالنشاط المضيف. ()onCreate: ويتم استدعاؤه بعد ربطه بالنشاط مباشرة وذلك لاستخدامها في تهيئة العناصر والمتغيرات التي نرغب في بقائها حتى بعد إزالة واجهة الـ Fragment من داخل النشاط المضيف. ()onCreateView: ويتم استدعاؤه لربط الواجهة الخاصة بالـ Fragment بالشيفرة الخاصة به. ()onActivityCreated: ويتم استدعاؤه بعد انتهاء النشاط المضيف من التابع ()onCreate الخاص به. ()onStart: ويتم استدعاؤه عندما تبدأ الواجهة الخاصة بالـ Fragment في الظهور وذلك بعد الانتهاء من ()onStart الخاصة بالنشاط أولًا. ()onResume: ويتم استدعاؤه عندما تظهر الواجهة الخاصة بالـFragment وتصبح قابلة لتفاعل المستخدم معها، مع العلم بأنه لا يمكن للمستخدم بالتفاعل مع واجهة الـ Fragment قبل أن يتم استدعاء ()onResume الخاصة بالنشاط أولًا لتصبح واجهة النشاط قابلة للتفاعل أيضًا. ()onPause: ويتم استدعاؤه عندما تبدأ الواجهة الخاصة بالنشاط المضيف في الاختفاء أو تظهر بشكل جزئي ولا يمكن للمستخدم التفاعل معها لوجود شيء ما يحجبها، أو عند الاستعداد لاستبدال أو إزالة الـ Fragment. ()onStop: ويتم استدعاؤه عندما تختفي الواجهة الخاصة بالنشاط المضيف من أمام المستخدم أو لاستبدال أو إزالة الـ Fragment. ()onDestroyView: ويتم استدعاؤه عند إزالة الواجهة التي تم إضافتها من قبل في ()onCreateView. ()onDestroy: ويتم استدعاؤه قبل تدمير الواجهة وإزالتها من الذاكرة. ()onDetach: ويتم استدعاؤه عند إزالة الـ Fragment من واجهة النشاط المضيف. لذا فكما ذكرنا فدورة حياة الـ Fragment مشابهة لدورة حياة النشاط ولكن هناك بعض التوابع الخاصة بالـ Fragment فقط ولا تتواجد في النشاط. تطبيق 1 الهدف من هذا التطبيق فهم الأساسيات الخاصة بالـ Fragments حيث تتكون من: واجهة خاصة به (XML File). صنف جديد يرث من Fragment. مكان خاص له في الواجهة الرئيسية الخاصة بالنشاط المضيف. من Android Studio نُنشئ مشروعًا جديدًا يُدعى Simple Fragment وبنفس الإعدادات في المشاريع السابقة. نبدأ أولًا بصنع Fragment جديد (ملف واجهة وملف للشيفرة) عن طريق الضغط بالزر الأيمن على اسم الحزمة الخاصة بالمشروع كما بالصورة التالية: ثم اختر: New > Fragment > Fragment Blank لتظهر لك هذه النافذة، قم بتغيير الاسم إلى الاسم الذي تراه مناسبًا وتغيير الاختيارات كما بالصورة: ثم اضغط Finish. سيقوم Android Studio بعد ذلك بإنشاء صنف جديد يُدعى ExampleFragment ويرث من الصنف Fragment، كما سينشئ ملف واجهة جديد يُدعى fragment_example. نقوم بتغيير ملف الواجهة كما كنا نفعل في التطبيقات السابقة وفي هذا المثال سنكتفي بوضع نص في منتصف الواجهة مع تغيير لون الخلفية: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ccefff" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Hello From Example Fragment" android:gravity="center"/> </LinearLayout> والآن لربط ملف الواجهة بالشيفرة الخاصة بالصنف ExampleFragment نكتب داخل التابع ()onCreateView -وهو التابع المسؤول عن ربط الشيفرة بالواجهة- الشيفرة التالية: @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView =inflater.inflate(R.layout.fragment_example, container, false); return rootView; } تختلف طريقة ربط الواجهة في الـ Fragment عنها في النشاط (Activity) فلا نستطيع استخدام التابع ()setContentView، عوضًا عن ذلك نستخدم الكائن inflater الذي تم تمريره للتابع ()onCreateView ونستدعي به التابع ()inflate وقد تعاملنا مع هذا التابع من قبل في الدرس الخاص بقوائم العرض ListView، ونمرر للتابع المعاملات الآتية: الواجهة التي نريد ربطها. الواجهة التي ستحتوي واجهة الـ Fragment بداخلها وهي هنا container. قيمة منطقية تُعبر عن "هل نريد وضع واجهة الـ Fragment داخل الواجهة container -المعامل السابق- أم لا؟"، وسنقوم دائمًا عند التعامل مع الـ Fragments بالإجابة بلا أو القيمة المنطقية false؛ وذلك لأنها بشكل افتراضي يتم ربطها. ويقوم هذا التابع بتحويل ملف الواجهة (XML File) إلى كائن من الصنف View يمكن التعامل معه داخل شيفرات جافا بسهولة. بعد ذلك نعيد الكائن rootView إلى التابع، لتصبح الشيفرة النهائية على الشّكل التّالي: package apps.noby.simplefragment; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView =inflater.inflate(R.layout.fragment_example, container, false); return rootView; } } لاحظ أنه لكي تعمل الشيفرة بشكل سليم عند الوراثة من الصنف Fragment يجب أن تقوم بتضمين ;import android.app.Fragment. يتبقى الآن الخطوة الأخيرة وهي إيجاد مكان لهذا الـ Fragment داخل النشاط المضيف له، وفي هذا التطبيق النشاط المضيف هو النشاط الرئيسي (MainActivity)، ولوضع مكان للـ Fragment داخل ملف واجهة النشاط نقوم بتعريف عنصر واجهة جديد من النوع <fragment> وتحديد له بعض الخصائص كما يلي: <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:name="apps.noby.simplefragment.ExampleFragment" android:id="@+id/fragment_container" tools:layout="@layout/fragment_example" /> هناك بعض الخصائص المشتركة في العناصر كتحديد الطول والعرض وهناك خصائص تميز العنصر ولابد من تحديدها مثل android:name وتكمن أهميته في تحديد الصنف الذي سيُعرض داخل هذا العنصر ولن يعمل التطبيق بدون وضع قيمة لهذه الخاصية وهنا وضعنا اسم الصنف الذي صنعناه سابقًا مع وضع اسم الحزمة كاملًا قبله. ولابد أيضًا من تحديد id مميز لهذا العنصر. وسنضع داخل ملف الواجهة الرئيسي نص قبل واجهة الـ Fragment ليصبح ملف الواجهة كالتالي: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Main Activity!" android:layout_gravity="center" android:textSize="25sp"/> <fragment android:layout_width="match_parent" android:layout_height="match_parent" android:name="apps.noby.simplefragment.ExampleFragment" android:id="@+id/fragment_container" tools:layout="@layout/fragment_example" /> </LinearLayout> ولن نقوم بتغيير الشيفرة الخاصة بالنشاط الرئيسي وسندعها كما هي، الآن يمكننا تجربة التطبيق على المحاكي للتأكد من عمله كما ينبغي. وتُسمى هذه الطريقة بالطريقة الساكنة لتضمين الـ Fragment؛ وذلك لأننا لم نحتج إلى كتابة شيفرات داخل النشاط لوضع الـ Fragment بداخله. تطبيق 2 في هذا التطبيق سنقوم بعرض أكثر من Fragment بداخل نشاط واحد عند توفر المساحة المناسبة له، أما إذا لم تتوفر فسيتم عرض Fragment واحد فقط. سنقوم بتعديل على المثال السابق وعمل Fragment جديد يُدعى DetailsFragment بنفس الطريقة السابقة ونجعل له الواجهة التالية: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffc829" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Hello From Details Fragment" android:gravity="center"/> </LinearLayout> وتتشابه الواجهة السابقة مع واجهة الـ Fragment الآخر، بعد ذلك سنقوم بعمل ملف واجهة جديد يُدعى activity_main ولكن سنختار أن يوضع في المجلد layout-large كما في الصورة التالية: وبداخل ملف الواجهة الجديد نضع مكان آخر للـ Fragment الجديد بجانب الأول. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Main Activity!" android:layout_gravity="center" android:textSize="25sp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <fragment android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:name="apps.noby.simplefragment.ExampleFragment" android:id="@+id/fragment_container" tools:layout="@layout/fragment_example" /> <fragment android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:name="apps.noby.simplefragment.DetailsFragment" android:id="@+id/fragment_details" tools:layout="@layout/fragment_details" /> </LinearLayout> </LinearLayout> ووضع القيم الخاصة بالخاصيتين name و id، والشيفرة الخاصة بملف جافا الخاص بـ DetailsFragment يتشابه مع ExampleFragment. package apps.noby.simplefragment; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class DetailsFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_details,container,false); return rootView; } } نستطيع الآن تجربة التطبيق بعد إجراء هذا التعديل على محاكي للهاتف وآخر للجهاز اللوحي -إذا لم تكن صنعت محاكيًا للحاسب اللوحي فينبغي أن تصنع واحدًا قبل التجربة-. بناءً على الحجم الخاص بشاشة الجهاز يقوم التطبيق باستدعاء ملف الواجهة الخاص بالنشاط المناسب (العادي أو الكبير). لاحظ أنه يجب أن يكون بجانب اسم المجلد layout كلمة large حتى يعلم التطبيق بوجود ملفات خاصة بالشاشات ذات الحجم الكبير. تطبيق 3 في هذا التطبيق سنقوم بتغيير طريقة تضمين الـ Fragment من الطريقة الساكنة إلى الديناميكية أي إمكانية إضافة، تبديل حذف الـ Fragment أثناء تشغيل التطبيق والتحكم به عن طريق الشيفرة. وللقيام بذلك سنستبدل عنصر الواجهة <fragment> بالعنصر <FrameLayout> والذي يقوم بحجز مساحة فارغة في الواجهة سيتم تحديد فيما بعد ما الذي سيشغلها. كل ما سيختلف في هذا التطبيق ملفات الواجهة الرئيسية الخاصة بالنشاط المضيف فقط ولن يحدث تغيير في الواجهة الخاصة بالـ Fragment. <?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:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Main Activity!" android:layout_gravity="center" android:textSize="25sp"/> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/view_container" /> </LinearLayout> ولتحديد المحتوى الخاص بالعنصر FrameLayout ننتقل إلى ملف النشاط MainActivity.java لتعديله، الآن لإضافة Fragment جديد داخل الواجهة ينبغي أن نمر بثلاث خطوات 1. إيجاد كائن من الصنف FragmentManger والذي يستطيع إدارة كافة المهام الخاصة بالتعامل مع الـ Fragments ومن ضمن هذه المهام هي إضافة الـ Framgments ولجلب كائن من هذا الصنف نستدعى التابع ()getFragmentManager حيث يوجد كائن بالفعل داخل النشاط ويكتفي استدعاءه فقط. FragmentManager fragmentManager = getFragmentManager(); 2. للتحكم في المهام الخاصة بإضافة أو تبديل او إزالة الـ Fragments نحتاج إلى كائن من الصنف FragmentTransaction والذي يحتوي على التوابع التي تقوم بعملية الإضافة تلك. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); 3. أخيرًا نستطيع استدعاء التابع المناسب للعملية وتمرير له المعاملات الصحيحة. ExampleFragment frag = new ExampleFragment(); fragmentTransaction.add(R.id.view_container, frag); fragmentTransaction.commit(); يستطيع التابع ()add من إضافة Fragment إلى الواجهة التي سيُعرض بها، وتلك هي المعاملات التي نمررها له: الـ id الخاص بعنصر الواجهة. كائن من الـ Fragment الذي نرغب في عرضه بداخل عنصر الواجهة. وبعد ذلك نستدعي التابع ()commit لتنفيذ العملية السابقة، حيث لن يتم تنفيذها إلا بعد استدعاءه. لتصبح الشيفرة الكاملة للنشاط كما يلي: package apps.noby.simplefragment; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(findViewById(R.id.view_container) != null){ FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); ExampleFragment frag = new ExampleFragment(); fragmentTransaction.add(R.id.view_container, frag); fragmentTransaction.commit(); } } } وفائدة الجملة الشرطية هو التأكد من أننا نتعامل مع ملف الواجهة activity_main.xml الخاص بالهواتف والذي يحتوي بداخله على عنصر له id قيمته view_container، وليس ملف الواجهة الآخر الخاص بالشاشات كبيرة الحجم حيث لا يحتوي على عنصر له ذلك الـ id. والآن بتجربة التطبيق على كلا المحاكيين -الهاتف والحاسب اللوحي- نجد أنه يعمل كالتطبيقات السابقة. تطبيق 4 في التطبيقات السابقة لم نتمكن من عرض الـ Fragment الآخر في حالة الهاتف وذلك لأن التبديل بين Fragment وآخر لا تتم إلا داخل النشاط المضيف، لذا ينبغي أن نجعل الشيفرة الخاصة بالـ Fragment قادرة على التحدث مع النشاط المضيف لها وتنفيذ شيفرات بداخله. ويمكننا القيام بذلك باستخدام interface كحلقة وصل بين النشاط المضيف والـ Fragment. نضع أولًا زر في الواجهة الخاصة بـ fragment_example.xml عند الضغط عليه ننتقل إلى الـ fragment_details كالآتي. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ccefff" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello From Example Fragment"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Go to Details Frafment" android:id="@+id/go_to_btn"/> </LinearLayout> ثم ننتقل إلى الشيفرة الخاصة بـ ExampleFragment.java وبداخلها نقوم بصنع interface جديد يُدعى OnBtnClicked وبداخله التابع ()goToFragment. public interface OnBtnClicked{ public void goToFragment(); } ثم نجعل الصنف الخاص بالنشاط المضيف يقوم باستخدام هذا الـ interface. public class MainActivity extends Activity implements ExampleFragment.OnBtnClicked نعود مجددًا للشيفرة الخاصة بـ ExampleFragmen.java ونقوم بكتابة التابع ()onAttach الخاص بدورة الحياة للـ Fragment كالتالي: private MainActivity mContext; @Override public void onAttach(Context context) { super.onAttach(context); mContext = (MainActivity) context; } ووظيفة الشيفرة السابقة جعل الشيفرة الخاصة بالـ Fragment تستطيع التواصل مع النشاط المضيف باستخدام كائن منه يُمرر لحظة ربط الـ fragment بالنشاط المضيف. ثم بعد ذلك لربط الزر الذي قمنا بإضافته في الواجهة بالشيفرة نستخدم التابع ()findViewById ولكن هذه المرة باستخدام الكائن rootView وذلك لأنه يُعبر عن الواجهة فلا يمكننا من استدعاء التابع مباشرة كما كان داخل النشاط. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView =inflater.inflate(R.layout.fragment_example, container, false); Button btn = (Button)rootView.findViewById(R.id.go_to_btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mContext.goToFragment(); } }); return rootView; } ونجعل الزر مستعدًا للاستجابة عند الضغط عليه على أن يقوم باستدعاء التابع ()goToFragment باستخدام الكائن الخاص بالنشاط المضيف. لتصبح الشيفرة النهائية لهذا الـ Fragment كالتالي: package apps.noby.simplefragment; import android.content.Context; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; public class ExampleFragment extends Fragment { private MainActivity mContext; @Override public void onAttach(Context context) { super.onAttach(context); mContext = (MainActivity) context; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView =inflater.inflate(R.layout.fragment_example, container, false); Button btn = (Button)rootView.findViewById(R.id.go_to_btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mContext.goToFragment(); } }); return rootView; } public interface OnBtnClicked{ public void goToFragment(); } } يتبقى فقط إضافة الوظيفة التي نريدها عن الضغط على هذا الزر وذلك بكتابة الشيفرة الخاصة بالتابع ()goToFragment داخل النشاط المضيف. package apps.noby.simplefragment; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends Activity implements ExampleFragment.OnBtnClicked { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(findViewById(R.id.view_container) != null){ FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); ExampleFragment frag = new ExampleFragment(); fragmentTransaction.add(R.id.view_container, frag); fragmentTransaction.commit(); } } @Override public void goToFragment() { if(findViewById(R.id.view_container) != null){ FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); DetailsFragment frag = new DetailsFragment(); fragmentTransaction.replace(R.id.view_container, frag); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); } } } وتتشابه الشيفرة داخل التابع ()goToFragment مع الأخرى المتواجدة داخل ()onCreate ولكننا نستخدم التابع ()replace بدلًا من ()add وذلك لتبديل الواجهات داخل نفس العنصر FrameLayout بشكل ديناميكي، كما نستدعي التابع ()addToBackStack وذلك حتى نضيف واجهة الـ Fragment السابق في الذاكرة الخاصة بزر الرجوع حتى نعود إليها عند الضغط على زر الرجوع وإلا سيتم غلق التطبيق. وتوضح الصورة التالية ما المقصود بذلك: والآن عند تجربة التطبيق على المحاكي (الهاتف أو الحاسب اللوحي) نجد أننا نستطيع الوصول إلى الـ DetailsFragment عند الضغط على الزر. تطبيق 5 في التطبيق التالي سنقوم بإرسال نص من Fragment إلى آخر للتحدث فيما بينهما. ويتم ذلك عن طريق استخدام التابع ()setArguments وللقيام بذلك نقوم بتلك التعديلات على التطبيق السابق. في ملف الواجهة fragment_example.xml نضيف عنصر EditText بالخصائص الآتية. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ccefff" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello From Example Fragment"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:hint="Enter Your Name" android:id="@+id/edit_txt"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Go to Details Frafment" android:id="@+id/go_to_btn"/> </LinearLayout> وفي ملف الواجهة fragment_details نضيف الخاصية id للعنصر TextView. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ffc829" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="Hello From Details Fragment" android:gravity="center" android:id="@+id/txt_view"/> </LinearLayout> وفي ملف الواجهة الرئيسي الخاص بالشاشات ذات الحجم الكبير نقوم بتغيير العنصر fragment الثاني بالعنصر FrameLayout. <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Main Activity!" android:layout_gravity="center" android:textSize="25sp"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <fragment android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:name="apps.noby.simplefragment.ExampleFragment" android:id="@+id/fragment_container" tools:layout="@layout/fragment_example" /> <FrameLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:id="@+id/fragment_details"/> </LinearLayout> </LinearLayout> داخل الشيفرة الخاصة بـ ExampleFragment.java نقوم بتغيير التابع داخل الـ interface ليقبل تمرير نص، ثم عند الضغط على الزر نرسل النص المكتوب داخل عنصر الواجهة EditText إلى التابع ()goToFragment. package apps.noby.simplefragment; import android.content.Context; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; public class ExampleFragment extends Fragment { private MainActivity mContext; @Override public void onAttach(Context context) { super.onAttach(context); mContext = (MainActivity) context; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View rootView =inflater.inflate(R.layout.fragment_example, container, false); Button btn = (Button)rootView.findViewById(R.id.go_to_btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText et = (EditText) rootView.findViewById(R.id.edit_txt); String name = et.getText().toString(); mContext.goToFragment(name); } }); return rootView; } public interface OnBtnClicked{ public void goToFragment(String name); } } داخل شيفرة النشاط الرئيسي MainActivity.java سنقوم بإرسال النص المرسل مع الكائن DetailsFragment باستخدام التابع ()setArguments. package apps.noby.simplefragment; import android.app.Activity; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends Activity implements ExampleFragment.OnBtnClicked { public static final String ARGUMENT_NAME = "name"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(findViewById(R.id.view_container) != null){ FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); ExampleFragment frag = new ExampleFragment(); fragmentTransaction.add(R.id.view_container, frag); fragmentTransaction.commit(); } } @Override public void goToFragment(String name) { FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); DetailsFragment frag = new DetailsFragment(); Bundle bundle = new Bundle(); bundle.putString(ARGUMENT_NAME,name); frag.setArguments(bundle); if(findViewById(R.id.view_container) != null){ fragmentTransaction.replace(R.id.view_container, frag); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); } else{ fragmentTransaction.replace(R.id.fragment_details, frag); fragmentTransaction.commit(); } } } وتم إضافة حالتين الأولى للواجهات الصغير والأخرى عن التعامل مع الواجهات الكبيرة. أخيرًا نقوم باستقبال النص في الشيفرة الخاصة بـ DetailsFragment.java وعرضه بداخل العنصر TextView. package apps.noby.simplefragment; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class DetailsFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_details,container,false); TextView tv = (TextView) rootView.findViewById(R.id.txt_view); Bundle bundle = getArguments(); String str = bundle.getString(MainActivity.ARGUMENT_NAME); tv.setText("Hello " + str + " From Details Fragment"); return rootView; } } بعد ذلك نقوم بتشغيل التطبيق على المحاكي وتجربته للتأكد من أداءه للوظيفة المطلوبة بشكل سليم. بهذا نكون قد وصلنا إلى نهاية هذا الدرس، في انتظار تجربتكم وآرائكم.