التعامل مع تغييرات الضبط

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

  • حجم عرض التطبيق
  • اتجاه الشاشة
  • حجم الخطّ وسُمكه
  • اللغة
  • الوضع الداكن مقابل الوضع الفاتح
  • مدى توفر لوحة المفاتيح

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

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

الترفيه في النشاط

يعيد النظام إنشاء Activity عند حدوث تغيير في الإعدادات. للقيام بذلك، يستدعي النظام onDestroy() ويدمّر مثيل Activity الحالي. بعد ذلك، يتم إنشاء مثيل جديد باستخدام onCreate()، وتتم تهيئة مثيل Activity الجديد هذا في الإعدادات الجديدة والمُعدَّلة. هذا يعني أيضًا أن النظام يعيد إنشاء واجهة المستخدم بالتهيئة الجديدة.

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

مثال على الترفيه

لنفترض أنّ السمة TextView تعرض عنوانًا ثابتًا باستخدام السمة android:text="@string/title"، على النحو المحدّد في ملف XML للتنسيق. عند إنشاء طريقة العرض، يتم تعيين النص مرة واحدة فقط، بناءً على اللغة الحالية. وإذا تغيّرت اللغة، يعيد النظام إنشاء النشاط. وبالتالي، يُعيد النظام أيضًا إنشاء العرض ويضبطه على القيمة الصحيحة استنادًا إلى اللغة الجديدة.

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

توقعات المستخدم

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

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

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

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

للاطّلاع على مزيد من المعلومات عن واجهات برمجة التطبيقات لكل من هذه الخطوات بالتفصيل، وعندما يكون استخدام كلٍّ منها مناسبًا، يمكنك الاطّلاع على حفظ حالات واجهة المستخدم.

فرض قيود على الأنشطة الترفيهية

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

لإيقاف إنشاء النشاط عند إجراء تغييرات معيّنة على الإعدادات، أضِف نوع الإعداد إلى android:configChanges في إدخال <activity> في ملف AndroidManifest.xml. تظهر القيم المحتملة في مستندات السمة android:configChanges.

يوقف رمز البيان التالي عملية إنشاء Activity في MyActivity عند تغيير اتجاه الشاشة ومدى توفّر لوحة المفاتيح:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

دائمًا ما تتسبب بعض تغييرات الإعدادات في إعادة تشغيل النشاط. ولا يمكنك إيقافها. على سبيل المثال، لا يمكنك إيقاف تغيير الألوان الديناميكية المقدَّم في الإصدار Android 12L (المستوى 32 من واجهة برمجة التطبيقات).

التفاعل مع تغييرات الإعدادات في نظام العرض

في نظام View، عندما يحدث تغيير في الإعدادات تم من خلاله إيقاف إنشاء Activity، يتلقّى النشاط مكالمة إلى Activity.onConfigurationChanged(). يتم أيضًا إرسال إشعار إلى View.onConfigurationChanged() عند عرض أي ملفات شخصية مرفقة بهذا المحتوى. بالنسبة إلى تغييرات الإعدادات التي لم تُضفها إلى android:configChanges، يُعيد النظام إنشاء النشاط كالعادة.

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

على سبيل المثال، تتحقّق طريقة تنفيذ onConfigurationChanged() التالية من توفّر لوحة مفاتيح:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

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

الاحتفاظ بالحالة

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

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

التفاعل مع تغييرات الإعدادات في Jetpack Compose

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

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

مثال

في المثال التالي، يعرض عنصر يمكن إنشاؤه تاريخًا بتنسيق معيّن. تتفاعل تلك العناصر مع التغييرات في إعدادات لغة النظام من خلال استدعاء ConfigurationCompat.getLocales() باستخدام LocalConfiguration.current.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

لتجنُّب إعادة إنشاء Activity عند تغيير اللغة، يجب إيقاف تغييرات إعدادات اللغة على Activity التي تستضيف رمز Compose. لإجراء ذلك، يمكنك ضبط android:configChanges على locale|layoutDirection.

تغييرات الإعدادات: المفاهيم الأساسية وأفضل الممارسات

في ما يلي المفاهيم الأساسية التي تحتاج إلى معرفتها عند العمل على تغييرات الضبط:

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

