الأربعاء، 18 أكتوبر 2017

فتات Fragment


فتات Fragment



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





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

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

عند إضافة جزء كجزء من تخطيط النشاط الخاص بك، فإنه يعيش في مجموعة ViewGroup داخل التسلسل الهرمي عرض النشاط ويحدد جزء تخطيط وجهة نظر الخاصة بها. يمكنك إدراج جزء في تخطيط النشاط من خلال الإعلان عن الجزء الموجود في ملف التخطيط للنشاط، كعنصر <fragment> ViewGroup <fragment> أو من شفرة التطبيق من خلال إضافته إلى مجموعة عرض حالية. ومع ذلك، لا يشترط أن يكون جزء من تخطيط النشاط؛ يمكنك أيضا استخدام جزء بدون واجهة المستخدم الخاصة به كعامل غير مرئي للنشاط.

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

للحصول على معلومات حول التعامل مع دورات الحياة، بما في ذلك إرشادات حول أفضل الممارسات، راجع التعامل مع دورات الحياة .

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

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




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


الشكل 1. مثال على كيفية دمج اثنين من وحدات واجهة المستخدم التي تحددها شظايا في نشاط واحد لتصميم الكمبيوتر اللوحي، ولكن فصل لتصميم الهاتف.

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

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

إنشاء جزء



لإنشاء جزء، يجب إنشاء فئة فرعية من Fragment (أو فئة فرعية موجودة منه). تحتوي فئة Fragment على كود يبدو كثيرا مثل Activity . يحتوي على أساليب رد الاتصال مشابهة لنشاط مثل onCreate() onStart() onPause() و onStop() . في الواقع، إذا كنت تقوم بتحويل تطبيق أندرويد موجود إلى استخدام الشظايا، فبإمكانك ببساطة نقل الرمز من أساليب معاودة الاتصال الخاصة بالنشاط إلى أساليب رد الاتصال الخاصة بك في الجزء الخاص بك.

عادة، يجب عليك تنفيذ أساليب دورة الحياة التالية على الأقل:

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

هناك أيضا عدد قليل من الفئات الفرعية التي قد ترغب في تمديد، بدلا من الطبقة Fragment قاعدة:

DialogFragment
يعرض مربع حوار عائم. يعد استخدام هذه الفئة لإنشاء مربع حوار بديلا جيدا لاستخدام أساليب مساعد الحوار في فئة " Activity ، لأنه يمكنك تضمين مربع الحوار جزء في كومة الخلفي من شظايا تدار من قبل النشاط، مما يسمح للمستخدم بالعودة إلى جزء مفصول.
ListFragment
يعرض قائمة بالعناصر التي يديرها مهايئ (مثل SimpleCursorAdapter )، على غرار ListActivity . فإنه يوفر عدة طرق لإدارة عرض قائمة مثل onListItemClick() رد الاتصال للتعامل مع أحداث النقر.
PreferenceFragment
يعرض تسلسل هرمي Preference كقائمة، على غرار PreferenceActivity . وهذا مفيد عند إنشاء نشاط "إعدادات" للتطبيق الخاص بك.
إضافة واجهة مستخدم
يتم استخدام جزء عادة كجزء من واجهة المستخدم النشاط ويسهم تخطيطه الخاص للنشاط.

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

ملاحظة: إذا كان الجزء الخاص بك فئة فرعية من ListFragment ، يقوم التنفيذ الافتراضي بإرجاع onCreateView() من onCreateView() ، لذلك لا تحتاج إلى تنفيذه.

لإرجاع تخطيط من onCreateView() ، يمكنك onCreateView() من مورد تخطيط محدد في شمل. لمساعدتك على القيام بذلك، onCreateView() يوفر كائن LayoutInflater .

على سبيل المثال، في ما يلي فئة فرعية من Fragment الذي يحمل تخطيطا من الملف example_fragment.xml :


public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }

}

