إتاحة وضع النوافذ على سطح المكتب

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

في الشكل 1، يمكنك الاطّلاع على تنظيم الشاشة مع تفعيل ميزة "نوافذ سطح المكتب". ملاحظات:

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

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

عند فتح تطبيق في وضع عرض المحتوى في النافذة الحالية على سطح المكتب، يتم فتح التطبيقات الأخرى في نوافذ على سطح المكتب أيضًا.

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

يمكن للمستخدمين أيضًا استدعاء وضع النوافذ على سطح المكتب من القائمة التي تظهر أسفل مقبض النافذة عند النقر على المقبض أو استخدام اختصار لوحة المفاتيح مفتاح Meta (Windows أو Command أو Search) + Ctrl + السهم المتّجه للأسفل.

يخرج المستخدمون من وضع النوافذ على سطح المكتب من خلال إغلاق جميع النوافذ النشطة أو من خلال سحب مقبض النافذة في أعلى نافذة سطح المكتب وسحب التطبيق إلى أعلى الشاشة. يؤدي اختصار لوحة المفاتيح Meta + H أيضًا إلى الخروج من وضع "العرض في نافذة" وتشغيل التطبيقات في وضع ملء الشاشة مرة أخرى.

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

إمكانية تغيير الحجم ووضع التوافق

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

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

يتم تغيير حجم واجهة المستخدم للتطبيقات التي تم الإعلان عنها على أنّها غير قابلة لتغيير الحجم (أي resizeableActivity = false) مع الحفاظ على نسبة العرض إلى الارتفاع نفسها.

الشكل 4. يتم تغيير حجم واجهة المستخدم لتطبيق لا يمكن تغيير حجمه عند تغيير حجم النافذة.

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

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

لمزيد من المعلومات حول وضع التوافق لتطبيقات الكاميرا، يُرجى الاطّلاع على وضع توافق الجهاز.

الشكل 5. تحتفظ عدسة الكاميرا بنسبة العرض إلى الارتفاع عند تغيير حجم النافذة.

إضافات العنوان القابلة للتخصيص

تحتوي جميع التطبيقات التي تعمل في وضع النوافذ على سطح المكتب على شريط عنوان، حتى في وضع ملء الشاشة. تأكَّد من أنّ شريط العنوان لا يحجب محتوى تطبيقك. شريط العنوان هو نوع من أنواع أشرطة العناوين المضمّنة: WindowInsets.Companion.captionBar(); في طرق العرض، WindowInsets.Type.captionBar()، وهو جزء من أشرطة النظام.

يمكنك الاطّلاع على مزيد من المعلومات حول التعامل مع الحواف في عرض المحتوى من الحافة إلى الحافة في تطبيقك والتعامل مع حواف النافذة في Compose.

يمكن أيضًا تخصيص شريط العنوان. قدّمت الإصدار 15 من نظام التشغيل Android نوع المظهر APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND لجعل شريط العنوان شفافًا للسماح للتطبيقات برسم محتوى مخصّص داخل شريط العنوان.

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

يمكن للتطبيقات التبديل بين مظهر عناصر النظام داخل الترجمة والشرح للمظهرَين الفاتح والداكن باستخدام APPEARANCE_LIGHT_CAPTION_BARS، على غرار طريقة التبديل بين شريط الحالة وشريط التنقّل.

قدّم نظام التشغيل Android 15 أيضًا طريقة WindowInsets#getBoundingRects() التي تتيح للتطبيقات فحص هوامش شريط العنوان بتفصيل أكبر. يمكن للتطبيقات التمييز بين المساحات التي يرسم فيها النظام عناصر النظام والمساحات غير المستخدَمة التي يمكن للتطبيقات وضع محتوى مخصّص فيها بدون تداخل عناصر النظام.

تشير قائمة عناصر Rect التي تعرضها واجهة برمجة التطبيقات إلى مناطق في النظام يجب تجنُّبها. أي مساحة متبقية (يتم احتسابها عن طريق طرح المستطيلات من هوامش شريط العنوان) هي المساحة التي يمكن للتطبيق الرسم فيها بدون تداخل عناصر النظام مع إمكانية تلقّي الإدخال.

Chrome قبل وبعد تنفيذ العناوين المخصّصة
الشكل 6. Chrome قبل وبعد تنفيذ العناوين المخصّصة

لضبط مستطيلات استبعاد إيماءات النظام لعنوان مخصّص، نفِّذ ما يلي في طريقة العرض أو العنصر القابل للإنشاء:

// In a custom View's onLayout or a similar lifecycle method
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed) {
        // Calculate the height of your custom header
        val customHeaderHeight = 100 // Replace with your actual header height in pixels

        // Create a Rect covering your custom header area
        val exclusionRect = Rect(0, 0, width, customHeaderHeight)

        // Set the exclusion rects for the system
        systemGestureExclusionRects = listOf(exclusionRect)
    }
}

