إدارة الإيماءات: معالجة تعارضات الإيماءات. الجزء 3

وتم إعداد ترجمة المقالة في وقت سابق للإطلاق المتقدم و الأساسي بالطبع تطوير الروبوت.



هذه هي المقالة الثالثة في سلسلة حول التحكم بالإيماءات. يمكنك التعرف على ترجمات من الأولى و الثانية أجزاء إذا غاب منهم.

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

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



لسوء الحظ ، إنه SeekBarقريب جدًا من منطقة الإيماءة للعودة إلى الشاشة الرئيسية ، والتي تبدأ منها إيماءة التبديل السريع إلى التطبيق السابق (QuickSwitch) ، مما يزعج المستخدم.

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

وهو ما يقودنا إلى السؤال "كيف يمكننا إصلاح هذا؟" لتسهيل هذا السؤال ، قمنا بإنشاء مخطط انسيابي يقدم لك إجابة بناءً على الموقف.


يمكنك العثور على نسخة PDF من المخطط الانسيابي هنا .

آمل أن لا تتطلب الأسئلة شرحًا ، ولكن فقط في حالة ، سوف أتناول كل منها:

1. هل يحتاج التطبيق إلى إخفاء أشرطة التنقل والحالة؟


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

الأسباب المحتملة للإجابة بـ "نعم" على هذا السؤال:


من الأمثلة الشائعة للتطبيقات التي يجب أن تجيب بنعم على هذا السؤال الألعاب ومشغلات الفيديو ومشاهدي الصور وتطبيقات الرسم.

2. السيناريو الرئيسي لاستخدام واجهة المستخدم ينطوي على الضربات الشديدة في / حول منطقة الإيماءة؟


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

عادة ما تقول الألعاب نعم بسبب حقيقة ذلك

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

بالإضافة إلى الألعاب ، من الأمثلة الشائعة لواجهة المستخدم التي يجب أن تقول نعم:

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

3. عرض المستخدمة بشكل متكرر في / حول منطقة لفتة؟


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

4. العرض يتضمن التمرير / السحب؟


نغير التكتيكات قليلاً ونبدأ في النظر إلى وجهات النظر الفردية. بالنسبة لأي من المشاهدات التي أجبت عنها بالإيجاب على السؤال الثالث ، نقدم توضيحًا صغيرًا: هل يجب على المستخدم تمريره / سحبه؟

وهناك العديد من الأمثلة التي لديك للرد على "نعم»: SeekBars، BottomSheet أو حتى PopupMenu(عليك أن اسحب لفتح).

5. هل يقع العرض بالكامل / بشكل عام أسفل مناطق الإيماءات؟


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

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

في الرسم البياني أعلاه ، قد تلاحظ وجود شاشة دوارة بملء الشاشة ( ViewPager) كمثال على إجابة سلبية وتتساءل عن سبب عدم الحاجة إلى معالجة هذه الحالة. ويرجع ذلك إلى حقيقة أن مساحات الإيماءات يسارًا / يمينًا صغيرة نسبيًا في العرض (الافتراضي: 20dpكل منها) مقارنة بعرض العرض. نموذجيعرض شاشة هاتفك في الاتجاه الرأسي ~ 360dp ، تاركا ~ 320dp من المساحة الحرة التي لا يجد فيها المستخدم صعوبة (هذه تقريبا 90٪ من الشاشة). حتى مع الهوامش الداخلية / المسافات البادئة ، لا يزال بإمكان المستخدم التمرير بشكل مريح عبر الرف الدائري.

6. هل تتداخل حدود العرض مع أي مناطق إيماءات مطلوبة؟


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

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

الأمثلة النموذجية هي:

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

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

لا يمكن معالجة النزاعات


لنبدأ بأبسط "حل" ، لا تفعل أي شيء !

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

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

تحريك المنظر من مناطق الإيماءات


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

دعونا نلقي نظرة على مثال مرة أخرى. لدينا واجهة مشغل موسيقى أظهرناها أعلاه. يحتوي على SeekBar، الموجود في أسفل الشاشة ، مما يسمح للمستخدم بالتمرير عبر الأغنية.


مشغل موسيقى واجهة المستخدم مع SeekBar في الجزء السفلي من الشاشة

ولكن عندما يحاول المستخدم


التمرير عبر الأغنية ، يحدث هذا: تسجيل إيماءة النظام التي تتعارض مع SeekBar

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



حل بسيط


الحل الأبسط هنا هو إضافة مسافة بادئة / هامش إضافي بحيث يتحرك SeekBar لأعلى من منطقة الإيماءة. شيء من هذا القبيل:



إذا سحبنا SeekBar في هذا المثال ، سترى أننا لم نعد