لتقديم تجربة جيدة للمستخدم، يُرجى التنبّه لأفضل الممارسات التالية:

  • الاستعداد للتغييرات المتكررة التي يتم إجراؤها على الإعدادات: لا تفترض أنّ هذه التغييرات نادرة أو لا تحدث مطلقًا، بغض النظر عن مستوى واجهة برمجة التطبيقات أو شكل الجهاز أو مجموعة أدوات واجهة المستخدم. عندما يتسبب المستخدم في تغيير في الإعدادات، يتوقع أن يتم تحديث التطبيقات ومواصلة العمل بشكل صحيح باستخدام الإعدادات الجديدة.
  • الاحتفاظ بحالة المستخدم: لا تفقد حالة المستخدم عند حدوث إعادة تحميل Activity. احتفظ بالحالة كما هو موضح في حفظ حالات واجهة المستخدم.
  • تجنُّب إيقاف الميزة كحل سريع: لا توقِف إعادة إنشاء Activity كاختصار لتجنُّب فقدان الحالة. عند إيقاف ميزة إنشاء النشاط، عليك الوفاء بوعودك بالتعامل مع التغيير، وسيظل بإمكانك فقدان الحالة بسبب إعادة إنشاء Activity بسبب تغييرات أخرى في الإعدادات أو توقُّف العمليات أو إغلاق التطبيق. من المستحيل إيقاف ميزة "Activity" كليًا. احتفظ بالحالة كما هو موضح في حفظ حالات واجهة المستخدم.
  • لا تجنَّب التغييرات في الإعدادات: لا تفرض قيودًا على الاتجاه أو نسبة العرض إلى الارتفاع أو إمكانية تغيير الحجم لتجنّب التغييرات في الإعدادات وإعادة الضبط على Activity. ويؤثر ذلك سلبًا على المستخدمين الذين يريدون استخدام تطبيقك بطريقتهم المفضّلة.

التعامل مع تغييرات الإعدادات المستندة إلى الحجم

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

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

فرض قيود على إعادة إنشاء النشاط لتغييرات الإعدادات المستندة إلى الحجم

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

يتم إيقاف إعادة إنشاء Activity بسبب تغييرات الإعدادات المستندة إلى الحجم عندما يكون لديك android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" في ملف البيان.

السماح بإعادة إنشاء النشاط لتغييرات الإعدادات المستندة إلى الحجم

في نظام التشغيل Android 7.0 (المستوى 24 من واجهة برمجة التطبيقات) والإصدارات الأحدث، لا يحدث إعادة إنشاء Activity إلا عند حدوث تغييرات في الإعدادات المستندة إلى الحجم إذا كان التغيير في الحجم كبيرًا. في حال عدم إعادة إنشاء النظام Activity بسبب عدم كفاية الحجم، قد يطلب النظام استخدام Activity.onConfigurationChanged() وView.onConfigurationChanged() بدلاً من ذلك.

هناك بعض التنبيهات التي يجب ملاحظتها بشأن استدعاءات Activity وView عند عدم إعادة إنشاء Activity:

  • في إصدارات Android من Android 11 (المستوى 30 من واجهة برمجة التطبيقات) وحتى Android 13 (المستوى 33 من واجهة برمجة التطبيقات)، لا يتم تلقّي طلب من نظام التشغيل Activity.onConfigurationChanged().
  • هناك مشكلة معروفة تتمثل في عدم استدعاء View.onConfigurationChanged() في بعض الحالات على نظام التشغيل Android 12L (مستوى واجهة برمجة التطبيقات 32) والإصدارات الأقدم من Android 13 (مستوى واجهة برمجة التطبيقات 33). لمزيد من المعلومات، يُرجى الاطّلاع على هذه المشكلة العلنية. وقد تم حلّ هذه المشكلة في الإصدارات اللاحقة من نظام التشغيل Android 13 وفي الإصدار 14 من نظام التشغيل Android.

بالنسبة إلى الرموز التي تعتمد على الاستماع إلى تغييرات الإعدادات المستندة إلى الحجم، نقترح استخدام الأداة View مع رمز View.onConfigurationChanged() تم تجاوزه بدلاً من الاعتماد على إنشاء Activity أو Activity.onConfigurationChanged().