پشتیبانی از پنجره دسکتاپ

پنجره‌بندی دسکتاپ به کاربران این امکان را می‌دهد که چندین برنامه را همزمان در پنجره‌های قابل تغییر اندازه اجرا کنند تا یک تجربه همه‌کاره و شبیه به دسکتاپ داشته باشند.

در شکل ۱، می‌توانید سازماندهی صفحه را با فعال بودن پنجره‌بندی دسکتاپ مشاهده کنید. نکات قابل توجه:

  • کاربران می‌توانند چندین برنامه را همزمان در کنار هم اجرا کنند.
  • نوار وظیفه در یک موقعیت ثابت در پایین صفحه نمایش قرار دارد که برنامه‌های در حال اجرا را نشان می‌دهد. کاربران می‌توانند برنامه‌ها را برای دسترسی سریع پین کنند.
  • نوار سربرگ جدید و قابل تنظیم، بالای هر پنجره را با کنترل‌هایی مانند کوچک‌نمایی و بزرگ‌نمایی تزئین می‌کند.
نمایشگر تبلت که چندین برنامه را در حال اجرا در پنجره‌های قابل تغییر اندازه با نوار وظیفه در پایین نشان می‌دهد.
شکل ۱. نمایش پنجره‌های دسکتاپ روی تبلت.

به طور پیش‌فرض، برنامه‌ها در تبلت‌های اندرویدی به صورت تمام صفحه باز می‌شوند. برای اجرای یک برنامه در حالت پنجره‌ای دسکتاپ، دستگیره پنجره را در بالای صفحه فشار داده و نگه دارید و دستگیره را به داخل رابط کاربری بکشید، همانطور که در شکل 2 نشان داده شده است.

وقتی یک برنامه در پنجره دسکتاپ باز است، برنامه‌های دیگر نیز در پنجره‌های دسکتاپ باز می‌شوند.

شکل ۲. برای ورود به پنجره‌بندی دسکتاپ، دستگیره پنجره برنامه را فشار داده، نگه دارید و بکشید.

کاربران همچنین می‌توانند از طریق منویی که در زیر دستگیره پنجره ظاهر می‌شود، پنجره‌های دسکتاپ را فراخوانی کنند. این منو زمانی نمایش داده می‌شود که شما روی دستگیره پنجره ضربه بزنید یا کلیک کنید یا از میانبر صفحه‌کلید Meta key (Windows، Command یا Search) + Ctrl + Down استفاده کنید.

کاربران با بستن تمام پنجره‌های فعال یا با گرفتن دستگیره پنجره در بالای پنجره دسکتاپ و کشیدن برنامه به بالای صفحه، از حالت پنجره‌ای دسکتاپ خارج می‌شوند. میانبر صفحه کلید Meta + H نیز از حالت پنجره‌ای دسکتاپ خارج شده و برنامه‌ها را دوباره به صورت تمام صفحه اجرا می‌کند.

برای بازگشت به حالت پنجره‌بندی دسکتاپ، روی کاشی فضای دسکتاپ در صفحه Recents ضربه بزنید یا کلیک کنید.

قابلیت تغییر اندازه و حالت سازگاری

در پنجره‌بندی دسکتاپ، برنامه‌هایی که جهت‌گیری قفل‌شده دارند، آزادانه قابل تغییر اندازه هستند. این بدان معناست که حتی اگر یک فعالیت به جهت‌گیری عمودی قفل شده باشد، کاربران همچنان می‌توانند اندازه برنامه را به پنجره‌ای با جهت‌گیری افقی تغییر دهند.

شکل ۳. تغییر اندازه پنجره یک برنامه با محدودیت نمایش عمودی به افقی.

برنامه‌هایی که به عنوان nonresizable (یعنی resizeableActivity = false ) تعریف شده‌اند، رابط کاربری‌شان با حفظ نسبت ابعاد یکسان، تغییر اندازه می‌دهد.

شکل ۴. رابط کاربری یک برنامه‌ی غیرقابل تغییر اندازه، با تغییر اندازه‌ی پنجره، تغییر اندازه می‌دهد.

برنامه‌های دوربینی که جهت را قفل می‌کنند یا به عنوان غیرقابل تغییر اندازه اعلام می‌شوند، رفتار خاصی با منظره‌یاب دوربین خود دارند: پنجره کاملاً قابل تغییر اندازه است، اما منظره‌یاب نسبت ابعاد یکسانی را حفظ می‌کند. با فرض اینکه برنامه‌ها همیشه در حالت عمودی یا افقی اجرا می‌شوند، برنامه‌ها فرضیاتی را به صورت کدنویسی شده یا در غیر این صورت انجام می‌دهند که منجر به محاسبات اشتباه در جهت یا نسبت ابعاد تصویر پیش‌نمایش یا گرفته شده می‌شود و در نتیجه تصاویر کشیده، به پهلو یا وارونه نمایش داده می‌شوند.

تا زمانی که برنامه‌ها برای پیاده‌سازی منظره‌یاب‌های دوربین کاملاً واکنش‌گرا آماده شوند، این روش ویژه، تجربه کاربری ساده‌تری را ارائه می‌دهد که اثرات فرضیات اشتباه را کاهش می‌دهد.

برای کسب اطلاعات بیشتر در مورد حالت سازگاری برای برنامه‌های دوربین، به حالت سازگاری دستگاه مراجعه کنید.

