الحافة إلى الحافة على نظام Android: القيام بذلك بشكل صحيح

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



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


الحافة إلى الحافة على سبيل المثال من قذيفة نظام أندرويد.

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

إن مشكلة إنشاء واجهة من الحافة إلى الحافة ليست جديدة في حد ذاتها وكانت ذات صلة قبل وقت طويل من I / O 2019. بالتأكيد ، سيتذكر كل واحد منكم كيف قمت أولاً بزيارة google بشيء من الفئة: "شريط الحالة الشفاف android" أو "تدرج شريط الحالة android" " .

المعايير الرئيسية للتطبيق لمطابقة العنوان من الحافة إلى الحافة هي:

  • شريط الحالة الشفاف ؛
  • شريط التنقل الشفاف.

قراءة المزيد عنها على material.io .


لا يقلق تطبيق Deezer من الامتثال من الحافة إلى الحافة

من المهم ملاحظة أننا لا نتحدث عن إزالتها تمامًا ، كما في " وضع ملء الشاشة ". نترك للمستخدم الفرصة لرؤية معلومات النظام الهامة واستخدام التنقل المألوف.

متطلب مهم بنفس القدر لإيجاد حل وهو قابلية التوسع والقابلية للتوسعة. هناك عدد من الآخرين:

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

قليلا من الناحية النظرية


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

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


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


تطبيق


في تطبيقنا ، سوف نعمل بنشاط على نافذة وأعلامها.
سيتم كتابة جميع الأمثلة في Kotlin ، ولكن يمكنك تنفيذها بسهولة في Java ، وذلك باستخدام الأدوات المساعدة بدلاً من وظائف الامتداد.

أول ما يجب فعله بعنصر تخطيط الجذر هو تعيين العلامة بوضوح:

android:fitsSystemWindows="true" 

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

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

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

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


يجب ألا يُسمح بهذا السلوك (يزحف شريط الأدوات على شريط المعلومات).

تبدو وظيفة removeSystemInsets () كما يلي:

 fun removeSystemInsets(view: View, listener: OnSystemInsetsChangedListener) { ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -> val desiredBottomInset = calculateDesiredBottomInset( view, insets.systemWindowInsetTop, insets.systemWindowInsetBottom, listener ) ViewCompat.onApplyWindowInsets( view, insets.replaceSystemWindowInsets(0, 0, 0, desiredBottomInset) ) } } 

تحسب الدالة calculateDesiredBottomInset () الجزء السفلي من Inset مع أو بدون لوحة المفاتيح ، اعتمادًا على التكوين الحالي للجهاز.

 fun calculateDesiredBottomInset( view: View, topInset: Int, bottomInset: Int, listener: OnSystemInsetsChangedListener ): Int { val hasKeyboard = isKeyboardAppeared(view, bottomInset) val desiredBottomInset = if (hasKeyboard) bottomInset else 0 listener(topInset, if (hasKeyboard) 0 else bottomInset) return desiredBottomInset } 

استخدم طريقة isKeyboardAppeared () للتحقق من ارتفاع لوحة المفاتيح . لقد وثقنا في فرضية أن لوحة المفاتيح لا يمكن أن تشغل أقل من ربع ارتفاع الشاشة. إذا كنت ترغب في ذلك ، يمكنك تعديل منطق التحقق كما تريد.

 private fun View.isKeyboardAppeared(bottomInset: Int) = bottomInset / resourdisplayMetrics.heightPixels.toDouble() > .25 

يستخدم الأسلوب removeSystemInsets () مستمعًا. في الواقع ، هذه ليست سوى typealias للتعبير لامدا. رمزها الكامل:

 typealias OnSystemBarsSizeChangedListener = (statusBarSize: Int, navigationBarSize: Int) -> Unit 

الخطوة التالية هي ضبط الشفافية على أشرطة النظام:

 window.statusBarColor = Color.TRANSPARENT window.navigationBarColor = Color.TRANSPARENT 

بعد تجميع كل ما سبق ، نحصل على الطريقة التالية:

 fun Activity.setWindowTransparency( listener: OnSystemInsetsChangedListener = { _, _ -> } ) { InsetUtil.removeSystemInsets(window.decorView, listener) window.navigationBarColor = Color.TRANSPARENT window.statusBarColor = Color.TRANSPARENT } 

الآن ، لتمكين وضع الحافة إلى الحافة للنشاط المطلوب ، تحتاج فقط إلى استدعاء الوظيفة التالية في طريقة onCreate () :

 setWindowTransparency { statusBarSize, navigationBarSize -> //  } 

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




يطير في مرهم


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

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

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

 class KitkatTransparentSystemBarsFrame @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = -1 ) : FrameLayout(context, attrs, defStyleAttr), KitkatTransparentSystemBarsContainer { override var onSystemInsetsChangedListener: OnSystemInsetsChangedListener = { _, _ -> } override fun fitSystemWindows(insets: Rect?): Boolean { insets ?: return false val desiredBottomInset = InsetUtil.calculateDesiredBottomInset( this, insets.top, insets.bottom, onSystemInsetsChangedListener ) return super.fitSystemWindows(Rect(0, 0, 0, desiredBottomInset)) } 

على الأجهزة التي تعمل بنظام Android 4.4 ، تعمل الشفافية الجزئية فقط من خلال إعداد إشارات شفافة:

 window.addFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION ) 

هذه العلامات تجعل أشرطة النظام شفافة ، مما يضيف تدرجًا خفيفًا إليها ، والتي ، للأسف ، لا يمكن إزالتها. ومع ذلك ، يمكن تحويل التدرج اللوني إلى شريط ألوان شفاف باستخدام هذه المكتبة: https://github.com/jgilfelt/SystemBarTint . لقد أنقذتنا أكثر من مرة في الماضي. آخر التغييرات التي تم إجراؤها على المكتبة منذ 5 سنوات ، لذلك لن تكشف إلا عن سحرها لمدن رجعية حقيقية.

ستبدو عملية الإبلاغ الكاملة لكيتات كما يلي:

 fun Activity.setWindowTransparencyKitkat( rootView: KitkatTransparentSystemBarsContainer, listener: OnSystemBarsSizeChangedListener = { _, _ -> } ) { rootView.onSystemBarsSizeChangedListener = listener window.addFlags( WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS or WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION ) } 

مع وضع هذا في الاعتبار ، نكتب طريقة عالمية يمكن أن تجعل أشرطة النظام شفافة (أو على الأقل شفافة) ، بغض النظر عن التطبيق الذي يتم تشغيله على جهاز به إصدار Android:

 when { Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP -> setWindowTransparency(::updateMargins) Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT -> setWindowTransparencyKitkat(root_container, ::updateMargins) else -> { /*do nothing*/ } } 

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

لقطات
Huawei Honor 8، Android 7.0



Xiaomi Redmi Note 4، Android 6.1



HTC Desire Dual Sim، Android 4.4.2



Samsung J3 ، Android 7.0



Meizu M3s ، أندرويد 5.1



آسوس Zenfone 3 ماكس ، أندرويد 6.0



أومي روما ، أندرويد 5.0



Nexus 5X ، Android 8.0



Samsung Galaxy S8، Android 9.0





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

يمكنك العثور على قائمة كاملة بالبرنامج وعينة من العمل في مستودع git الخاص بنا.

المواد مستوحاة من تقرير كريس بانز "أن تصبح مجرب نافذة رئيسية".


أعرب عن امتناني لـ Surf studio و Evgeny Saturov للمساعدة في إعداد المواد.

Source: https://habr.com/ru/post/ar464373/


All Articles