تعدُّد المهام ودعم فتح نسختين من التطبيق

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

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

يمكنك الإفصاح عن PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI في AndroidManifest.xml بتطبيقك ضمن علامة <activity>:

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:resizeableActivity="true">
    <meta-data
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</activity>

إدارة مثيلات التطبيقات باستخدام إيماءات السحب

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

الشكل 7. ابدأ نسخة جديدة من Chrome عن طريق سحب علامة تبويب من نافذة سطح المكتب.

يقدِّم Android 15 علامتَين لتخصيص سلوك السحب:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG: تشير إلى أنّه يجب تفويض عملية سحب لم يتم التعامل معها إلى النظام لبدءها إذا لم يتعامل أي إطار مرئي مع عملية الإفلات. عند استخدام هذا العلامة، على المتصل توفير ClipData مع ClipData.Item يحتوي على IntentSender غير قابل للتغيير لنشاط سيتم تشغيله (راجِع ClipData.Item.Builder#setIntentSender()). يمكن للنظام تشغيل الغرض أو عدم تشغيله استنادًا إلى عوامل مثل حجم الشاشة الحالي أو وضع النوافذ. إذا لم يُطلق النظام الغرض، سيتم إلغاؤه من خلال عملية السحب العادية.

  • DRAG_FLAG_GLOBAL_SAME_APPLICATION: يشير إلى أنّ عملية السحب يمكن أن تتجاوز حدود النافذة (بالنسبة إلى مثيلات متعددة من التطبيق نفسه).

    عند استدعاء [startDragAndDrop()][20] مع ضبط هذا العلامة، لن تتمكّن سوى النوافذ المرئية التي تنتمي إلى التطبيق نفسه من المشاركة في عملية السحب وتلقّي المحتوى المسحوب.

يوضّح المثال التالي كيفية استخدام هذه العلامات مع startDragAndDrop():

// Assuming 'view' is the View that initiates the drag
view.setOnLongClickListener {
    // Create an IntentSender for the activity you want to launch
    val launchIntent = Intent(view.context, NewInstanceActivity::class.java)
    val pendingIntent = PendingIntent.getActivity(
        view.context,
        0,
        launchIntent,
        PendingIntent.FLAG_IMMUTABLE // Ensure the PendingIntent is immutable
    )

    // Build the ClipData.Item with the IntentSender
    val item = ClipData.Item.Builder()
        .setIntentSender(pendingIntent.intentSender)
        .build()

    // Create ClipData with a simple description and the item
    val dragData = ClipData(
        ClipDescription("New Instance Drag", arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)),
        item
    )

    // Combine the drag flags
    val dragFlags = View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG or
                    View.DRAG_FLAG_GLOBAL_SAME_APPLICATION

    // Start the drag operation
    view.startDragAndDrop(
        dragData,                     // The ClipData to drag
        View.DragShadowBuilder(view), // A visual representation of the dragged item
        null,                         // Local state object (not used here)
        dragFlags                     // The drag flags
    )
    true // Indicate that the long click was consumed
}
الشكل 8. نقل علامة تبويب بين نسختَين من تطبيق Chrome

تحسينات إضافية

تخصيص عمليات تشغيل التطبيقات والتبديل بين التطبيقات من وضع "العرض في نافذة" إلى وضع "ملء الشاشة"

تحديد الحجم والموضع التلقائيَين

لا تحتاج بعض التطبيقات إلى نافذة كبيرة لتقديم قيمة للمستخدمين، حتى إذا كان يمكن تغيير حجمها. يمكنك استخدام طريقة ActivityOptions#setLaunchBounds() لتحديد حجم وموضع تلقائيَين عند تشغيل نشاط.

في ما يلي مثال على كيفية ضبط حدود التشغيل لنشاط:

val options = ActivityOptions.makeBasic()

// Define the desired launch bounds (left, top, right, bottom in pixels)
val launchBounds = Rect(100, 100, 700, 600) // Example: 600x500 window at (100,100)

// Apply the launch bounds to the ActivityOptions
options.setLaunchBounds(launchBounds)

// Start the activity with the specified options
val intent = Intent(this, MyActivity::class.java)
startActivity(intent, options.toBundle())

الانتقال إلى وضع ملء الشاشة من مساحة سطح المكتب

يمكن للتطبيقات عرض المحتوى بملء الشاشة من خلال استدعاء Activity#requestFullScreenMode(). تعرض الطريقة التطبيق بملء الشاشة مباشرةً من ميزة "العرض في نافذة".

لطلب وضع ملء الشاشة من نشاط، استخدِم الرمز التالي:

// In an Activity
fun enterFullScreen() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // Android 15 (U)
        requestFullScreenMode()
    }
}