شکل ۵. منظره‌یاب دوربین نسبت ابعاد خود را با تغییر اندازه‌ی پنجره حفظ می‌کند.

درج هدر قابل تنظیم

همه برنامه‌هایی که در حالت پنجره‌ای دسکتاپ اجرا می‌شوند، حتی در حالت فراگیر ، دارای نوار سربرگ هستند. بررسی کنید که محتوای برنامه شما توسط نوار سربرگ پنهان نشده باشد. نوار سربرگ از نوع درج نوار عنوان است: WindowInsets.Companion.captionBar() ؛ در نماها، WindowInsets.Type.captionBar() ، که بخشی از نوارهای سیستم است.

می‌توانید در بخش «نمایش محتوا از لبه به لبه در برنامه» درباره مدیریت درج‌های داخلی بیشتر بیاموزید و در بخش «نوشتن»، درج‌های داخلی پنجره را مدیریت کنید .

نوار هدر نیز قابل تنظیم است. اندروید ۱۵ نوع ظاهری APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND را معرفی کرد تا نوار هدر را شفاف کند و به برنامه‌ها اجازه دهد محتوای سفارشی را درون نوار هدر ترسیم کنند.

سپس برنامه‌ها مسئول استایل‌دهی به بخش بالایی محتوای خود می‌شوند تا شبیه نوار عنوان (پس‌زمینه، محتوای سفارشی و غیره) به نظر برسند، به استثنای عناصر عنوان سیستم (دکمه‌های بستن و بزرگ‌نمایی) که توسط سیستم روی نوار عنوان شفاف بالای برنامه ترسیم می‌شوند.

برنامه‌ها می‌توانند ظاهر عناصر سیستمی داخل عنوان را برای تم‌های روشن و تاریک با استفاده از APPEARANCE_LIGHT_CAPTION_BARS تغییر دهند، مشابه نحوه تغییر وضعیت نوار وضعیت و نوار ناوبری.

اندروید ۱۵ همچنین متد WindowInsets#getBoundingRects() معرفی کرد که به برنامه‌ها امکان می‌دهد تا جزئیات بیشتری از درج‌های نوار عنوان را بررسی کنند. برنامه‌ها می‌توانند بین مناطقی که سیستم عناصر سیستم را ترسیم می‌کند و مناطق بلااستفاده که برنامه‌ها می‌توانند محتوای سفارشی را بدون همپوشانی عناصر سیستم در آنها قرار دهند، تمایز قائل شوند.

لیست اشیاء Rect که توسط API برگردانده می‌شوند، نواحی سیستمی را نشان می‌دهند که باید از آنها اجتناب شود. فضای باقی‌مانده (که با کم کردن مستطیل‌ها از نوار عنوان محاسبه می‌شود) جایی است که برنامه می‌تواند بدون همپوشانی عناصر سیستم و با قابلیت دریافت ورودی، ترسیم کند.

کروم قبل و بعد از پیاده‌سازی هدرهای سفارشی.
شکل ۶. کروم قبل و بعد از پیاده‌سازی هدرهای سفارشی.

برای تنظیم rect های حذف ژست سیستم برای یک هدر سفارشی، کد زیر را در view یا Composable خود پیاده‌سازی کنید:

// 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)
    }
}

پشتیبانی از چندوظیفگی و چند نمونه‌ای

چندوظیفگی هسته‌ی پنجره‌بندی دسکتاپ است و اجازه دادن به چندین نمونه از برنامه‌تان می‌تواند بهره‌وری کاربران را به شدت افزایش دهد.

اندروید ۱۵ 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>

مدیریت نمونه‌های برنامه با حرکات کشیدن

در حالت چند پنجره‌ای، کاربران می‌توانند با کشیدن یک عنصر نما از پنجره برنامه، یک نمونه برنامه جدید را شروع کنند. همچنین می‌توانند عناصر را بین نمونه‌های یک برنامه جابجا کنند.

شکل ۷. با کشیدن یک تب به بیرون از پنجره دسکتاپ، یک نمونه جدید از کروم را اجرا کنید.

اندروید ۱۵ دو پرچم (flag) برای سفارشی‌سازی رفتار درگ معرفی می‌کند:

  • DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG : نشان می‌دهد که اگر ClipData.Item ClipData حاوی یک IntentSender تغییرناپذیر است را به فعالیتی که قرار است راه‌اندازی شود، ارائه دهد (به ClipData.Item.Builder#setIntentSender() مراجعه کنید). سیستم می‌تواند بر اساس عواملی مانند اندازه‌ی فعلی صفحه نمایش یا حالت پنجره‌بندی، intent را راه‌اندازی کند یا نکند. اگر سیستم intent را راه‌اندازی نکند، intent با استفاده از جریان کشیدن عادی لغو می‌شود.

  • 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
}
شکل ۸. جابجایی یک تب بین دو نمونه از برنامه کروم.

بهینه‌سازی‌های اضافی

سفارشی‌سازی اجرای برنامه‌ها و انتقال برنامه‌ها از حالت پنجره‌ای دسکتاپ به حالت تمام صفحه.

اندازه و موقعیت پیش‌فرض را مشخص کنید

همه برنامه‌ها، حتی اگر قابلیت تغییر اندازه داشته باشند، برای ارائه ارزش به کاربر به یک پنجره بزرگ نیاز ندارند. می‌توانید از متد 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()
    }
}