إنشاء تخطيط
في النموذج أعلاه، R.layout.example_fragment هو مرجع لمورد تخطيط باسم example_fragment.xml محفوظ في موارد التطبيق. للحصول على معلومات حول كيفية إنشاء تخطيط في شمل، راجع وثائق واجهة المستخدم .
معلمة container تم تمريرها إلى onCreateView() هي مجموعة العرض الرئيسية (من تخطيط النشاط) الذي سيتم إدراج تخطيط الجزء الخاص به. المعلمة savedInstanceState هي Bundle توفر بيانات حول المثيل السابق من الجزء، إذا تم استئناف الجزء (تتم مناقشة استعادة الدولة أكثر في القسم حول التعامل مع دورة حياة جزء ).

تأخذ طريقة inflate() ثلاث حجج:

معرف المورد للتخطيط الذي تريد تضخيمه.
و ViewGroup لتكون الوالد للتخطيط مبالغ فيه. إن تمرير container أمر مهم لكي يقوم النظام بتطبيق معلمات التخطيط على طريقة عرض الجذر للتخطيط المضخم المحدد من قبل العرض الرئيسي الذي سيذهب إليه.
منطقية تشير إلى ما إذا كان يجب إرفاق التخطيط المضخم إلى مجموعة ViewGroup (المعلمة الثانية) أثناء التضخم. (في هذه الحالة، هذا غير صحيح لأن النظام يقوم بالفعل بإدراج التخطيط المضخم في container فإن تمرير صحيح سيؤدي إلى إنشاء مجموعة عرض زائدة في التخطيط النهائي.)
الآن لقد رأيت كيفية إنشاء جزء يوفر تخطيطا. بعد ذلك، يجب إضافة الجزء إلى نشاطك.

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

إعلان الجزء داخل ملف تخطيط النشاط.

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment android:name="com.example.news.ArticleListFragment"
            android:id="@+id/list"
            android:layout_weight="1"
            android:layout_width="0dp"
            android:layout_height="match_parent" />
    <fragment android:name="com.example.news.ArticleReaderFragment"
            android:id="@+id/viewer"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="match_parent" />

</LinearLayout>

تحدد السمة android:name <fragment> فئة فراغمينت للإنشاء في التخطيط.
عندما يقوم النظام بإنشاء تخطيط النشاط هذا، يقوم onCreateView() كل جزء محدد في التخطيط ويدعو الأسلوب onCreateView() لكل واحد، لاسترداد تخطيط كل جزء. يقوم النظام بإدراج View التي يتم إرجاعها بواسطة الجزء مباشرة بدلا من العنصر <fragment> .
ملاحظة: كل جزء يتطلب معرف فريد يمكن للنظام استخدامه لاستعادة الجزء إذا تم إعادة تشغيل النشاط (والتي يمكنك استخدامها لالتقاط جزء لتنفيذ المعاملات، مثل إزالته). هناك ثلاث طرق لتوفير معرف لجزء:
توريد android:id السمة android:id مع معرف فريد من نوعه.
توريد android:tag السمة android:tag مع سلسلة فريدة من نوعها.
إذا لم تقدم أي من النظامين السابقين، يستخدم النظام معرف عرض الحاوية.
أو، برمجيا إضافة جزء إلى مجموعة عرض موجودة.
في أي وقت أثناء تشغيل نشاطك، يمكنك إضافة أجزاء إلى تخطيط النشاط. تحتاج ببساطة إلى تحديد ViewGroup التي لوضع جزء.

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



FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
يمكنك بعد ذلك إضافة جزء باستخدام طريقة add() ، وتحديد الجزء المراد إضافته والرأي الذي تريد إدراجه فيه. فمثلا:
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();



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

لإضافة جزء بدون واجهة مستخدم، أضف الجزء من النشاط باستخدام add(Fragment, String) (توفير سلسلة "علامة" فريدة add(Fragment, String) ، بدلا من معرف الملف الشخصي). هذا يضيف جزء، ولكن، لأنه لا يرتبط مع عرض في تخطيط النشاط، فإنه لا يتلقى مكالمة إلى onCreateView() . لذلك لا تحتاج إلى تنفيذ هذه الطريقة.

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

على سبيل المثال النشاط الذي يستخدم جزء كعميل خلفية، من دون واجهة المستخدم، راجع عينة FragmentRetainInstance.java ، التي يتم تضمينها في عينات سك (متوفرة من خلال مدير أندرويد سك) والموجودة على النظام الخاص بك كما <sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java .

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

