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

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

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

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

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

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

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

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

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

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

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

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

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

برنامه‌هایی که به عنوان nonresizable (یعنی 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() : به طور خودکار ارتفاع Composable شما را برای مطابقت با نوار هدر سیستم تنظیم می‌کند و به پس‌زمینه سفارشی شما کمک می‌کند تا ناحیه عنوان را بدون کدگذاری مقادیر پیکسلی پر کند.

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

برای اطلاعات بیشتر، به بخش «درباره‌ی پنجره‌های تودرتو » مراجعه کنید. علاوه بر عنوان، می‌توانید عناصر رابط کاربری دیگری را در نوار عنوان نمایش دهید، مانند تب‌ها - مانند گوگل کروم - نوارهای جستجو یا آواتارهای پروفایل.

رابط کاربری

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

با استفاده از APPEARANCE_LIGHT_CAPTION_BARS ظاهر عناصر عنوان سیستم را برای تم‌های روشن و تیره تغییر دهید. با استفاده از WindowInsets.Companion.captionBar() در Compose یا WindowInsets.Type.captionBar() در Views به insets دسترسی پیدا کنید.

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

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

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

از اندروید ۱۵ به بعد، می‌توانید از PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI استفاده کنید. با تنظیم این ویژگی در AndroidManifest.xml ، مشخص می‌کنید که رابط کاربری سیستم باید گزینه‌هایی (مانند دکمه "پنجره جدید") را برای اجرای برنامه در چندین نمونه ارائه دهد.

<application>
    <property
        android:name="android.window.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI"
        android:value="true" />
</application>

توجه: در محیط‌های دسکتاپ و سایر محیط‌های چند پنجره‌ای، وظایف جدید در یک پنجره جدید باز می‌شوند، بنابراین هر بار که برنامه شما چندین وظیفه را شروع می‌کند، مسیر کاربر را دوباره بررسی کنید.

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

در حالت چند پنجره‌ای، کاربران می‌توانند با کشیدن یک عنصر رابط کاربری (مانند یک تب یا یک سند) از پنجره برنامه، یک نمونه برنامه جدید را شروع کنند. کاربران همچنین می‌توانند عناصر را بین نمونه‌های مختلف یک برنامه جابجا کنند.

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

انتقال اطلاعات با کشیدن و رها کردن

برای پیکربندی یک composable به عنوان منبع کشیدن برای کشیدن و رها کردن چند نمونه‌ای که به کاربران اجازه می‌دهد محتوا را به نمونه دیگری از برنامه شما بکشند، یا با رها کردن محتوا روی یک قسمت خالی از صفحه، یک نمونه جدید ایجاد کنند، از اصلاح‌کننده dragAndDropSource استفاده کنید. در لامبدا آن، DragAndDropTransferData برگردانید و ClipData را که حاوی داده‌هایی برای انتقال است، و پرچم‌هایی را برای پیکربندی رفتار چند نمونه‌ای ارسال کنید.

اندروید ۱۵ دو فلگ کلیدی برای پنجره‌بندی به سبک دسکتاپ و تعاملات چند نمونه‌ای معرفی می‌کند:

  • 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 : به کاربران اجازه می‌دهد تا با انداختن محتوای کشیده‌شده روی یک قسمت خالی از صفحه، یک نمونه جدید از برنامه شما را شروع کنند، البته اگر هیچ پنجره دیگری این انداختن را مدیریت نکند.
    • هنگام استفاده از این پرچم، باید با استفاده از ClipData.Item.Builder#setIntentSender() یک IntentSender ارائه دهید که سیستم در صورت وقوع یک drop مدیریت نشده، از آن برای راه‌اندازی activity جدید استفاده می‌کند.

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) را فراخوانی کنید.
  • Handle: استخراج داده‌ها در فراخوانی onDrop .

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

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

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

همه برنامه‌ها، حتی اگر قابلیت تغییر اندازه داشته باشند، برای ارائه ارزش به کاربر به یک پنجره بزرگ نیاز ندارند. می‌توانید از متد ActivityOptions#setLaunchBounds() برای تعیین اندازه و موقعیت پیش‌فرض هنگام اجرای یک فعالیت استفاده کنید.

از فضای دسکتاپ وارد حالت تمام صفحه شوید

برنامه‌ها می‌توانند با فراخوانی Activity#requestFullScreenMode() به حالت تمام صفحه بروند. این متد، برنامه را مستقیماً از پنجره دسکتاپ به صورت تمام صفحه نمایش می‌دهد.