ننشط إيماءة العودة: لم يعد SeekBar يتعارض مع إيماءة النظام السفلية.

لتنفيذ ذلك ، نحتاج إلى استخدام إدخالات إيماءات النظام الجديدة المتاحة في API 29 ومكتبة Jetpack Core v1.2.0 (حاليًا في ألفا ). في المثال ، نزيد المسافة البادئة السفلية SeekBarلتتناسب مع قيمة الإيماءة الأقل :

ViewCompat.setOnApplyWindowInsetsListener(seekBar) { view, insets ->
     //      view ,    system gesture insets
    view.updatePadding(
        bottom = insets.systemGestureInsets.bottom
    )
    insets
}

إذا كنت مهتمًا بتعلم كيفية تسهيل العمل معه WindowInsets، يمكنك قراءة مقالتنا الأخرى حول هذا الموضوع:

WindowInsets - تخطيط المستمعين

مزيد من الإجراءات


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


SeekBarانتقل إلى الجزء العلوي من لوحة التشغيل.

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

, . « ».

API


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

هناك وظيفتان مختلفتان يوفرهما النظام لاستبعاد مناطق الإيماءات: View.setSystemGestureExclusionRects()و Window.setSystemGestureExclusionRects(). يعتمد ما يجب عليك استخدامه على التطبيق: إذا كنت تستخدم Android View، يفضل النظام واجهة برمجة تطبيقات العرض ، أو استخدام Windowواجهة برمجة التطبيقات.

الفرق الرئيسي بين APIs اثنين هو أن API APIتتوقع أن تكون أي مستطيلات في مساحة الإحداثيات بالنافذة. إذا كنت تستخدم طريقة العرض ، فستعمل عادةً في مساحة إحداثيات العرض بدلاً من ذلك. تهتم واجهة برمجة تطبيقات View بالتحويل بين مساحات الإحداثيات ، أي أنك تحتاج إلى التفكير فقط من حيث محتوى العرض.

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

دعونا نرى ما يحدث عندما يحاول مستخدم تخطي أغنية عندما "شريط التمرير"SeekBarيوجد (سحب دائري) بالقرب من أحد الحواف:


يتعارض SeekBar مع منطقة الإيماءة الخلفية.

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

عادةً ما يتم استدعاء واجهات برمجة تطبيقات استثناء الإيماءات من مكانين: onDraw()عندما يتم تقديم العرض الخاص بك ، onLayout()وخلاف ذلك. يمر وجهة نظركList<Rect>تحتوي على جميع المستطيلات المراد استبعادها. كما ذكرنا سابقًا ، يجب أن تكون هذه المستطيلات في نظام الإحداثيات الخاص بهم.

عادة ما تقوم بإنشاء دالة مثل هذه سيتم استدعاؤها من onLayout()و / أو onDraw():

private val gestureExclusionRects = mutableListOf<Rect>()
private fun updateGestureExclusion() {
   //   ,      Android 10+
   if (Build.VERSION.SDK_INT < 29) return
  // -,     
   gestureExclusionRects.clear()
      //   ,    .  SeekBar    .
   thumb?.also { t ->
       gestureExclusionRects += t.copyBounds()
   }
   //            view,       ,     
   // ,       
   systemGestureExclusionRects = gestureExclusionRects
}

يمكن العثور على مثال كامل هنا .

بعد إضافة هذا ، يعمل


التلف بالقرب من الحواف كما هو متوقع: يعمل SeekBar في منطقة الإيماءة الخلفية

ملاحظة في المثال أعلاه. SeekBarيقوم بذلك بالفعل تلقائيًا في Android 10 ، لذلك لا حاجة للقيام بذلك بنفسك. هنا نقوم بذلك كمثال فقط لإظهار المخطط العام.

محددات


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

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

نظرًا لأن السلوك الذي توفره واجهة برمجة التطبيقات يمكن أن ينتهك تجربة مريحة للمستخدم ، فإن النظام يحد من استخدامه: يمكن للتطبيقات استبعاد ما يصل إلى 200dp فقط لكل حافة.

إليك بعض الأسئلة الشائعة التي يطرحها المطورون عندما يسمعون ذلك:

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

بمعنى آخر ، يجب أن يكون نظام الملاحة دائمًا متسقًا وسهل الاستخدام.

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

ماذا لو كنت بحاجة إلى استبعاد أكثر من 200dp لكل حافة؟ سيستبعد النظام أقل 200dp التي طلبتها.


يسمح النظام بطلب لارتفاع إجمالي يبلغ 200 dp ، يتم احتسابه من الحافة السفلية لوجهي الشخصي

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

الغوص في المنشور التالي


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






All Articles