بعض الأشياء التي يمكنك القيام به مع FragmentManager ما يلي:

الحصول على شظايا موجودة في النشاط، مع findFragmentById() (للشظايا التي توفر واجهة مستخدم في تخطيط النشاط) أو findFragmentByTag() (للشظايا التي تقوم أو لا توفر واجهة مستخدم).
شظايا البوب ​​قبالة كومة الخلفي، مع popBackStack() (محاكاة أمر العودة من قبل المستخدم).
تسجيل مستمع للتغييرات إلى كومة الخلفي، مع addOnBackStackChangedListener() .
لمزيد من المعلومات حول هذه الأساليب وغيرها، راجع وثائق فئة FragmentManager .

كما هو موضح في القسم السابق، يمكنك أيضا استخدام FragmentManager لفتح FragmentTransaction ، والذي يسمح لك لتنفيذ المعاملات، مثل إضافة وإزالة أجزاء.

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

يمكنك الحصول على مثيل FragmentTransaction من FragmentManager مثل هذا:


FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();


كل معاملة هي مجموعة من التغييرات التي تريد تنفيذها في نفس الوقت. يمكنك إعداد كافة التغييرات التي تريد إجراءها معاملة معينة باستخدام أساليب مثل add() ، remove() ، replace() . ثم، لتطبيق المعاملة على النشاط، يجب استدعاء commit() .

قبل استدعاء commit() ، ومع ذلك، قد ترغب في الاتصال addToBackStack() ، من أجل إضافة المعاملة إلى كومة الخلفي من المعاملات جزء. ويدير هذه المكدس الخلفي من قبل النشاط ويسمح للمستخدم بالعودة إلى حالة الجزء السابق، عن طريق الضغط على زر العودة .

على سبيل المثال، إليك كيف يمكنك استبدال جزء واحد بآخر، والحفاظ على الحالة السابقة في المكدس الخلفي:

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();


في هذا المثال، يحل newFragment محل أي جزء (إن وجد) موجود حاليا في حاوية التخطيط المحددة بواسطة معرف R.id.fragment_container . من خلال الاتصال addToBackStack() ، يتم حفظ المعاملة استبدال إلى كومة الخلفي بحيث يمكن للمستخدم عكس الصفقة وإعادة الجزء السابق عن طريق الضغط على زر العودة .

لاسترداد أجزاء من كومة الخلفية، يجب عليك تجاوز onBackPressed() في فئة النشاط الرئيسي:


@Override
public void onBackPressed() {
    if (getFragmentManager().getBackStackEntryCount() > 0) {
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
}

إذا كنت لا تجاوز onBackPressed() وهناك نشاط سابق على كومة الخلفي، والضغط على زر العودة يسبب التطبيق للعودة إلى هذا النشاط. إذا لم يكن هناك نشاط سابق، والضغط مرة أخرى يسبب التطبيق لإغلاق.

إذا قمت بإضافة تغييرات متعددة على المعاملة (مثل add() أخرى add() أو remove() ) واستدعاء addToBackStack() ، ثم يتم إضافة كافة التغييرات التي تم تطبيقها قبل استدعاء addToBackStack() إلى المكدس الخلفي كعملية واحدة وزر العودة سوف عكسها جميعا معا.

لا يهم الترتيب الذي تقوم بإضافة التغييرات إلى FragmentTransaction ، باستثناء:

يجب استدعاء commit() الماضي
إذا كنت تضيف أجزاء متعددة إلى الحاوية نفسها، فسيحدد الترتيب الذي تضيف إليه الترتيب الذي تظهر به في التسلسل الهرمي للعرض
إذا كنت لا استدعاء addToBackStack() عند إجراء معاملة إزالة جزء، ثم يتم تدمير هذا الجزء عند ارتكاب المعاملة ولا يمكن للمستخدم الانتقال إلى ذلك. حيث، إذا قمت باستدعاء addToBackStack() عند إزالة جزء، ثم يتم إيقاف جزء وسوف تستأنف إذا كان المستخدم يتنقل مرة أخرى.

تلميح: لكل معاملة جزء، يمكنك تطبيق الرسوم المتحركة الانتقالية، عن طريق استدعاء setTransition() قبل ارتكاب.

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


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

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

على وجه التحديد، يمكن getActivity() الوصول إلى مثيل Activity مع getActivity() وسهولة أداء المهام مثل العثور على طريقة عرض في تخطيط النشاط:

View listView = getActivity().findViewById(R.id.list);

وبالمثل، يمكن أن نشاطك استدعاء أساليب في جزء من خلال الحصول على مرجع إلى Fragment من findFragmentById() ، وذلك باستخدام findFragmentById() أو findFragmentByTag() . فمثلا:





 ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);


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

