تتيح ميزة "العرض في نافذة" للمستخدمين تشغيل تطبيقات متعددة في الوقت نفسه في نوافذ تطبيقات قابلة لتغيير الحجم، ما يوفّر تجربة استخدام متعددة الاستخدامات ومماثلة لأجهزة الكمبيوتر المكتبية.
في الشكل 1، يمكنك الاطّلاع على تنظيم الشاشة مع تفعيل ميزة "العرض في نافذة". ملاحظات:
- يمكن للمستخدمين تشغيل تطبيقات متعددة جنبًا إلى جنب في الوقت نفسه.
- يظهر شريط التطبيقات في موضع ثابت في أسفل الشاشة، ويعرض التطبيقات قيد التشغيل. يمكن للمستخدمين تثبيت التطبيقات للوصول إليها بسرعة.
- يزيّن شريط عناوين جديد قابل للتخصيص أعلى كل نافذة، ويتضمّن عناصر تحكّم، مثل التصغير والتكبير.
تُفتح التطبيقات تلقائيًا في وضع ملء الشاشة على أجهزة Android اللوحية. لتشغيل تطبيق في وضع "العرض في نافذة"، اضغط مع الاستمرار على شريط التحكّم في أعلى الشاشة واسحبه ضمن واجهة المستخدم، كما هو موضّح في الشكل 2.
عند فتح تطبيق في وضع "العرض في نافذة"، تُفتح التطبيقات الأخرى في نوافذ سطح المكتب أيضًا.
يمكن للمستخدمين أيضًا تفعيل ميزة "العرض في نافذة" من القائمة التي تظهر أسفل الـ شريط التحكّم في النافذة عند النقر على شريط التحكّم أو استخدامه أو استخدام اختصار لوحة المفاتيح مفتاح Meta (Windows أو Command أو Search) + Ctrl + السهم للأسفل.
يخرج المستخدمون من وضع "العرض في نافذة" عن طريق إغلاق جميع النوافذ النشطة أو عن طريق سحب شريط التحكّم في النافذة في أعلى نافذة سطح المكتب وسحب التطبيق إلى أعلى الشاشة. يؤدي اختصار لوحة المفاتيح Meta + H أيضًا إلى الخروج من وضع "العرض في نافذة" وتشغيل التطبيقات في وضع ملء الشاشة مرة أخرى.
للعودة إلى وضع "العرض في نافذة"، انقر على مربّع مساحة سطح المكتب في شاشة "التطبيقات الحديثة".
تحسين تصميم تطبيقك لبيئة مماثلة لأجهزة الكمبيوتر المكتبية
قد يختلف التصميم لتجربة استخدام مماثلة لأجهزة الكمبيوتر المكتبية اختلافًا كبيرًا عن تصميم الأجهزة الجوّالة بسبب زيادة مساحة الشاشة ودقة إدخال البيانات باستخدام الماوس ولوحة المفاتيح وتوقّع إنتاجية عالية.
توفر مكتبة Jetpack WindowManager واجهة برمجة تطبيقات ذات رأي محدد لمساعدة المطوّرين في تحديد وقت عرض واجهة مستخدم مماثلة لأجهزة الكمبيوتر المكتبية، والتي عادةً ما تكون ذات كثافة معلومات أعلى، وأنماط تنقّل مختلفة، وتفاعلات محسّنة مع الماوس.
lifecycleScope.launch(Dispatchers.Main) { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { windowInfoTracker.windowEngagementInfo(this@DesktopWindowingActivity) .collect { windowEngagementInfo -> if(windowEngagementInfo.hasEngagementMode(WindowEngagementInfo.EngagementMode.PRECISE_POINTER)){ showDesktopOptimizedUI() }else { showTouchOptimizedUI() } } } }
لمعرفة المزيد من المعلومات، يمكنك الاطّلاع على مقالة التصميم لتجربة استخدام مماثلة لأجهزة الكمبيوتر المكتبية.
إمكانية تغيير الحجم ووضع التوافق
في وضع "العرض في نافذة"، يمكن تغيير حجم التطبيقات التي تم قفل اتجاهها بحرية. يعني ذلك أنّه حتى إذا تم قفل النشاط على الاتجاه العمودي، سيظل بإمكان المستخدمين تغيير حجم التطبيق إلى نافذة ذات اتجاه أفقي.
يتم تغيير حجم واجهة مستخدم التطبيقات التي تم الإعلان عنها على أنّها غير قابلة لتغيير الحجم (أي resizeableActivity = false) مع الحفاظ على نسبة العرض إلى الارتفاع نفسها.
تتلقّى تطبيقات الكاميرا التي تقفل الاتجاه أو يتم الإعلان عنها على أنّها غير قابلة لتغيير الحجم معاملة خاصة في ما يتعلق بعدسة الكاميرا: يمكن تغيير حجم النافذة بالكامل، ولكن تحافظ عدسة الكاميرا على نسبة العرض إلى الارتفاع نفسها. من خلال افتراض أنّ التطبيقات تعمل دائمًا في الاتجاه العمودي أو الأفقي، تضع التطبيقات رموزًا ثابتة أو تقدّم افتراضات أخرى تؤدي إلى إجراء عمليات حسابية غير صحيحة لاتجاه الصورة المعاينة أو الملتقطة أو نسبة العرض إلى الارتفاع، ما يؤدي إلى ظهور صور ممدودة أو جانبية أو مقلوبة.
إلى أن تصبح التطبيقات جاهزة لتنفيذ عدسات كاميرا متجاوبة بالكامل، توفّر المعاملة الخاصة تجربة مستخدم أساسية أكثر تخفّف من الآثار التي قد تسببها الافتراضات الخاطئة.
لمعرفة المزيد من المعلومات عن وضع التوافق لتطبيقات الكاميرا، يمكنك الاطّلاع على مقالة وضع توافق الجهاز.
حواف رأس قابلة للتخصيص
تحتوي جميع التطبيقات التي تعمل في وضع "العرض في نافذة" على شريط عناوين، حتى في وضع ملء الشاشة. يمكنك تخصيص هذا الشريط لمنع حجب محتوى تطبيقك ورسم عناصر واجهة مستخدم مخصّصة مباشرةً في مساحة الرأس.
التنفيذ
لرسم محتوى مخصّص في شريط العناوين، تتمثّل الخطوة الأولى في جعل خلفية شريط العناوين شفافة. يمكنك تحقيق ذلك باستخدام العلامة
APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND مع
WindowInsetsController.
window.insetsController?.setSystemBarsAppearance( WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND, WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND )
بعد أن يصبح شريط العناوين شفافًا، يمكنك تصميم منطقة الرأس لتتطابق مع تصميم تطبيقك. استخدِم WindowInsets.isCaptionBarVisible لرصد ما إذا كان الشريط موجودًا وتطبيق الارتفاع أو المساحة المتروكة المناسبَين على تصميمك.
@OptIn(ExperimentalLayoutApi::class) @Composable fun CaptionBar() { if (WindowInsets.isCaptionBarVisible) { Row( modifier = Modifier .windowInsetsTopHeight(WindowInsets.captionBar) .fillMaxWidth() .background(if (isSystemInDarkTheme()) Color.White else Color.Black), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Caption Bar Title", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(4.dp) ) } } }
setSystemBarsAppearance(appearance,mask): يضبط النمط المرئي لأشرطة النظام. يحدّد المعلمة الأولى علامات المظهر المستهدَفة، بينما تعمل المعلمة الثانية كقناع للتحكّم في العلامات المحدّدة التي يتم تعديلها.windowInsetsTopHeight(): يضبط تلقائيًا ارتفاع العنصر القابل للإنشاء ليتطابق مع شريط العناوين في النظام، ما يساعد الخلفية المخصّصة في ملء منطقة العنوان بدون وضع قيم البكسل بشكل ثابت.WindowInsets.captionBar: يوفّر أبعاد عناصر التحكّم في ميزة "العرض في نافذة" (إغلاق وتكبير وما إلى ذلك)، ما يسمح بتغيير حجم واجهة المستخدم أو إخفائها تلقائيًا عند الدخول إلى وضع "العرض في نافذة" أو الخروج منه.
لمعرفة المزيد من المعلومات، يمكنك الاطّلاع على مقالة لمحة عن حواف النوافذ. بالإضافة إلى العنوان، يمكنك عرض عناصر واجهة مستخدم أخرى في شريط العناوين، مثل علامات التبويب (كما في Google Chrome) أو أشرطة البحث أو صور الملف الشخصي الرمزية.
واجهة المستخدم
لتجنُّب تداخل واجهة المستخدم مع أزرار النظام، يوفّر Android 15 الـ
WindowInsets#getBoundingRects() method. تعرض هذه الطريقة قائمة بعناصر
Rect التي تمثّل المناطق التي تشغلها عناصر النظام. وأي مساحة متبقية في شريط العناوين هي منطقة آمنة يمكنك وضع محتوى مخصّص فيها بأمان.
يمكنك تبديل مظهر عناصر التسميات التوضيحية في النظام للمظهرَين الفاتح والداكن باستخدام
APPEARANCE_LIGHT_CAPTION_BARS. يمكنك الوصول إلى الحواف باستخدام
WindowInsets.Companion.captionBar() في Compose أو
WindowInsets.Type.captionBar() في طريقة العرض.
لمعرفة المزيد من المعلومات، يمكنك الاطّلاع على مقالة لمحة عن حواف النوافذ.
تعدّد المهام ودعم فتح نسختين من التطبيق
يُعدّ تعدّد المهام أساس ميزة "العرض في نافذة"، ويمكن أن يؤدي السماح بمثيلات متعددة من تطبيقك إلى زيادة إنتاجية المستخدمين بشكل كبير.
اعتبارًا من Android 15، يمكنك استخدام
PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI. من خلال ضبط هذه السمة في AndroidManifest.xml، يمكنك تحديد أنّ واجهة مستخدم النظام يجب أن توفّر خيارات (مثل زر "نافذة جديدة") لتشغيل التطبيق في مثيلات متعددة.
<application>
<property
android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
android:value="true" />
</application>
ملاحظة: في وضع "العرض في نافذة" وبيئات النوافذ المتعددة الأخرى، تُفتح المهام الجديدة في نافذة جديدة، لذا تحقَّق جيدًا من رحلة المستخدم في أي وقت يبدأ فيه تطبيقك مهامًا متعددة.
إدارة مثيلات التطبيق باستخدام إيماءات السحب
في وضع النوافذ المتعددة، يمكن للمستخدمين بدء مثيل جديد للتطبيق عن طريق سحب عنصر واجهة مستخدم (مثل علامة تبويب أو مستند) خارج نافذة التطبيق. يمكن للمستخدمين أيضًا نقل العناصر بين مثيلات مختلفة من التطبيق نفسه.
نقل البيانات باستخدام السحب والإفلات
لضبط دالة مركّبة كمصدر سحب لعملية السحب والإفلات لفتح نسختين من التطبيق ما يسمح للمستخدمين بسحب المحتوى إلى مثيل آخر من تطبيقك أو إنشاء مثيل جديد عن طريق إفلات المحتوى في منطقة فارغة من الشاشة، استخدِم عنصر التعديل dragAndDropSource. في تعبير lambda، أعِد
DragAndDropTransferData، مع تمرير ClipData الذي يحتوي على البيانات المطلوب
نقلها، والعلامات لضبط سلوك فتح نسختين من التطبيق.
يقدّم Android 15 علامتَين رئيسيتَين لفتح نسختين من التطبيق وتغيير حجم النوافذ على نمط أجهزة الكمبيوتر المكتبية:
DRAG_FLAG_GLOBAL_SAME_APPLICATION: تشير إلى أنّه يمكن لعملية السحب تجاوز حدود النوافذ (لمثيلات متعددة من التطبيق نفسه). عند استدعاءstartDragAndDrop()مع ضبط هذه العلامة، لن تتمكّن سوى النوافذ المرئية التي تنتمي إلى التطبيق نفسه من المشاركة في عملية السحب وتلقّي المحتوى المسحوب.
Modifier.dragAndDropSource { _ -> DragAndDropTransferData( clipData = ClipData.newPlainText("label", "Your data"), flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION ) }
DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: تسمح للمستخدمين ببدء مثيل جديد من تطبيقك عن طريق إفلات المحتوى المسحوب في منطقة فارغة من الشاشة، إذا لم تتعامل أي نافذة أخرى مع عملية الإفلات.- عند استخدام هذه العلامة، عليك توفير
IntentSenderباستخدامClipData.Item.Builder#setIntentSender()، والذي يستخدمه النظام لـ تشغيل النشاط الجديد إذا حدثت عملية إفلات لم تتم معالجتها.
- عند استخدام هذه العلامة، عليك توفير
Modifier.dragAndDropSource { _ -> val intent = Intent.makeMainActivity(activity.componentName).apply { putExtra("EXTRA_ITEM_ID", itemId) flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK or Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT } val pendingIntent = PendingIntent.getActivity( activity, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val data = ClipData( "Item $itemId", arrayOf(ClipDescription.MIMETYPE_TEXT_INTENT), ClipData.Item.Builder().setIntentSender(pendingIntent.intentSender).build() ) DragAndDropTransferData( clipData = data, flags = View.DRAG_FLAG_GLOBAL_SAME_APPLICATION or View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG, ) }
تلقّي البيانات المنقولة
لقبول البيانات من مثيل آخر، استخدِم المعدِّل dragAndDropTarget.
عليك طلب الأذونات بشكل صريح إذا كانت البيانات واردة من مثيل أو تطبيق مختلف.
Modifier.dragAndDropTarget( shouldStartDragAndDrop = { event -> event.toAndroidDragEvent().clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN) }, target = object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { requestDragAndDropPermissions(activity, event.toAndroidDragEvent()) val clipData = event.toAndroidDragEvent().clipData val item = clipData?.getItemAt(0)?.text if (item != null) { // Process the dropped text item here } return item != null } } )
الخطوات الرئيسية:
- الفلتر: استخدِم
shouldStartDragAndDropللتحقّق مما إذا كان نوع MIME للبيانات الواردة متوافقًا. - الأذونات: استخدِم
requestDragAndDropPermissions(event)للوصول إلى البيانات. - المعالجة: استخرِج البيانات في
onDropمعاودة الاتصال.
تحسينات إضافية
يمكنك تخصيص عمليات تشغيل التطبيقات ونقل التطبيقات من وضع "العرض في نافذة" إلى وضع ملء الشاشة.
تحديد الحجم والموضع التلقائيَين
لا تحتاج جميع التطبيقات، حتى إذا كانت قابلة لتغيير الحجم، إلى نافذة كبيرة لتقديم قيمة للمستخدم. يمكنك استخدام الطريقة ActivityOptions#setLaunchBounds() لتحديد حجم وموضع تلقائيَين عند تشغيل نشاط.
الدخول إلى وضع ملء الشاشة من مساحة سطح المكتب
يمكن أن تنتقل التطبيقات إلى وضع ملء الشاشة عن طريق استدعاء Activity#requestFullScreenMode(). تعرض هذه الطريقة التطبيق في وضع ملء الشاشة مباشرةً من وضع "العرض في نافذة".