يوفّر Android جميع المكوّنات لتطبيقات الشاشات الكبيرة بتصنيف خمس نجوم. تقوم الوصفات في كتاب الطبخ هذا باختيار ودمج مكونات مختارة لحل مشكلات تطوير معينة. تتضمن كل وصفة أفضل الممارسات وعينات التعليمات البرمجية عالية الجودة وتعليمات خطوة بخطوة لمساعدتك في أن تصبح طاهيًا على شاشة كبيرة.
التقييمات بالنجوم
يتم تقييم الوصفات بالنجوم استنادًا إلى مدى توافقها مع إرشادات جودة تطبيق الشاشة الكبيرة.
تستوفي معايير المستوى 1، وتتميز شاشات كبيرة متميّزة | |
يستوفي معايير المستوى 2، وهو محسَّن للشاشات الكبيرة | |
تستوفي معايير المستوى 3 (الشاشات الكبيرة) | |
يوفِّر بعض إمكانات الشاشة الكبيرة، ولكنه لا يفي بإرشادات جودة تطبيقات الشاشة الكبيرة. | |
يلبي احتياجات حالة استخدام معينة، ولكنه لا يتوافق مع الشاشات الكبيرة بشكل صحيح |
إمكانية استخدام كاميرا Chromebook
روِّج لمستخدمي Chromebook على Google Play.
إذا كان بإمكان تطبيق الكاميرا العمل مع ميزات الكاميرا الأساسية فقط، لا تسمح لمتاجر التطبيقات بمنع مستخدمي Chromebook من تثبيت التطبيق لأنّك حدّدت بدون قصد ميزات الكاميرا المتقدمة في الهواتف المتطورة.
تتضمّن أجهزة Chromebook كاميرا أمامية (مواجهة للمستخدم) تعمل بشكل جيد لعقد اجتماعات الفيديو واللقطات وغيرها من التطبيقات. ولكن لا تحتوي بعض أجهزة Chromebook على كاميرا خلفية (مواجهة للعالم)، ولا تتوافق معظم الكاميرات الموجَّهة للمستخدمين على أجهزة Chromebook مع التركيز التلقائي أو الفلاش.
أفضل الممارسات
تتوافق تطبيقات الكاميرا المتعدّدة الاستخدامات مع جميع الأجهزة بغض النظر عن إعدادات الكاميرا، مثل الأجهزة المزوّدة بكاميرات أمامية وكاميرات خلفية وكاميرات خارجية متصلة بكابل USB.
للتأكّد من أنّ متاجر التطبيقات تتيح تطبيقك لأكبر عدد من الأجهزة، عليك دائمًا الإفصاح عن جميع ميزات الكاميرا التي يستخدمها تطبيقك والإشارة بشكل صريح إلى ما إذا كانت الميزات مطلوبة أم لا.
المكوّنات
- إذن
CAMERA
: يمنح تطبيقك الإذن بالوصول إلى كاميرات الجهاز. - عنصر البيان
<uses-feature>
: إبلاغ متاجر التطبيقات بالميزات التي يستخدمها تطبيقك - السمة
required
: تشير إلى متاجر التطبيقات في ما إذا كان يمكن تشغيل تطبيقك بدون ميزة محدّدة.
الخطوات
ملخّص
يُرجى تقديم بيان عن إذن "CAMERA
". يُرجى تعريف ميزات الكاميرا التي توفّر الدعم الأساسي للكاميرا. حدد ما إذا كانت كل ميزة مطلوبة أم لا.
1. تقديم بيان عن إذن "CAMERA
"
أضِف الإذن التالي إلى بيان التطبيق:
<uses-permission android:name="android.permission.CAMERA" />
2. توضيح ميزات الكاميرا الأساسية
أضِف الميزات التالية إلى بيان التطبيق:
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. تحديد ما إذا كانت كل ميزة مطلوبة
يجب ضبط android:required="false"
لميزة android.hardware.camera.any
لإتاحة الوصول إلى تطبيقك من خلال الأجهزة التي تحتوي على أي نوع من الكاميرات المدمجة أو الخارجية، أو بدون كاميرا على الإطلاق.
بالنسبة إلى الميزات الأخرى، اضبط android:required="false"
لضمان وصول الأجهزة، مثل أجهزة Chromebook التي لا تحتوي على كاميرات خلفية أو ميزة "التركيز التلقائي" أو الفلاش، إلى تطبيقك في متاجر التطبيقات.
النتائج
يمكن لمستخدمي أجهزة Chromebook تنزيل تطبيقك وتثبيته من Google Play ومتاجر التطبيقات الأخرى. ولن يتم فرض قيود على وظائف الكاميرا في الأجهزة التي تتيح استخدام الكاميرات كاملة الميزات، مثل الهواتف.
يتم إتاحة تطبيقك لأكبر عدد ممكن من الأجهزة، وذلك من خلال تحديد ميزات الكاميرا التي يتيحها تطبيقك صراحةً وتحديد الميزات التي يتطلبها تطبيقك.
مراجع إضافية
لمزيد من المعلومات، اطّلِع على ميزات جهاز الكاميرا في مستندات <uses-feature>
.
يكون اتجاه التطبيق محظورًا على الهواتف ولكن ليس على الأجهزة ذات الشاشات الكبيرة.
يعمل تطبيقك بشكل رائع على الهواتف باتجاه عمودي، لذا تم حصر التطبيق على الوضع العمودي فقط. ولكنك ترى فرصة لإنجاز المزيد من المهام على الشاشات الكبيرة باتجاه أفقي.
كيف يمكنك إجراء ذلك في كلتا الحالتين - تقييد التطبيق إلى الاتجاه الرأسي على الشاشات الصغيرة، مع تمكين الوضع الأفقي على الشاشات الكبيرة؟
أفضل الممارسات
تلتزم أفضل التطبيقات بالإعدادات المفضّلة للمستخدم، مثل اتجاه الجهاز.
تنص إرشادات جودة تطبيقات الشاشات الكبيرة على أن تتوافق التطبيقات مع جميع إعدادات الأجهزة، بما في ذلك الاتجاهات العمودية والأفقية، ووضع النوافذ المتعددة، والحالة المطوية وغير المطوية للأجهزة القابلة للطي. على التطبيقات تحسين التنسيقات وواجهات المستخدم لمختلف عمليات الضبط، كما يجب أن تحفظ التطبيقات الحالة وتستعيدها أثناء تغييرات الإعدادات.
هذه الوصفة هي إجراء مؤقت - جزء صغير من دعم الشاشة الكبيرة. استخدِم الوصفة إلى أن تتمكّن من تحسين تطبيقك لتوفير توافق كامل مع جميع إعدادات الأجهزة.
المكوّنات
screenOrientation
: إعداد بيان التطبيق الذي يتيح لك تحديد كيفية استجابة تطبيقك لتغيُّرات اتجاه الجهاز- Jetpack WindowManager: مجموعة من المكتبات التي تتيح لك تحديد حجم نافذة التطبيق ونسبة العرض إلى الارتفاع فيها، وهي متوافقة مع الأنظمة القديمة مع المستوى 14 من واجهة برمجة التطبيقات.
Activity#setRequestedOrientation()
: الطريقة التي يمكنك من خلالها تغيير اتجاه التطبيق في وقت التشغيل
الخطوات
ملخّص
تفعيل التطبيق للتعامل مع تغييرات الاتجاه بشكل تلقائي في بيان التطبيق. في وقت التشغيل، حدِّد حجم نافذة التطبيق. إذا كانت نافذة التطبيق صغيرة، يمكنك فرض قيود على اتجاه التطبيق من خلال إلغاء إعداد اتجاه البيان.
1. تحديد إعداد الاتجاه في بيان التطبيق
يمكنك إما تجنُّب تعريف العنصر screenOrientation
في بيان التطبيق (في هذه الحالة يتم ضبط الاتجاه التلقائي على unspecified
) أو ضبط اتجاه الشاشة على fullUser
. إذا لم يقفل المستخدم التدوير المستند إلى أداة الاستشعار، سيتمكّن تطبيقك من استخدام جميع اتجاهات الجهاز.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
الفرق بين استخدام unspecified
وfullUser
بسيط ولكنه مهم. إذا لم تحدّد قيمة screenOrientation
، يختار النظام الاتجاه، وقد تختلف السياسة التي يستخدمها النظام لتحديد الاتجاه من جهاز إلى آخر. من ناحية أخرى، يتطابق تحديد fullUser
إلى حدّ أكبر مع السلوك الذي حدّده المستخدم للجهاز: إذا كان المستخدم قد قفل التدوير باستخدام أداة الاستشعار، يتبع التطبيق الخيار المفضَّل للمستخدم. بخلاف ذلك، يسمح النظام بأي من اتجاهات الشاشة الأربعة المحتملة (عمودي أو أفقي أو عمودي عكسي أو أفقي عكسي). يمكنك الاطّلاع على android:screenOrientation
.
2. تحديد حجم الشاشة
عند ضبط البيان على إتاحة جميع الاتجاهات التي يسمح بها المستخدم، يمكنك تحديد اتجاه التطبيق آليًا بناءً على حجم الشاشة.
أضِف مكتبات Jetpack WindowManager إلى ملف build.gradle
أو build.gradle.kts
في الوحدة:
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
رائع
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
استخدِم طريقة Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
لمعرفة حجم شاشة الجهاز ككائن WindowMetrics
. ويمكن مقارنة مقاييس الفترات بفئات حجم النافذة لتحديد وقت تقييد الاتجاه.
توفر فئات حجم Windows نقاط التوقف بين الشاشات الصغيرة والكبيرة.
استخدِم نقطتَي الإيقاف WindowWidthSizeClass#COMPACT
وWindowHeightSizeClass#COMPACT
لتحديد حجم الشاشة:
Kotlin
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- ملاحظة:
- يتم تنفيذ الأمثلة أعلاه كأساليب لنشاط؛ وبالتالي، تمت الإشارة إلى النشاط باسم
this
في وسيطةcomputeMaximumWindowMetrics()
. - يتم استخدام طريقة
computeMaximumWindowMetrics()
بدلاً منcomputeCurrentWindowMetrics()
لأنّه يمكن تشغيل التطبيق في وضع النوافذ المتعددة، الذي يتجاهل إعداد اتجاه الشاشة. وليس هناك جدوى من تحديد حجم نافذة التطبيق وتجاوز إعداد الاتجاه ما لم تكن نافذة التطبيق هي شاشة الجهاز بأكملها.
راجِع WindowManager للحصول على تعليمات حول الإفصاح عن التبعيات لإتاحة طريقة computeMaximumWindowMetrics()
في تطبيقك.
3. تجاهُل إعداد بيان التطبيق
عند التأكّد من أنّ حجم الشاشة مصغّر في الجهاز، يمكنك استدعاء التطبيق Activity#setRequestedOrientation()
لإلغاء إعداد screenOrientation
في البيان:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
بإضافة المنطق إلى طريقتَي onCreate()
وView.onConfigurationChanged()
، يمكنك الحصول على الحد الأقصى لمقاييس النوافذ وإلغاء إعداد الاتجاه عندما يتم تغيير حجم النشاط أو نقله بين الشاشات، مثلاً بعد تدوير الجهاز أو عندما يكون جهاز قابل للطي مطويًا أو غير مطوي.
لمزيد من المعلومات عن وقت حدوث تغييرات الإعدادات والوقت الذي تؤدي فيه إلى إعادة إنشاء النشاط، يُرجى الاطّلاع على مقالة تغييرات إعدادات الاسم المعرِّف.
النتائج
يجب أن يظل تطبيقك الآن في الاتجاه العمودي على الشاشات الصغيرة بغض النظر عن تدوير الجهاز. على الشاشات الكبيرة، يجب أن يكون التطبيق متوافقًا مع الاتجاهات الأفقية والعمودية.
مراجع إضافية
للمساعدة في ترقية تطبيقك بحيث يتوافق مع جميع إعدادات الأجهزة طوال الوقت، يُرجى الاطّلاع على ما يلي:
إيقاف تشغيل الوسائط مؤقتًا واستئنافها باستخدام مفتاح المسافة للوحة المفاتيح الخارجية
ويشمل تحسين الشاشة الكبيرة القدرة على التعامل مع إدخالات لوحة المفاتيح الخارجية، مثل التفاعل مع مفتاح المسافة الذي يتم الضغط عليه لإيقاف تشغيل الفيديوهات والوسائط الأخرى أو استئناف تشغيلها. يكون هذا مفيدًا بشكل خاص للأجهزة اللوحية، التي غالبًا ما تتصل بلوحات المفاتيح الخارجية، وأجهزة Chromebook، التي تأتي عادةً مع لوحات مفاتيح خارجية ولكن يمكن استخدامها في وضع الجهاز اللوحي.
عندما تكون الوسائط هي العنصر الوحيد في النافذة (مثل تشغيل الفيديو في وضع ملء الشاشة)، يمكنك الاستجابة لأحداث الضغط على المفاتيح على مستوى النشاط، أو في Jetpack Compose، على مستوى الشاشة.
أفضل الممارسات
عند تشغيل التطبيق لملف وسائط، يجب أن يتمكّن المستخدمون من إيقاف التشغيل مؤقتًا واستئناف التشغيل من خلال الضغط على مفتاح المسافة على لوحة مفاتيح خارجية.
المكوّنات
KEYCODE_SPACE
: ثابت رمز المفتاح لمفتاح المسافة.
إنشاء
onPreviewKeyEvent
:Modifier
يتيح لمكوِّن اعتراض الأحداث الرئيسية للجهاز عندما يكون التركيز (أو أحد عناصره الفرعية).onKeyEvent
: على غرارonPreviewKeyEvent
، يتيحModifier
هذا لمكوِّن اعتراض الأحداث الرئيسية للأجهزة عندما يتم التركيز على الجهاز (أو أحد عناصره الفرعية).
الملفات الشخصية
onKeyUp()
: يتم استدعاء هذا الإجراء عند تحرير مفتاح ولا تتم معالجته من خلال ملف شخصي ضمن نشاط.
الخطوات
ملخّص
تستجيب التطبيقات والتطبيقات المستندة إلى العرض المستنِدة إلى Jetpack Compose إلى ضغطات مفاتيح لوحة المفاتيح بطرق مشابهة: يجب أن يستمع التطبيق إلى أحداث الضغط على المفاتيح، ويفلتر الأحداث، ويستجيب لعمليات الضغط على المفاتيح التي تم اختيارها، مثل الضغط على مفتاح Spacebar.
1. الاستماع إلى أحداث لوحة المفاتيح
الملفات الشخصية
في نشاط في تطبيقك، يمكنك إلغاء طريقة onKeyUp()
:
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
يتم استدعاء الطريقة في كل مرة يتم فيها رفع إصبعك عن مفتاح تم الضغط عليه، بحيث يتم تنشيطها مرة واحدة بالضبط لكل ضغطة مفتاح.
إنشاء
باستخدام Jetpack Compose، يمكنك الاستفادة من عنصر التعديل onPreviewKeyEvent
أو onKeyEvent
في الشاشة التي تدير ضغطة المفتاح:
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
أو
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. فلترة الضغطات على مفتاح المسافة
داخل الإجراء onKeyUp()
أو طريقتي التعديل onPreviewKeyEvent
وonKeyEvent
، يمكنك الفلترة حسب KeyEvent.KEYCODE_SPACE
لإرسال الحدث الصحيح إلى مكوّن الوسائط:
الملفات الشخصية
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
إنشاء
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
أو
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
النتائج
يمكن لتطبيقك الآن الاستجابة للضغطات على مفتاح Spacebar لإيقاف الفيديو أو الوسائط الأخرى مؤقتًا واستئناف تشغيله.
مراجع إضافية
لمزيد من المعلومات عن أحداث لوحة المفاتيح وكيفية إدارتها، راجِع التعامل مع إدخال لوحة المفاتيح.
رفض راحة اليد بقلم الشاشة
يمكن لقلم الشاشة أن يكون أداة إنتاجية وإبداعية بشكل ممتاز على الشاشات الكبيرة. ولكن عندما يرسم المستخدمون أو يكتبون أو يتفاعلوا مع تطبيق باستخدام قلم الشاشة، فإنهم أحيانًا يلمسون الشاشة براحة أيديهم. ويمكن الإبلاغ عن حدث اللمس إلى تطبيقك قبل أن يتعرّف النظام على الحدث ويرفضه باعتباره لمسة راحة يد غير مقصودة.
أفضل الممارسات
يجب أن يرصد تطبيقك أحداث اللمس الغريبة ويتجاهلها. يلغي Android لمسة راحة اليد من خلال إرسال عنصر MotionEvent
. ابحث في الكائن عن ACTION_CANCEL
أو ACTION_POINTER_UP
وFLAG_CANCELED
لتحديد ما إذا كان سيتم رفض الإيماءة التي تسببها لمس راحة اليد.
المكوّنات
MotionEvent
: يمثّل أحداث اللمس والحركة. تحتوي على المعلومات اللازمة لتحديد ما إذا كان يجب تجاهل حدث معيّن.OnTouchListener#onTouch()
: يتلقىMotionEvent
عنصرًا.MotionEvent#getActionMasked()
: تعرض الإجراء المرتبط بحدث متحرّك.ACTION_CANCEL
: عدد ثابتMotionEvent
يشير إلى ضرورة التراجع عن الإيماءة.ACTION_POINTER_UP
: ثابتMotionEvent
يشير إلى ارتفاع مؤشر ما غير المؤشر الأول (أي أنّه تراجع عن الاتصال بشاشة الجهاز).FLAG_CANCELED
: قيمة ثابتة للسمةMotionEvent
تشير إلى أنّ المؤشر الذي ارتفع إلى الأعلى تسبّب في حدوث حدث لمس غير مقصود. تمت الإضافة إلى أحداث "ACTION_POINTER_UP
" و"ACTION_CANCEL
" على نظام التشغيل Android 13 (المستوى 33 لواجهة برمجة التطبيقات) والإصدارات الأحدث.
الخطوات
ملخّص
يمكنك فحص عناصر MotionEvent
التي تم إرسالها إلى تطبيقك. واستخدام واجهات برمجة تطبيقات MotionEvent
لتحديد خصائص الأحداث:
- أحداث النقاط الأحادية: ابحث عن
ACTION_CANCEL
. على الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، تحقَّق أيضًا منFLAG_CANCELED
. - الأحداث المتعدّدة النقاط: على نظام التشغيل Android 13 والإصدارات الأحدث، تحقَّق من
ACTION_POINTER_UP
وFLAG_CANCELED
.
يمكنك الرد على ACTION_CANCEL
وACTION_POINTER_UP
من أصل FLAG_CANCELED
حدث.
1. الحصول على كائنات أحداث متحركة
إضافة OnTouchListener
إلى تطبيقك:
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. تحديد إجراء الحدث والعلامات
تحقَّق من وجود ACTION_CANCEL
، الذي يشير إلى حدث مؤشّر الماوس فوق جميع مستويات واجهة برمجة التطبيقات. على نظام التشغيل Android 13 والإصدارات الأحدث، تحقَّق من ACTION_POINTER_UP
للتأكّد من توفّر FLAG_CANCELED.
.
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. التراجع عن الإيماءة
بعد تحديد لمسة راحة اليد، يمكنك التراجع عن التأثيرات التي تظهر على الشاشة للإيماءة.
يجب أن يحتفظ تطبيقك بسجلّ إجراءات المستخدم حتى يمكن التراجع عن الإدخالات غير المقصودة، مثل لمس راحة اليد. للاطّلاع على مثال، راجِع تنفيذ تطبيق رسم أساسي في الدرس التطبيقي حول ترميز تحسين توافق قلم الشاشة في تطبيق Android.
النتائج
يمكن لتطبيقك الآن تحديد ورفض لمسات راحة اليد في أحداث المؤشرات المتعددة على Android 13 والمستويات الأعلى من واجهة برمجة التطبيقات، ولأحداث المؤشر الواحد على جميع مستويات واجهة برمجة التطبيقات.
مراجع إضافية
لمزيد من المعلومات، يُرجى الاطّلاع على ما يلي:
- ميزات وواجهات برمجة التطبيقات في Android 13: تحسين آلية رفض راحة اليد
- أدلة المطوِّرين
- الدرس التطبيقي حول الترميز: تحسين توافق قلم الشاشة في تطبيق Android
إدارة حالة WebView
WebView
هو مكوّن شائع الاستخدام يوفّر نظامًا متقدمًا لإدارة الحالة. يجب أن يحافظ WebView
على حالته وموضع التمرير خلال تغييرات الإعدادات. يمكن أن يفقد "WebView
" موضع التمرير عندما يتدوير الجهاز أو يفتح هاتفًا قابلاً للطي، ما يفرض على المستخدم الانتقال مرة أخرى من أعلى WebView
إلى موضع التمرير السابق.
أفضل الممارسات
عليك تقليل عدد مرات إعادة إنشاء "WebView
". WebView
جيد في إدارة حالته، ويمكنك الاستفادة من هذه الجودة من خلال إدارة أكبر عدد ممكن من التغييرات على الإعدادات. يجب أن يتعامل تطبيقك مع تغييرات الإعدادات لأنّ إعادة إنشاء Activity
(طريقة التعامل مع تغييرات الإعدادات في النظام) تعيد إنشاء WebView
أيضًا، ما يؤدي إلى فقدان WebView
لحالته.
المكوّنات
android:configChanges
: سمة العنصر<activity>
في البيان يسرد تغييرات الضبط التي عالجها النشاط.View#invalidate()
: الطريقة التي تؤدي إلى إعادة رسم العرض مكتسَب من قِبل "WebView
".
الخطوات
ملخّص
لحفظ حالة WebView
، تجنَّب إعادة إنشاء Activity
بقدر الإمكان، ثم أبقِ العلامة WebView
غير صالحة حتى تتمكّن من تغيير حجمها مع الاحتفاظ بحالتها.
1. إضافة تغييرات الإعدادات إلى ملف AndroidManifest.xml
الخاص بتطبيقك
تجنُّب إعادة إنشاء النشاط من خلال تحديد تغييرات الإعدادات التي يعالجها تطبيقك (وليس من خلال النظام):
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. إلغاء صلاحية WebView
عندما يتلقّى تطبيقك تغييرًا في الإعدادات
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
تنطبق هذه الخطوة على نظام العرض فقط، لأنّ Jetpack Compose لا يحتاج إلى إلغاء صلاحية أي عنصر لتغيير حجم عناصر Composable
بشكل صحيح. ومع ذلك، يعيد Compose إنشاء WebView
غالبًا إذا لم تتم إدارته بشكلٍ صحيح. استخدِم برنامج تضمين Accompanist WebView لحفظ حالة WebView
واستعادتها في تطبيقات Compose الخاصة بك.
النتائج
تحتفظ الآن مكوّنات WebView
في تطبيقك بحالتها وموضع التمرير عبر التغييرات المتعددة في الإعدادات، بدءًا من تغيير الحجم ووصولاً إلى تغيير الاتجاه ووصولاً إلى الطي والفتح.
مراجع إضافية
لمعرفة المزيد من المعلومات عن تغييرات الإعدادات وكيفية إدارتها، يُرجى الاطّلاع على مقالة التعامل مع تغييرات الإعدادات.
إدارة حالة RecyclerView
يمكن أن تعرض RecyclerView
كميات كبيرة من البيانات باستخدام الحد الأدنى من موارد الرسومات. أثناء انتقال RecyclerView
في قائمة العناصر، يعيد RecyclerView
استخدام مثيل View
من العناصر التي تم الانتقال خارج الشاشة لإنشاء عناصر جديدة أثناء الانتقال على الشاشة. ويمكن أن تؤدي التغييرات على الإعدادات، مثل تدوير الجهاز، إلى إعادة ضبط حالة RecyclerView
، ما يفرض على المستخدمين الانتقال مرة أخرى إلى موضعهم السابق في قائمة RecyclerView
عنصر.
أفضل الممارسات
يجب أن يحافظ RecyclerView
على حالته، لا سيّما موضع التمرير، وحالة عناصر القائمة خلال جميع تغييرات الإعدادات.
المكوّنات
RecyclerView.Adapter#setStateRestorationPolicy()
: تحدِّد هذه السياسة كيفية استعادةRecyclerView.Adapter
لحالته بعد تغيير الإعدادات.ViewModel
: يحتفظ بهذه الحالة لنشاط أو جزء.
الخطوات
ملخّص
يمكنك ضبط سياسة استعادة حالة RecyclerView.Adapter
لحفظ موضع التمرير RecyclerView
. حفظ حالة RecyclerView
عنصر قائمة يمكنك إضافة حالة عناصر القائمة إلى محوّل RecyclerView
واستعادة حالة عناصر القائمة عند ربطها بعنصر ViewHolder
.
1. تفعيل سياسة استعادة الحالة "Adapter
"
يمكنك تفعيل سياسة استعادة حالة محوّل RecyclerView
للاحتفاظ بموضع التمرير في RecyclerView
في كل تغييرات الإعدادات. أضِف مواصفات السياسة إلى الدالة الإنشائية للمهايئ:
Kotlin
class MyAdapter() : RecyclerView.Adapter() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter{ public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. حفظ حالة عناصر القائمة ذات الحالة
يمكنك حفظ حالة عناصر قائمة RecyclerView
المعقّدة، مثل العناصر التي تحتوي على عناصر EditText
. على سبيل المثال، لحفظ حالة EditText
، أضِف معاودة الاتصال مشابهة لمعالج onClick
لتسجيل تغييرات النص. ضمن معاودة الاتصال، حدِّد البيانات التي تريد حفظها:
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
يُرجى توضيح اسم معاودة الاتصال في "Activity
" أو "Fragment
". استخدِم ViewModel
لتخزين الولاية.
3. إضافة حالة عنصر القائمة إلى Adapter
أضِف حالة عناصر القائمة إلى RecyclerView.Adapter
. مرِّر حالة العنصر إلى الدالة الإنشائية للمحوّل عند إنشاء المضيف Activity
أو Fragment
:
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. استرداد حالة عنصر القائمة في ViewHolder
للمحوِّل
عند ربط ViewHolder
بعنصر في "RecyclerView.Adapter
"، يمكنك استعادة حالة العنصر:
Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Java
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
النتائج
بإمكان "RecyclerView
" الآن استعادة موضع التمرير وحالة كل عنصر في قائمة "RecyclerView
".
مراجع إضافية
إدارة لوحة المفاتيح القابلة للفصل
يساعد دعم لوحات المفاتيح القابلة للفصل في زيادة إنتاجية المستخدمين إلى أقصى حد على الأجهزة
ذات الشاشات الكبيرة. يقوم Android بإجراء تغيير في التهيئة في كل مرة يتم فيها توصيل
لوحة مفاتيح بأحد الأجهزة أو فصلها عن الجهاز، مما قد يتسبب في فقدان حالة واجهة المستخدم. يمكن لتطبيقك إما حفظ حالته واستعادتها، ما يتيح للنظام إمكانية معالجة النشاط، أو تقييد إعادة إنشاء النشاط عند إجراء تغييرات على إعدادات لوحة المفاتيح.
وفي جميع الحالات، يتم تخزين كل البيانات المرتبطة بلوحة المفاتيح في كائن Configuration
. يحتوي الحقلان keyboard
وkeyboardHidden
لعنصر الإعداد على معلومات حول نوع لوحة المفاتيح ومدى توفّرها.
أفضل الممارسات
تتوافق التطبيقات المُحسَّنة للشاشات الكبيرة مع كل نوع من أجهزة الإدخال، بدءًا من لوحات المفاتيح الخاصة بالبرامج والأجهزة وحتى قلم الشاشة والماوس ولوحة اللمس وغيرها من الأجهزة الملحقة.
يتضمن دعم لوحات المفاتيح الخارجية تغييرات في التهيئة، ويمكنك إدارتها بأي من الطريقتين التاليتين:
- اسمح للنظام بإعادة إنشاء النشاط قيد التشغيل حاليًا، وستتولى إدارة حالة تطبيقك.
- إدارة تغيير الإعدادات بنفسك (لن تتم إعادة إنشاء النشاط):
- توضيح جميع قيم الإعدادات المتعلّقة بلوحة المفاتيح
- إنشاء معالِج تغيير الإعدادات
يمكن لتطبيقات الإنتاجية، والتي غالبًا ما تتطلب التحكم الدقيق في واجهة المستخدم لإدخال النص وإدخالاته الأخرى، الاستفادة من منهج التنفيذ الذاتي للتعامل مع تغييرات الإعدادات.
في حالات خاصة، قد تحتاج إلى تغيير تخطيط التطبيق عند توصيل لوحة مفاتيح للأجهزة أو فصلها، على سبيل المثال، لتوفير مساحة أكبر للأدوات أو نوافذ التعديل.
وبما أنّ الطريقة الوحيدة الموثوقة لمعالجة تغييرات الإعدادات هي تجاوز طريقة العرض onConfigurationChanged()
، يمكنك إضافة مثيل عرض جديد للنشاط على تطبيقك والاستجابة في معالج onConfigurationChanged()
لطريقة العرض للتغييرات التي تحدث بسبب إرفاق لوحة المفاتيح أو فصلها.
المكوّنات
android:configChanges
: سمة العنصر<activity>
في بيان التطبيق يُعلِم النظام بالتغييرات في الإعدادات التي يديرها التطبيق.View#onConfigurationChanged()
: الطريقة التي تتفاعل مع نشر إعدادات تطبيق جديدة.
الخطوات
ملخّص
يُرجى تعريف السمة configChanges
وإضافة قيم متعلّقة بلوحة المفاتيح. أضِف View
إلى العرض الهرمي لطريقة عرض النشاط مع رصد أي تغييرات في الإعدادات.
1. تعريف السمة configChanges
يمكنك تعديل العنصر <activity>
في بيان التطبيق عن طريق إضافة قيم keyboard|keyboardHidden
إلى قائمة تغييرات الإعدادات المُدارة مسبقًا:
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. إضافة طريقة عرض فارغة إلى التدرّج الهرمي لطريقة العرض
تعريف طريقة عرض جديدة وإضافة رمز المعالج الخاص بك داخل طريقة onConfigurationChanged()
الخاصة بطريقة العرض:
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Java
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
النتائج
سيستجيب تطبيقك الآن للوحة مفاتيح خارجية يتم إرفاقها أو فصلها بدون إعادة إنشاء النشاط الجاري.
مراجع إضافية
للتعرّف على كيفية حفظ حالة واجهة المستخدم للتطبيق أثناء تغييرات الإعدادات مثل مرفق لوحة المفاتيح أو الانفصال، يمكنك الاطّلاع على حفظ حالات واجهة المستخدم.