على سبيل المثال، إذا كان تطبيق الأخبار يحتوي على شظيتين في نشاط واحد لعرض قائمة المقالات (الجزء أ) وأخرى لعرض مقالة (الجزء ب) - يجب أن يكون الجزء أ من النص على النشاط عند تحديد عنصر قائمة أنه يمكن أن أقول جزء B لعرض المقالة. في هذه الحالة، يتم الإعلان عن واجهة OnArticleSelectedListener داخل جزء A:
public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

ثم يقوم النشاط الذي يستضيف الجزء بتنفيذ واجهة OnArticleSelectedListener ويتجاوز onArticleSelected() لإعلام الجزء ب من الحدث من جزء A. للتأكد من أن نشاط المضيف ينفذ هذه الواجهة طريقة رد onAttach() على onAttach() الذي يستدعي النظام عندما إضافة جزء إلى النشاط) مثيل مثيل OnArticleSelectedListener عن طريق صب Activity الذي تم تمريره إلى onAttach() :


 public static class FragmentA extends ListFragment {


    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}
 إذا لم ينفذ النشاط الواجهة، ثم يلقي جزء ClassCastException . على النجاح، عضو mListener يحمل إشارة إلى تنفيذ النشاط OnArticleSelectedListener ، بحيث يمكن أن الجزء أ مشاركة الأحداث مع النشاط من خلال استدعاء أساليب المعرفة من خلال واجهة OnArticleSelectedListener . على سبيل المثال، إذا كان الجزء "أ" امتدادا ل ListFragment ، في كل مرة ينقر المستخدم على عنصر قائمة، يستدعي النظام onListItemClick() في الشظية، ثم يقوم باستدعاء onArticleSelected() لمشاركة الحدث مع النشاط:


public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}


معلمة id تم تمريرها إلى onListItemClick() هي معرف الصف للعنصر الذي تم النقر عليه، والذي يستخدمه النشاط (أو جزء آخر) لجلب المقالة من ContentProvider للتطبيق.

يتوفر المزيد من المعلومات حول استخدام موفر المحتوى في مستند موفري المحتوى .

إضافة عناصر إلى شريط التطبيقات
يمكن للشظايا الخاصة بك المساهمة عناصر القائمة إلى القائمة خيارات النشاط (و، وبالتالي شريط التطبيق ) بتنفيذ onCreateOptionsMenu() . من أجل هذه الطريقة لاستقبال المكالمات، ومع ذلك، يجب استدعاء setHasOptionsMenu() أثناء onCreate() ، للإشارة إلى أن الجزء ترغب في إضافة عناصر إلى قائمة الخيارات (وإلا، لن تتلقى جزء مكالمة على onCreateOptionsMenu() ).

يتم إلحاق أية عناصر ثم قمت بإضافتها إلى قائمة الخيارات من الجزء إلى عناصر القائمة الموجودة. يتلقى الشظية أيضا onOptionsItemSelected() إلى onOptionsItemSelected() عند تحديد عنصر القائمة.

يمكنك أيضا تسجيل وجهة نظر في تخطيط جزء الخاص بك لتوفير قائمة السياق عن طريق استدعاء registerForContextMenu() . عندما يفتح المستخدم قائمة السياق، يتلقى جزء مكالمة إلى onCreateContextMenu() . عندما يقوم المستخدم بتحديد عنصر، يتلقى جزء مكالمة إلى onContextItemSelected() .

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

لمزيد من المعلومات حول القوائم، راجع دليل مطوري القوائم وفئة تدريب بار أب .


التعامل مع دورة حياة جزء



إدارة دورة حياة جزء هو الكثير مثل إدارة دورة حياة النشاط. مثل أي نشاط، يمكن أن يوجد جزء في ثلاث ولايات:

استأنف
الجزء مرئي في النشاط قيد التشغيل.
توقف
وهناك نشاط آخر في المقدمة ولديه تركيز، ولكن النشاط الذي يعيش فيه هذا الجزء لا يزال مرئيا (النشاط الأمامي شفاف جزئيا أو لا يغطي كامل الشاشة).
توقفت
الجزء غير مرئي. إما تم إيقاف نشاط المضيف أو إزالة الجزء من النشاط ولكن تمت إضافته إلى المكدس الخلفي. لا يزال جزء توقف لا يزال على قيد الحياة (يتم الاحتفاظ جميع المعلومات الدولة والعضو من قبل النظام). ومع ذلك، فإنه لم يعد مرئيا للمستخدم وسيتم قتل إذا قتل النشاط.
أيضا مثل النشاط، يمكنك الاحتفاظ حالة جزء باستخدام Bundle ، في حالة قتل عملية النشاط وتحتاج إلى استعادة حالة جزء عندما يتم إعادة إنشاء النشاط. يمكنك حفظ الدولة أثناء onSaveInstanceState() واستعادة ذلك أثناء onCreate() أو onCreateView() أو onActivityCreated() . لمزيد من المعلومات حول حفظ الحالة، راجع وثيقة الأنشطة .

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

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

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

التنسيق مع دورة حياة النشاط
تؤثر دورة حياة النشاط الذي يؤثر فيه الجزء بشكل مباشر على دورة حياة الجزء، بحيث يؤدي كل رد على دورة حياة النشاط إلى رد مماثل لكل جزء. على سبيل المثال، عندما يتلقى النشاط onPause() ، تتلقى كل جزء في النشاط onPause() .

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

onAttach()
يتم استدعاؤها عند ارتباط الشظية بالنشاط (يتم تمرير Activity هنا).
onCreateView()
تمت الدعوة لإنشاء التسلسل الهرمي للعرض المرتبط بالجزء.
onActivityCreated()
تمت onCreate() عند onCreate() أسلوب onCreate() .
onDestroyView()
يتم الاتصال بها عند إزالة التسلسل الهرمي للعرض المرتبط بالجزء.
onDetach()
يطلق عليه عندما يتم فصل جزء من النشاط.
ويوضح الشكل 3. تدفق دورة حياة جزء، كما يتأثر نشاط المضيف الخاص به، في هذا الشكل، يمكنك أن ترى كيف أن كل حالة المتعاقبة من النشاط يحدد أساليب رد الاتصال التي قد تتلقى جزء. على سبيل المثال، عندما يتلقى النشاط onCreate() ، جزء في النشاط لا يتلقى أكثر من onActivityCreated() .

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

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

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

ملاحظة: تتوفر شفرة المصدر الكاملة لهذا النشاط في FragmentLayout.java . FragmentLayout.java .

النشاط الرئيسي ينطبق تخطيط بالطريقة المعتادة، أثناء onCreate() :



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.fragment_layout);
}
التنسيق المطبق هو fragment_layout.xml :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent" />

    <FrameLayout android:id="@+id/details" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent"
            android:background="?android:attr/detailsElementBackground" />

</LinearLayout>


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

ومع ذلك، ليست جميع تكوينات الشاشة واسعة بما فيه الكفاية لإظهار كل من قائمة المسرحيات والملخص، جنبا إلى جنب. لذلك، يتم استخدام تخطيط أعلاه فقط لتكوين الشاشة المشهد، من خلال حفظه في res/layout-land/fragment_layout.xml .

وهكذا، عندما تكون الشاشة في اتجاه عمودي، يطبق النظام التخطيط التالي، الذي يتم حفظه في res/layout/fragment_layout.xml :

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles"
            android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

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

بعد ذلك، يمكنك أن ترى كيف يتم تحقيق ذلك في الطبقات جزء. الأول هو TitlesFragment، مما يدل على قائمة من مسرحية شكسبير الألقاب. هذا جزء يمتد ListFragmentويعتمد عليها للتعامل مع أكثر من عمل عرض القائمة.

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

public static class TitlesFragment extends ListFragment {
    boolean mDualPane;
    int mCurCheckPosition = 0;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Populate list with our static array of titles.
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

        // Check to see if we have a frame in which to embed the details
        // fragment directly in the containing UI.
        View detailsFrame = getActivity().findViewById(R.id.details);
        mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;

        if (savedInstanceState != null) {
            // Restore last state for checked position.
            mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
        }

        if (mDualPane) {
            // In dual-pane mode, the list view highlights the selected item.
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            // Make sure our UI is in the correct state.
            showDetails(mCurCheckPosition);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("curChoice", mCurCheckPosition);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails(position);
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    void showDetails(int index) {
        mCurCheckPosition = index;

        if (mDualPane) {
            // We can display everything in-place with fragments, so update
            // the list to highlight the selected item and show the data.
            getListView().setItemChecked(index, true);

            // Check what fragment is currently shown, replace if needed.
            DetailsFragment details = (DetailsFragment)
                    getFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index);

                // Execute a transaction, replacing any existing fragment
                // with this one inside the frame.
                FragmentTransaction ft = getFragmentManager().beginTransaction();
                if (index == 0) {
                    ft.replace(R.id.details, details);
                } else {
                    ft.replace(R.id.a_item, details);
                }
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft.commit();
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            Intent intent = new Intent();
            intent.setClass(getActivity(), DetailsActivity.class);
            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}

جزء الثاني، DetailsFragmentيظهر ملخص المسرحية للعنصر المحدد من القائمة من TitlesFragment:




public static class DetailsFragment extends Fragment {
    /**
     * Create a new instance of DetailsFragment, initialized to
     * show the text at 'index'.
     */
    public static DetailsFragment newInstance(int index) {
        DetailsFragment f = new DetailsFragment();

        // Supply index input as an argument.
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);

        return f;
    }

    public int getShownIndex() {
        return getArguments().getInt("index", 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            // We have different layouts, and in one of them this
            // fragment's containing frame doesn't exist.  The fragment
            // may still be created from its saved state, but there is
            // no reason to try to create its view hierarchy because it
            // won't be displayed.  Note this is not needed -- we could
            // just run the code below, where we would create and return
            // the view hierarchy; it would just never be used.
            return null;
        }

        ScrollView scroller = new ScrollView(getActivity());
        TextView text = new TextView(getActivity());
        int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                4, getActivity().getResources().getDisplayMetrics());
        text.setPadding(padding, padding, padding, padding);
        scroller.addView(text);
        text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
        return scroller;
    }
}


نذكر من TitlesFragmentفئة، وأنه إذا كان المستخدم بالنقر فوق عنصر القائمة والتخطيط الحالي لا لا تتضمن R.id.detailsعرض (الذي هو المكان الذي DetailsFragmentينتمي)، ثم التطبيق يبدأ DetailsActivityالنشاط لعرض محتوى هذا البند.

هنا هو DetailsActivityالذي يضمن ببساطة DetailsFragmentلعرض ملخص المسرحية المختارة عندما تكون الشاشة في الاتجاه العمودي:

public static class DetailsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}


لاحظ أن هذا النشاط انتهاء نفسه إذا كان التكوين هو المشهد، حتى أن النشاط الرئيسي يمكن أن يستغرق أكثر وعرض DetailsFragmentجانب TitlesFragment. يمكن أن يحدث هذا إذا يبدأ المستخدم DetailsActivityأثناء وجوده في اتجاه عمودي، ولكن بعد ذلك بالتناوب إلى أفقي (التي إعادة تشغيل النشاط الحالي).

لمزيد من العينات باستخدام شظايا (والملفات المصدر كاملة لهذا المثال)، راجع API ديموس عينة التطبيق متوفر في ApiDemos (متوفر للتحميل من عنصر SDK عينات ).

ليست هناك تعليقات:

إرسال تعليق

للوضع الليلي في التطبيق DayNight

في هذا البرنامج التعليمي ، سنناقش ونستخدم موضوع Android DayNight في تطبيقنا. إذا كان لديك تطبيق يحتوي على مواد للقراءة ، فإن استخدام الوض...