تعبیه فعالیت

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

شکل 1. برنامه تنظیمات با فعالیت های کنار هم.

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

جاسازی فعالیت نیازی به بازآفرینی کد ندارد. شما با ایجاد یک فایل پیکربندی XML یا با برقراری تماس‌های Jetpack WindowManager API تعیین می‌کنید که چگونه برنامه‌تان فعالیت‌های خود را نمایش می‌دهد - کنار هم یا پشته‌ای.

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

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

جاسازی فعالیت در اکثر دستگاه‌های دارای صفحه نمایش بزرگ دارای Android 12L (سطح API 32) و بالاتر پشتیبانی می‌شود.

تقسیم پنجره وظیفه

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

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

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

شکل 2. دو فعالیت در کنار هم.

یا، فعالیتی که کل پنجره کار را اشغال کرده است می‌تواند با راه‌اندازی یک فعالیت جدید در کنار:

شکل 3. فعالیت A فعالیت B را به طرفین شروع می کند.

فعالیت هایی که قبلاً در یک تقسیم و به اشتراک گذاری یک پنجره وظیفه هستند می توانند فعالیت های دیگر را به روش های زیر راه اندازی کنند:

  • در کنار فعالیت دیگری:

    شکل 4. فعالیت A فعالیت C را به سمت فعالیت B شروع می کند.
  • به پهلو، و تقسیم را به طرفین منتقل کنید، فعالیت اولیه قبلی را پنهان کنید:

    شکل 5. فعالیت B فعالیت C را به پهلو شروع می کند و تقسیم را به طرفین تغییر می دهد.
  • راه اندازی یک فعالیت در محل در بالا؛ یعنی در همان پشته فعالیت:

    شکل 6. فعالیت B فعالیت C را بدون هیچ پرچم قصد اضافی شروع می کند.
  • یک پنجره کامل فعالیت را در همان کار راه اندازی کنید:

    شکل 7. فعالیت A یا فعالیت B فعالیت C را شروع می کند که پنجره وظیفه را پر می کند.

ناوبری برگشتی

انواع مختلف برنامه‌ها بسته به وابستگی‌های بین فعالیت‌ها یا نحوه راه‌اندازی رویداد برگشتی توسط کاربران، می‌توانند قوانین پیمایش برگشتی متفاوتی در حالت پنجره وظیفه تقسیم‌بندی داشته باشند، برای مثال:

  • با هم رفتن: اگر فعالیت‌ها مرتبط هستند، و یکی نباید بدون دیگری نشان داده شود، پیمایش برگشتی را می‌توان برای تکمیل هر دو پیکربندی کرد.
  • به تنهایی: اگر فعالیت‌ها کاملاً مستقل باشند، پیمایش برگشتی در یک فعالیت بر وضعیت فعالیت دیگری در پنجره کار تأثیر نمی‌گذارد.

هنگام استفاده از پیمایش دکمه‌ای، رویداد برگشت به آخرین فعالیت متمرکز ارسال می‌شود.

برای پیمایش مبتنی بر اشاره:

  • Android 14 (سطح API 34) و پایین‌تر — رویداد برگشتی به فعالیتی که حرکت در آن انجام شده ارسال می‌شود. وقتی کاربران از سمت چپ صفحه تند تند بکشند، رویداد برگشت به فعالیت در سمت چپ پنجره تقسیم ارسال می‌شود. هنگامی که کاربران از سمت راست صفحه تند کشیدند، رویداد برگشتی به فعالیت در قسمت سمت راست ارسال می‌شود.

  • اندروید 15 (سطح API 35) و بالاتر

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

    • در سناریوهایی که شامل دو فعالیت از برنامه‌های مختلف است (همپوشانی)، رویداد برگشتی به آخرین فعالیت متمرکز هدایت می‌شود و با رفتار پیمایش دکمه‌ها همسو می‌شود.

طرح چند صفحه ای

Jetpack WindowManager شما را قادر می‌سازد تا در دستگاه‌های دارای صفحه نمایش بزرگ با Android 12L (سطح API 32) یا بالاتر و در برخی از دستگاه‌های دارای نسخه‌های پلتفرم قبلی، فعالیتی را با طرح‌بندی چند صفحه‌ای ایجاد کنید. برنامه‌های موجود که بر اساس فعالیت‌های متعدد به جای قطعات یا طرح‌بندی‌های مبتنی بر نما هستند، مانند SlidingPaneLayout می‌توانند یک تجربه کاربری بهبودیافته با صفحه نمایش بزرگ بدون تغییر کد منبع ارائه کنند.

یکی از مثال‌های رایج تقسیم فهرست-جزئیات است. برای اطمینان از ارائه با کیفیت بالا، سیستم فعالیت لیست را شروع می کند و سپس برنامه بلافاصله فعالیت جزئیات را شروع می کند. سیستم انتقال منتظر می ماند تا هر دو فعالیت ترسیم شوند، سپس آنها را با هم نمایش می دهد. برای کاربر، این دو فعالیت به عنوان یک راه اندازی می شوند.

شکل 8. دو فعالیت به طور همزمان در یک طرح چند صفحه ای شروع شدند.

تقسیم ویژگی ها

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

برای قوانین تعریف شده در یک فایل پیکربندی XML، ویژگی های زیر را تنظیم کنید:

  • splitRatio : نسبت های ظرف را تنظیم می کند. مقدار یک عدد ممیز شناور در بازه باز (0.0، 1.0) است.
  • splitLayoutDirection : نحوه چیدمان کانتینرهای تقسیم نسبت به یکدیگر را مشخص می کند. ارزش ها عبارتند از:
    • ltr : از چپ به راست
    • rtl : از راست به چپ
    • locale : ltr یا rtl از روی تنظیمات محلی تعیین می شود

برای مثال به بخش پیکربندی XML مراجعه کنید.

برای قوانین ایجاد شده با استفاده از API های WindowManager، یک شی SplitAttributes با SplitAttributes.Builder ایجاد کنید و متدهای سازنده زیر را فراخوانی کنید:

  • setSplitType() : نسبت کانتینرهای تقسیم را تنظیم می کند. برای آرگومان های معتبر، از جمله متد SplitAttributes.SplitType.ratio() ، به SplitAttributes.SplitType مراجعه کنید.
  • setLayoutDirection() : چیدمان کانتینرها را تنظیم می کند. برای مقادیر ممکن به SplitAttributes.LayoutDirection مراجعه کنید.

برای مثال به بخش WindowManager API مراجعه کنید.

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

متغیرهای

فعالیت‌های مکان‌دار فعالیت‌های ثانویه خالی هستند که ناحیه‌ای از یک تقسیم فعالیت را اشغال می‌کنند. در نهایت قرار است با فعالیت دیگری که حاوی محتوا است جایگزین شوند. به عنوان مثال، یک فعالیت نگهدارنده مکان می‌تواند سمت ثانویه یک فعالیت تقسیم‌شده در یک طرح‌بندی فهرست-جزئیات را اشغال کند تا زمانی که یک مورد از فهرست انتخاب شود، در این مرحله یک فعالیت حاوی اطلاعات جزئیات برای آیتم فهرست انتخاب‌شده جایگزین مکان‌دار می‌شود.

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

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

با این حال، ویژگی stickyPlaceholder روش SplitPlaceholderRule یا setSticky() SplitPlaceholder.Builder می تواند رفتار پیش فرض را لغو کند. هنگامی که ویژگی یا متد مقدار true را مشخص می کند، هنگامی که اندازه نمایشگر از یک نمایشگر دو جداره به نمایشگر تک صفحه ای کاهش می یابد، سیستم مکان نگهدار را به عنوان بالاترین فعالیت در پنجره کار نمایش می دهد (برای مثال به پیکربندی تقسیم بندی مراجعه کنید) .

شکل 11. تاشو و باز شدن دستگاه تاشو. فعالیت متغیر مکان چسبنده است.

اندازه پنجره تغییر می کند

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

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

انباشته کردن فعالیت ها امکان پذیر است زیرا WindowManager فعالیت های موجود در پنجره ثانویه را در بالای فعالیت ها در پنجره اصلی مرتب می کند.

چندین فعالیت در پنجره ثانویه

اکتیویتی B فعالیت C را در جای خود و بدون پرچم های قصد اضافی شروع می کند:

تقسیم فعالیت شامل فعالیت های A، B، و C با C در بالای B انباشته شده است.

که منجر به ترتیب z زیر از فعالیت ها در همان کار می شود:

پشته فعالیت ثانویه حاوی فعالیت C در بالای B. پشته ثانویه در بالای پشته فعالیت اولیه حاوی فعالیت A انباشته شده است.

بنابراین، در یک پنجره کار کوچکتر، برنامه به یک فعالیت با C در بالای پشته کوچک می شود:

پنجره کوچکی که فقط فعالیت C را نشان می دهد.

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

اگر پیکربندی پنجره کار به اندازه بزرگتر بازیابی شود که بتواند چندین پنجره را در خود جای دهد، فعالیت ها دوباره در کنار هم نمایش داده می شوند.

تقسیم های انباشته

فعالیت B فعالیت C را به طرفین شروع می کند و تقسیم را به طرفین تغییر می دهد:

پنجره کار که فعالیت های A و B و سپس فعالیت های B و C را نشان می دهد.

نتیجه، ترتیب z فعالیت‌های زیر در همان کار است:

فعالیت های A، B و C در یک پشته. فعالیت ها به ترتیب زیر از بالا به پایین انباشته می شوند: C، B، A.

در یک پنجره کار کوچکتر، برنامه به یک فعالیت منفرد با C در بالا کوچک می شود:

پنجره کوچکی که فقط فعالیت C را نشان می دهد.

جهت گیری پرتره ثابت

تنظیم مانیفست android:screenOrientation به برنامه ها امکان می دهد فعالیت ها را به جهت گیری عمودی یا افقی محدود کنند. برای بهبود تجربه کاربری در دستگاه‌های صفحه‌نمایش بزرگ مانند تبلت‌ها و تاشوها، سازندگان دستگاه (OEM) می‌توانند درخواست‌های جهت‌گیری صفحه را نادیده بگیرند و برنامه را در جهت عمودی در نمایشگرهای افقی یا جهت افقی در نمایشگرهای عمودی را نادیده بگیرند.

شکل 12. فعالیت های جعبه نامه: پرتره ثابت در دستگاه افقی (سمت چپ)، منظره ثابت در دستگاه عمودی (راست).

به طور مشابه، هنگامی که تعبیه فعالیت فعال است، OEM ها می توانند دستگاه ها را برای فعالیت های پرتره ثابت صندوق نامه در جهت افقی روی صفحه نمایش های بزرگ (عرض ≥ 600dp) سفارشی کنند. هنگامی که یک فعالیت پرتره ثابت فعالیت دوم را راه اندازی می کند، دستگاه می تواند دو فعالیت را در کنار هم در یک نمایشگر دو صفحه نمایش دهد.

شکل 13. فعالیت پرتره ثابت A فعالیت B را به پهلو شروع می کند.

همیشه ویژگی android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED را به فایل مانیفست برنامه خود اضافه کنید تا به دستگاه‌ها اطلاع دهید که برنامه شما از جاسازی فعالیت پشتیبانی می‌کند (بخش پیکربندی تقسیم را ببینید). سپس دستگاه‌های سفارشی‌سازی‌شده OEM می‌توانند تعیین کنند که آیا فعالیت‌های پرتره ثابت در صندوق پست انجام شود یا خیر.

پیکربندی تقسیم

قوانین تقسیم، تقسیم‌های فعالیت را پیکربندی می‌کنند. شما قوانین تقسیم را در یک فایل پیکربندی XML یا با برقراری تماس های Jetpack WindowManager API تعریف می کنید.

در هر صورت، برنامه شما باید به کتابخانه WindowManager دسترسی داشته باشد و باید به سیستم اطلاع دهد که برنامه جاسازی فعالیت را اجرا کرده است.

موارد زیر را انجام دهید:

  1. به عنوان مثال، آخرین وابستگی کتابخانه WindowManager را به فایل build.gradle در سطح ماژول برنامه خود اضافه کنید:

    implementation 'androidx.window:window:1.1.0-beta02'

    کتابخانه WindowManager تمام اجزای مورد نیاز برای تعبیه فعالیت را فراهم می کند.

  2. به سیستم اطلاع دهید که برنامه شما جاسازی فعالیت را اجرا کرده است.

    ویژگی android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED را به عنصر <application> فایل مانیفست برنامه اضافه کنید و مقدار را روی true تنظیم کنید، برای مثال:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
        <application>
            <property
                android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"
                android:value="true" />
        </application>
    </manifest>
    

    در نسخه 1.1.0-alpha06 و نسخه‌های بعدی WindowManager، تقسیم‌های جاسازی فعالیت غیرفعال می‌شوند، مگر اینکه ویژگی به مانیفست اضافه شود و روی true تنظیم شود.

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

پیکربندی XML

برای ایجاد یک پیاده سازی مبتنی بر XML از تعبیه فعالیت، مراحل زیر را کامل کنید:

  1. یک فایل منبع XML ایجاد کنید که کارهای زیر را انجام دهد:

    • فعالیت هایی را تعریف می کند که یک تقسیم را به اشتراک می گذارند
    • گزینه های تقسیم را پیکربندی می کند
    • هنگامی که محتوا در دسترس نیست، یک مکان نگهدار برای ظرف ثانویه تقسیم ایجاد می کند
    • فعالیت هایی را مشخص می کند که هرگز نباید بخشی از یک تقسیم شوند

    به عنوان مثال:

    <!-- main_split_config.xml -->
    
    <resources
        xmlns:window="http://schemas.android.com/apk/res-auto">
    
        <!-- Define a split for the named activities. -->
        <SplitPairRule
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:finishPrimaryWithSecondary="never"
            window:finishSecondaryWithPrimary="always"
            window:clearTop="false">
            <SplitPairFilter
                window:primaryActivityName=".ListActivity"
                window:secondaryActivityName=".DetailActivity"/>
        </SplitPairRule>
    
        <!-- Specify a placeholder for the secondary container when content is
             not available. -->
        <SplitPlaceholderRule
            window:placeholderActivityName=".PlaceholderActivity"
            window:splitRatio="0.33"
            window:splitLayoutDirection="locale"
            window:splitMinWidthDp="840"
            window:splitMaxAspectRatioInPortrait="alwaysAllow"
            window:stickyPlaceholder="false">
            <ActivityFilter
                window:activityName=".ListActivity"/>
        </SplitPlaceholderRule>
    
        <!-- Define activities that should never be part of a split. Note: Takes
             precedence over other split rules for the activity named in the
             rule. -->
        <ActivityRule
            window:alwaysExpand="true">
            <ActivityFilter
                window:activityName=".ExpandedActivity"/>
        </ActivityRule>
    
    </resources>
    
  2. یک اولیه ساز ایجاد کنید.

    جزء WindowManager RuleController فایل پیکربندی XML را تجزیه می کند و قوانین را در دسترس سیستم قرار می دهد. یک Initializer کتابخانه راه‌اندازی Jetpack، فایل XML را در هنگام راه‌اندازی برنامه در دسترس RuleController قرار می‌دهد تا هنگام شروع هر فعالیتی، قوانین اعمال شوند.

    برای ایجاد یک مقداردهی اولیه، موارد زیر را انجام دهید:

    1. به عنوان مثال، آخرین وابستگی کتابخانه Jetpack Startup را به فایل build.gradle در سطح ماژول خود اضافه کنید:

      implementation 'androidx.startup:startup-runtime:1.1.1'

    2. کلاسی ایجاد کنید که رابط Initializer را پیاده سازی کند.

      Initializer با ارسال شناسه فایل پیکربندی XML ( main_split_config.xml ) به روش RuleController.parseRules() قوانین تقسیم را در دسترس RuleController قرار می دهد.

      کاتلین

      class SplitInitializer : Initializer<RuleController> {
      
          override fun create(context: Context): RuleController {
              return RuleController.getInstance(context).apply {
                  setRules(RuleController.parseRules(context, R.xml.main_split_config))
              }
          }
      
          override fun dependencies(): List<Class<out Initializer<*>>> {
              return emptyList()
          }
      }

      جاوا

      public class SplitInitializer implements Initializer<RuleController> {
      
           @NonNull
           @Override
           public RuleController create(@NonNull Context context) {
               RuleController ruleController = RuleController.getInstance(context);
               ruleController.setRules(
                   RuleController.parseRules(context, R.xml.main_split_config)
               );
               return ruleController;
           }
      
           @NonNull
           @Override
           public List<Class<? extends Initializer<?>>> dependencies() {
               return Collections.emptyList();
           }
      }
  3. یک ارائه دهنده محتوا برای تعاریف قوانین ایجاد کنید.

    androidx.startup.InitializationProvider به عنوان <provider> به فایل مانیفست برنامه خود اضافه کنید. یک ارجاع به اجرای اولیه ساز RuleController خود، SplitInitializer اضافه کنید:

    <!-- AndroidManifest.xml -->
    
    <provider android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <!-- Make SplitInitializer discoverable by InitializationProvider. -->
        <meta-data android:name="${applicationId}.SplitInitializer"
            android:value="androidx.startup" />
    </provider>
    

    InitializationProvider قبل از فراخوانی متد onCreate() برنامه SplitInitializer کشف و مقداردهی اولیه می کند. در نتیجه، زمانی که فعالیت اصلی برنامه شروع می شود، قوانین تقسیم اعمال می شوند.

WindowManager API

می‌توانید با تعداد انگشت شماری تماس API، تعبیه فعالیت را به صورت برنامه‌ریزی اجرا کنید. فراخوانی ها را در متد onCreate() یک زیر کلاس از Application انجام دهید تا مطمئن شوید که قوانین قبل از شروع هر فعالیتی قابل اجرا هستند.

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

  1. ایجاد یک قانون تقسیم:

    1. یک SplitPairFilter ایجاد کنید که فعالیت هایی را که تقسیم را به اشتراک می گذارند شناسایی می کند:

      کاتلین

      val splitPairFilter = SplitPairFilter(
         ComponentName(this, ListActivity::class.java),
         ComponentName(this, DetailActivity::class.java),
         null
      )

      جاوا

      SplitPairFilter splitPairFilter = new SplitPairFilter(
         new ComponentName(this, ListActivity.class),
         new ComponentName(this, DetailActivity.class),
         null
      );
    2. فیلتر را به مجموعه فیلتر اضافه کنید:

      کاتلین

      val filterSet = setOf(splitPairFilter)

      جاوا

      Set<SplitPairFilter> filterSet = new HashSet<>();
      filterSet.add(splitPairFilter);
    3. ایجاد ویژگی های طرح بندی برای تقسیم:

      کاتلین

      val splitAttributes: SplitAttributes = SplitAttributes.Builder()
          .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
          .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
          .build()

      جاوا

      final SplitAttributes splitAttributes = new SplitAttributes.Builder()
            .setSplitType(SplitAttributes.SplitType.ratio(0.33f))
            .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
            .build();

      SplitAttributes.Builder یک شی حاوی ویژگی های طرح بندی ایجاد می کند:

      • setSplitType : نحوه تخصیص ناحیه نمایش موجود به هر کانتینر فعالیت را مشخص می کند. نوع تقسیم نسبت، نسبت ناحیه نمایش موجود را که به ظرف اولیه اختصاص داده شده است، مشخص می کند. ظرف ثانویه باقیمانده منطقه نمایش موجود را اشغال می کند.
      • setLayoutDirection : نحوه چیدمان کانتینرهای اکتیویتی را نسبت به یکدیگر مشخص می کند، ابتدا کانتینر اصلی.
    4. یک SplitPairRule بسازید:

      کاتلین

      val splitPairRule = SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build()

      جاوا

      SplitPairRule splitPairRule = new SplitPairRule.Builder(filterSet)
          .setDefaultSplitAttributes(splitAttributes)
          .setMinWidthDp(840)
          .setMinSmallestWidthDp(600)
          .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
          .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.NEVER)
          .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
          .setClearTop(false)
          .build();

      SplitPairRule.Builder قانون را ایجاد و پیکربندی می کند:

      • filterSet : حاوی فیلترهای جفت تقسیم شده است که با شناسایی فعالیت هایی که یک تقسیم را به اشتراک می گذارند، تعیین می کند که چه زمانی قانون اعمال شود.
      • setDefaultSplitAttributes : ویژگی های طرح بندی را به قانون اعمال می کند.
      • setMinWidthDp : حداقل عرض نمایش (بر حسب پیکسل های مستقل از چگالی، dp) را تنظیم می کند که تقسیم را فعال می کند.
      • setMinSmallestWidthDp : حداقل مقداری را (بر حسب dp) تنظیم می کند که ابعاد کوچکتر از دو بعد نمایشگر برای فعال کردن تقسیم بدون توجه به جهت دستگاه باید داشته باشد.
      • setMaxAspectRatioInPortrait : حداکثر نسبت ابعاد نمایش (ارتفاع:عرض) را در جهت عمودی که تقسیم فعالیت برای آن نمایش داده می شود را تنظیم می کند. اگر نسبت تصویر یک نمایشگر عمودی از حداکثر نسبت تصویر بیشتر شود، بدون توجه به عرض نمایشگر، تقسیم‌بندی غیرفعال می‌شود. توجه: مقدار پیش‌فرض 1.4 است که باعث می‌شود فعالیت‌ها کل پنجره کار را در جهت عمودی در اکثر تبلت‌ها اشغال کنند. SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT و setMaxAspectRatioInLandscape نیز ببینید. مقدار پیش‌فرض برای Landscape ALWAYS_ALLOW است.
      • setFinishPrimaryWithSecondary : تعیین می کند که چگونه تمام فعالیت ها در کانتینر ثانویه بر فعالیت های کانتینر اولیه تأثیر می گذارد. NEVER نشان می‌دهد که سیستم نباید فعالیت‌های اولیه را زمانی که تمام فعالیت‌ها در کانتینر ثانویه تمام می‌شوند به پایان برساند ( به پایان فعالیت‌ها مراجعه کنید).
      • setFinishSecondaryWithPrimary : تعیین می کند که چگونه اتمام تمام فعالیت ها در کانتینر اولیه بر فعالیت های کانتینر ثانویه تأثیر می گذارد. ALWAYS نشان می‌دهد که سیستم باید همیشه فعالیت‌ها را در کانتینر ثانویه پایان دهد، زمانی که همه فعالیت‌ها در ظرف اولیه تمام می‌شوند ( به پایان فعالیت‌ها مراجعه کنید).
      • setClearTop : مشخص می کند که آیا تمام فعالیت ها در کانتینر ثانویه زمانی که یک اکتیویتی جدید در کانتینر راه اندازی می شود به پایان می رسد یا خیر. False مشخص می‌کند که فعالیت‌های جدید در بالای فعالیت‌هایی که قبلاً در کانتینر ثانویه قرار دارند، انباشته می‌شوند.
    5. نمونه singleton از WindowManager RuleController را دریافت کنید و قانون را اضافه کنید:

      کاتلین

        val ruleController = RuleController.getInstance(this)
        ruleController.addRule(splitPairRule)
        

      جاوا

        RuleController ruleController = RuleController.getInstance(this);
        ruleController.addRule(splitPairRule);
        
  2. هنگامی که محتوا در دسترس نیست، یک مکان نگهدار برای ظرف ثانویه ایجاد کنید:

    1. یک ActivityFilter ایجاد کنید که فعالیتی را که نگهدارنده مکان یک تقسیم پنجره وظیفه را با آن به اشتراک می گذارد، شناسایی می کند:

      کاتلین

      val placeholderActivityFilter = ActivityFilter(
          ComponentName(this, ListActivity::class.java),
          null
      )

      جاوا

      ActivityFilter placeholderActivityFilter = new ActivityFilter(
          new ComponentName(this, ListActivity.class),
          null
      );
    2. فیلتر را به مجموعه فیلتر اضافه کنید:

      کاتلین

      val placeholderActivityFilterSet = setOf(placeholderActivityFilter)

      جاوا

      Set<ActivityFilter> placeholderActivityFilterSet = new HashSet<>();
      placeholderActivityFilterSet.add(placeholderActivityFilter);
    3. یک SplitPlaceholderRule ایجاد کنید:

      کاتلین

      val splitPlaceholderRule = SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            Intent(context, PlaceholderActivity::class.java)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build()

      جاوا

      SplitPlaceholderRule splitPlaceholderRule = new SplitPlaceholderRule.Builder(
            placeholderActivityFilterSet,
            new Intent(context, PlaceholderActivity.class)
          ).setDefaultSplitAttributes(splitAttributes)
           .setMinWidthDp(840)
           .setMinSmallestWidthDp(600)
           .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ratio(1.5f))
           .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ALWAYS)
           .setSticky(false)
           .build();

      SplitPlaceholderRule.Builder قانون را ایجاد و پیکربندی می کند:

      • placeholderActivityFilterSet : حاوی فیلترهای فعالیت است که با شناسایی فعالیت هایی که فعالیت نگهدارنده مکان با آنها مرتبط است، تعیین می کند که چه زمانی قانون اعمال شود.
      • Intent : راه اندازی فعالیت نگهدارنده مکان را مشخص می کند.
      • setDefaultSplitAttributes : ویژگی های طرح بندی را به قانون اعمال می کند.
      • setMinWidthDp : حداقل عرض نمایش (برحسب پیکسل های مستقل از چگالی، dp) را تنظیم می کند که امکان تقسیم را فراهم می کند.
      • setMinSmallestWidthDp : حداقل مقداری را (بر حسب dp) تنظیم می کند که اندازه کوچکتر از دو بعد نمایشگر باید داشته باشد تا بدون در نظر گرفتن جهت دستگاه، تقسیم شود.
      • setMaxAspectRatioInPortrait : حداکثر نسبت ابعاد نمایش (ارتفاع:عرض) را در جهت عمودی که تقسیم فعالیت برای آن نمایش داده می شود را تنظیم می کند. توجه: مقدار پیش‌فرض 1.4 است که منجر به پر کردن فعالیت‌ها در پنجره کار در جهت عمودی در اکثر تبلت‌ها می‌شود. SPLIT_MAX_ASPECT_RATIO_PORTRAIT_DEFAULT و setMaxAspectRatioInLandscape نیز ببینید. مقدار پیش‌فرض برای Landscape ALWAYS_ALLOW است.
      • setFinishPrimaryWithPlaceholder : تعیین می کند که چگونه تکمیل فعالیت محل مکان بر فعالیت ها در ظرف اولیه تأثیر می گذارد. ALWAYS نشان می‌دهد که سیستم باید همیشه فعالیت‌ها را در محفظه اولیه زمانی که نگه‌دارنده به پایان می‌رسد پایان دهد ( به پایان فعالیت‌ها مراجعه کنید).
      • setSticky : تعیین می کند که آیا پس از اینکه مکان نگهدار برای اولین بار در یک تقسیم با حداقل عرض کافی ظاهر شد، فعالیت مکان نگهدار در بالای پشته فعالیت در نمایشگرهای کوچک ظاهر شود یا خیر.
    4. قانون را به RuleController WindowManager اضافه کنید:

      کاتلین

      ruleController.addRule(splitPlaceholderRule)

      جاوا

      ruleController.addRule(splitPlaceholderRule);
  3. فعالیت هایی را مشخص کنید که هرگز نباید بخشی از یک تقسیم شوند:

    1. یک ActivityFilter ایجاد کنید که فعالیتی را مشخص می کند که همیشه باید کل منطقه نمایش کار را اشغال کند:

      کاتلین

      val expandedActivityFilter = ActivityFilter(
        ComponentName(this, ExpandedActivity::class.java),
        null
      )

      جاوا

      ActivityFilter expandedActivityFilter = new ActivityFilter(
        new ComponentName(this, ExpandedActivity.class),
        null
      );
    2. فیلتر را به مجموعه فیلتر اضافه کنید:

      کاتلین

      val expandedActivityFilterSet = setOf(expandedActivityFilter)

      جاوا

      Set<ActivityFilter> expandedActivityFilterSet = new HashSet<>();
      expandedActivityFilterSet.add(expandedActivityFilter);
    3. یک ActivityRule ایجاد کنید:

      کاتلین

      val activityRule = ActivityRule.Builder(expandedActivityFilterSet)
          .setAlwaysExpand(true)
          .build()

      جاوا

      ActivityRule activityRule = new ActivityRule.Builder(
          expandedActivityFilterSet
      ).setAlwaysExpand(true)
       .build();

      ActivityRule.Builder قانون را ایجاد و پیکربندی می کند:

      • expandedActivityFilterSet : حاوی فیلترهای فعالیت است که تعیین می کند با شناسایی فعالیت هایی که می خواهید از تقسیم ها حذف شوند، چه زمانی قانون اعمال شود.
      • setAlwaysExpand : مشخص می کند که آیا فعالیت باید کل پنجره کار را پر کند یا خیر.
    4. قانون را به RuleController WindowManager اضافه کنید:

      کاتلین

      ruleController.addRule(activityRule)

      جاوا

      ruleController.addRule(activityRule);

تعبیه برنامه های متقابل

در Android 13 (سطح API 33) و بالاتر، برنامه‌ها می‌توانند فعالیت‌های برنامه‌های دیگر را جاسازی کنند. تعبیه فعالیت های متقابل یا متقاطع UID ، یکپارچه سازی بصری فعالیت ها از چندین برنامه Android را امکان پذیر می کند. سیستم یک فعالیت برنامه میزبان و یک فعالیت جاسازی شده از یک برنامه دیگر را در کنار هم یا بالا و پایین صفحه نمایش می دهد، درست مانند جاسازی فعالیت تک برنامه.

برای مثال، برنامه تنظیمات می‌تواند فعالیت انتخابگر تصویر زمینه را از برنامه WallpaperPicker جاسازی کند:

شکل 14. برنامه تنظیمات (منو در سمت چپ) با انتخابگر تصویر زمینه به عنوان فعالیت جاسازی شده (راست).

مدل اعتماد

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

برای جلوگیری از سوء استفاده از جاسازی فعالیت‌های بین برنامه‌ای، Android از برنامه‌ها می‌خواهد که برای اجازه دادن به جاسازی فعالیت‌های خود شرکت کنند. برنامه‌ها می‌توانند میزبان‌ها را به‌عنوان مورد اعتماد یا غیرقابل اعتماد معرفی کنند.

میزبان های قابل اعتماد

برای اجازه دادن به سایر برنامه‌ها برای جاسازی و کنترل کامل ارائه فعالیت‌ها از برنامه شما، گواهی SHA-256 برنامه میزبان را در ویژگی android:knownActivityEmbeddingCerts عناصر <activity> یا <application> فایل مانیفست برنامه خود مشخص کنید.

مقدار android:knownActivityEmbeddingCerts را به عنوان یک رشته تنظیم کنید:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@string/known_host_certificate_digest"
    ... />

یا برای تعیین چندین گواهی، آرایه ای از رشته ها:

<activity
    android:name=".MyEmbeddableActivity"
    android:knownActivityEmbeddingCerts="@array/known_host_certificate_digests"
    ... />

که به منبعی مانند زیر ارجاع می دهد:

<resources>
    <string-array name="known_host_certificate_digests">
      <item>cert1</item>
      <item>cert2</item>
      ...
    </string-array>
</resources>

دارندگان برنامه می توانند با اجرای کار Gradle signingReport خلاصه گواهی SHA را دریافت کنند. خلاصه گواهی اثر انگشت SHA-256 بدون کولون جداکننده است. برای اطلاعات بیشتر، به اجرای گزارش امضا و احراز هویت مشتری مراجعه کنید.

هاست های غیر قابل اعتماد

برای اینکه به هر برنامه ای اجازه دهید فعالیت های برنامه شما را جاسازی کند و نمایش آنها را کنترل کند، ویژگی android:allowUntrustedActivityEmbedding در عناصر <activity> یا <application> در مانیفست برنامه مشخص کنید، به عنوان مثال:

<activity
    android:name=".MyEmbeddableActivity"
    android:allowUntrustedActivityEmbedding="true"
    ... />

مقدار پیش‌فرض ویژگی false است، که از تعبیه فعالیت بین برنامه‌ای جلوگیری می‌کند.

احراز هویت سفارشی

برای کاهش خطرات تعبیه فعالیت های نامعتبر، یک مکانیسم احراز هویت سفارشی ایجاد کنید که هویت میزبان را تأیید می کند. اگر گواهی‌های میزبان را می‌شناسید، از کتابخانه androidx.security.app.authenticator برای احراز هویت استفاده کنید. اگر میزبان پس از تعبیه فعالیت شما احراز هویت شود، می توانید محتوای واقعی را نمایش دهید. در غیر این صورت، می توانید به کاربر اطلاع دهید که اقدام مجاز نبوده و محتوا را مسدود کنید.

از متد ActivityEmbeddingController#isActivityEmbedded() از کتابخانه Jetpack WindowManager برای بررسی اینکه آیا یک میزبان فعالیت شما را تعبیه می کند، استفاده کنید، به عنوان مثال:

کاتلین

fun isActivityEmbedded(activity: Activity): Boolean {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity)
}

جاوا

boolean isActivityEmbedded(Activity activity) {
    return ActivityEmbeddingController.getInstance(this).isActivityEmbedded(activity);
}

حداقل محدودیت اندازه

سیستم Android حداقل ارتفاع و عرض مشخص شده در عنصر مانیفست برنامه <layout> را برای فعالیت های جاسازی شده اعمال می کند. اگر برنامه ای حداقل ارتفاع و عرض را مشخص نکند، مقادیر پیش فرض سیستم اعمال می شود ( sw220dp ).

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

<activity-alias>

برای اینکه جاسازی فعالیت قابل اعتماد یا غیرقابل اعتماد با عنصر <activity-alias> کار کند، android:knownActivityEmbeddingCerts یا android:allowUntrustedActivityEmbedding باید به جای نام مستعار، برای فعالیت هدف اعمال شود. خط مشی ای که امنیت سرور سیستم را تأیید می کند بر اساس پرچم های تنظیم شده روی هدف است، نه نام مستعار.

برنامه میزبان

برنامه های میزبان، تعبیه فعالیت بین برنامه ای را به همان روشی که تعبیه فعالیت تک برنامه ای را پیاده سازی می کنند، پیاده سازی می کنند. اشیاء SplitPairRule و SplitPairFilter یا ActivityRule و ActivityFilter فعالیت های تعبیه شده و تقسیم پنجره وظیفه را مشخص می کنند. قوانین تقسیم به صورت ایستا در XML یا در زمان اجرا با استفاده از فراخوانی های Jetpack WindowManager API تعریف می شوند.

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

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

یک برنامه میزبان می تواند فعالیت های خود را بدون محدودیت تعبیه کند تا زمانی که فعالیت ها در همان کار راه اندازی شوند.

نمونه های تقسیم شده

تقسیم از پنجره کامل

شکل 15. فعالیت A فعالیت B را به طرفین شروع می کند.

بدون نیاز به بازسازی مجدد می توانید پیکربندی تقسیم را به صورت ایستا یا در زمان اجرا تعریف کنید و سپس Context#startActivity() بدون هیچ پارامتر اضافی فراخوانی کنید.

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم به طور پیش فرض

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

شکل 16. تقسیم ایجاد شده با باز کردن دو فعالیت به طور همزمان. یکی از فعالیت ها جایگاه نگهدارنده است.

برای ایجاد یک تقسیم با یک مکان نگهدار، یک مکان نگهدار ایجاد کنید و آن را با فعالیت اصلی مرتبط کنید:

<SplitPlaceholderRule
    window:placeholderActivityName=".PlaceholderActivity">
    <ActivityFilter
        window:activityName=".MainActivity"/>
</SplitPlaceholderRule>

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

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

درخواست راه اندازی باید به فعالیت اصلی هدایت شود و فعالیت جزئیات هدف باید به صورت تقسیمی راه اندازی شود. سیستم به طور خودکار بر اساس عرض نمایش موجود، ارائه صحیح را انتخاب می کند - پشته یا کنار هم -.

کاتلین

override fun onCreate(savedInstanceState Bundle?) {
    . . .
    RuleController.getInstance(this)
        .addRule(SplitPairRule.Builder(filterSet).build())
    startActivity(Intent(this, DetailActivity::class.java))
}

جاوا

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    RuleController.getInstance(this)
        .addRule(new SplitPairRule.Builder(filterSet).build());
    startActivity(new Intent(this, DetailActivity.class));
}

مقصد پیوند عمیق ممکن است تنها فعالیتی باشد که باید در پشته ناوبری پشتی در دسترس کاربر باشد، و ممکن است بخواهید از رد کردن فعالیت جزئیات و باقی گذاشتن تنها فعالیت اصلی خودداری کنید:

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

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

در عوض، می‌توانید هر دو فعالیت را همزمان با استفاده از ویژگی finishPrimaryWithSecondary به پایان برسانید:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".ListActivity"
        window:secondaryActivityName=".DetailActivity"/>
</SplitPairRule>

بخش تنظیمات پیکربندی را ببینید.

فعالیت های متعدد در کانتینرهای تقسیم شده

قرار دادن چندین فعالیت در یک کانتینر تقسیم شده، کاربران را قادر می سازد به محتوای عمیق دسترسی داشته باشند. به عنوان مثال، با تقسیم جزئیات لیست، کاربر ممکن است نیاز داشته باشد که به بخش جزئیات فرعی برود اما فعالیت اصلی را در جای خود نگه دارد:

شکل 18. فعالیت در قسمت دوم پنجره وظیفه باز شد.

کاتلین

class DetailActivity {
    . . .
    fun onOpenSubDetail() {
      startActivity(Intent(this, SubDetailActivity::class.java))
    }
}

جاوا

public class DetailActivity {
    . . .
    void onOpenSubDetail() {
        startActivity(new Intent(this, SubDetailActivity.class));
    }
}

فعالیت جزییات فرعی در بالای فعالیت جزئیات قرار می گیرد و آن را پنهان می کند:

سپس کاربر می تواند با پیمایش در پشته به سطح جزئیات قبلی برگردد:

شکل 19. فعالیت از بالای پشته حذف شد.

انباشتن فعالیت‌ها روی هم، رفتار پیش‌فرض زمانی است که فعالیت‌ها از یک فعالیت در یک کانتینر ثانویه راه‌اندازی می‌شوند. فعالیت‌هایی که از کانتینر اولیه در یک تقسیم فعال راه‌اندازی می‌شوند نیز به کانتینر ثانویه در بالای پشته فعالیت ختم می‌شوند.

فعالیت در یک کار جدید

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

شکل 20. فعالیت C را در یک کار جدید از فعالیت B شروع کنید.

جایگزینی فعالیت

فعالیت ها را می توان در پشته کانتینر ثانویه جایگزین کرد. به عنوان مثال، زمانی که فعالیت اولیه برای ناوبری سطح بالا استفاده می شود و فعالیت ثانویه یک مقصد انتخاب شده است. هر انتخاب از پیمایش سطح بالا باید یک فعالیت جدید را در ظرف ثانویه شروع کند و فعالیت یا فعالیت هایی را که قبلاً در آنجا بودند حذف کند.

شکل 21. فعالیت ناوبری سطح بالا در صفحه اصلی جایگزین فعالیت های مقصد در پنجره ثانویه می شود.

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

در چنین مواردی صفحه A باید از پشته حذف شود.

رفتار پیش‌فرض هنگام پرتاب به کنار در یک کانتینر جدید بر روی یک تقسیم موجود این است که کانتینرهای ثانویه جدید را در بالا قرار دهید و کانتینرهای قدیمی را در پشته نگه دارید. می‌توانید تقسیم‌ها را طوری پیکربندی کنید که کانتینرهای ثانویه قبلی را با clearTop پاک کرده و فعالیت‌های جدید را به طور معمول راه‌اندازی کنید.

<SplitPairRule
    window:clearTop="true">
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenA"/>
    <SplitPairFilter
        window:primaryActivityName=".Menu"
        window:secondaryActivityName=".ScreenB"/>
</SplitPairRule>

کاتلین

class MenuActivity {
    . . .
    fun onMenuItemSelected(selectedMenuItem: Int) {
        startActivity(Intent(this, classForItem(selectedMenuItem)))
    }
}

جاوا

public class MenuActivity {
    . . .
    void onMenuItemSelected(int selectedMenuItem) {
        startActivity(new Intent(this, classForItem(selectedMenuItem)));
    }
}

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

تقسیم های متعدد

برنامه‌ها می‌توانند با راه‌اندازی فعالیت‌های اضافی در کنار، ناوبری عمیق چند سطحی را ارائه دهند.

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

شکل 22. فعالیت B فعالیت C را به طرفین شروع می کند.

پشته پشتی شامل تمام فعالیت‌هایی است که قبلاً باز شده‌اند، بنابراین کاربران می‌توانند پس از اتمام C به تقسیم A/B بروند.

فعالیت های A، B و C در یک پشته. فعالیت ها به ترتیب زیر از بالا به پایین جمع می شوند: C ، B ، A.

برای ایجاد یک تقسیم جدید ، فعالیت جدید را از ظرف ثانویه موجود به طرف آن راه اندازی کنید. تنظیمات را برای هر دو شکاف A/B و B/C اعلام کنید و فعالیت C را به طور عادی از B:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
    <SplitPairFilter
        window:primaryActivityName=".B"
        window:secondaryActivityName=".C"/>
</SplitPairRule>

کاتلین

class B {
    . . .
    fun onOpenC() {
        startActivity(Intent(this, C::class.java))
    }
}

جاوا

public class B {
    . . .
    void onOpenC() {
        startActivity(new Intent(this, C.class));
    }
}

واکنش نشان می دهد به تغییرات حالت تقسیم

فعالیت های مختلف در یک برنامه می تواند دارای عناصر UI باشد که عملکرد یکسانی را انجام می دهند. به عنوان مثال ، کنترلی که پنجره ای حاوی تنظیمات حساب را باز می کند.

شکل 23 فعالیت های مختلف با عناصر UI از نظر عملکردی یکسان.

اگر دو فعالیت که یک عنصر UI مشترک دارند ، در یک تقسیم قرار دارند ، نشان دادن این عنصر در هر دو فعالیت ، زائد و شاید گیج کننده است.

شکل 24 عناصر UI کپی در تقسیم فعالیت.

برای دانستن اینکه چه زمانی فعالیت ها در یک تقسیم هستند ، جریان SplitController.splitInfoList را بررسی کنید یا یک شنونده را با SplitControllerCallbackAdapter برای تغییر در حالت تقسیم ثبت کنید. سپس ، UI را بر این اساس تنظیم کنید:

کاتلین

val layout = layoutInflater.inflate(R.layout.activity_main, null)
val view = layout.findViewById<View>(R.id.infoButton)
lifecycleScope.launch {
    lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        splitController.splitInfoList(this@SplitDeviceActivity) // The activity instance.
            .collect { list ->
                view.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE
            }
    }
}

جاوا

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    . . .
    new SplitControllerCallbackAdapter(SplitController.getInstance(this))
        .addSplitListener(
            this,
            Runnable::run,
            splitInfoList -> {
                View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
                layout.findViewById(R.id.infoButton).setVisibility(
                    splitInfoList.isEmpty() ? View.VISIBLE : View.GONE);
            });
}

Coroutines را می توان در هر حالت چرخه عمر راه اندازی کرد ، اما به طور معمول در حالت STARTED برای حفظ منابع راه اندازی می شود (برای کسب اطلاعات بیشتر به استفاده از Coroutines Kotlin با اجزای آگاه از چرخه عمر مراجعه کنید).

در هر حالت چرخه عمر ، تماس تلفنی را می توان انجام داد ، از جمله هنگامی که یک فعالیت متوقف می شود. شنوندگان معمولاً باید در onStart() ثبت شوند و در onStop() ثبت نشده باشند.

معین

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

یک فعالیت می تواند مجبور شود همیشه با استفاده از پیکربندی Expand ، پنجره کار را پر کند:

<ActivityRule
    window:alwaysExpand="true">
    <ActivityFilter
        window:activityName=".FullWidthActivity"/>
</ActivityRule>

فعالیت های پایان

کاربران می توانند با کشیدن از لبه نمایش ، فعالیت های هر دو طرف تقسیم را به پایان برسانند:

شکل 25 فعالیت اتمام ژست را بکشید.
شکل 26. فعالیت اتمام ژست را بکشید.

اگر دستگاه برای استفاده از دکمه عقب به جای ناوبری ژست تنظیم شده باشد ، ورودی به فعالیت متمرکز ارسال می شود - فعالیتی که آخرین بار لمس شده یا راه اندازی شده است.

تأثیر این که تمام فعالیت ها در یک ظرف بر روی ظرف مخالف قرار دارد ، بستگی به پیکربندی تقسیم دارد.

ویژگی های پیکربندی

شما می توانید ویژگی های قانون جفت تقسیم را برای پیکربندی چگونگی پایان کار تمام فعالیت ها در یک طرف تقسیم بر فعالیت های آن طرف تقسیم تأثیر می گذارد. صفات عبارتند از:

  • window:finishPrimaryWithSecondary - چگونگی پایان کار تمام فعالیت ها در ظرف ثانویه بر فعالیت های موجود در ظرف اصلی تأثیر می گذارد
  • window:finishSecondaryWithPrimary - چگونگی پایان کار تمام فعالیت ها در کانتینر اصلی بر فعالیت های موجود در ظرف ثانویه تأثیر می گذارد

مقادیر احتمالی ویژگی ها عبارتند از:

  • always - همیشه فعالیت ها را در ظرف مرتبط به پایان برسانید
  • never - هرگز فعالیت ها را در ظرف مرتبط به پایان نرسانید
  • adjacent - هنگامی که دو ظروف در مجاورت یکدیگر نمایش داده می شوند ، فعالیت ها را در ظرف مرتبط به پایان برسانید ، اما نه وقتی که این دو ظروف جمع شده اند

به عنوان مثال:

<SplitPairRule
    &lt;!-- Do not finish primary container activities when all secondary container activities finish. --&gt;
    window:finishPrimaryWithSecondary="never"
    &lt;!-- Finish secondary container activities when all primary container activities finish. --&gt;
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

پیکربندی پیش فرض

هنگامی که تمام فعالیت ها در یک ظرف یک تقسیم تقسیم ، ظرف باقیمانده کل پنجره را اشغال می کند:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم فعالیت های A و B. A به پایان رسیده است و B را برای اشغال کل پنجره رها می کند.

فعالیت های حاوی فعالیت های A و B. B به پایان رسیده است و باعث می شود که کل پنجره را اشغال کند.

فعالیت ها را با هم تمام کنید

هنگامی که تمام فعالیت های موجود در ظرف ثانویه به پایان برسد ، فعالیت ها را در ظرف اصلی به طور خودکار به پایان برسانید:

<SplitPairRule
    window:finishPrimaryWithSecondary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم فعالیت های A و B. B به پایان رسیده است ، که همچنین A را به پایان می رساند و پنجره کار را خالی می کند.

تقسیم فعالیت های A و B. A به پایان رسید و B را در پنجره کار تنها گذاشت.

هنگامی که تمام فعالیت های موجود در ظرف اصلی به پایان برسد ، فعالیت ها را در ظرف ثانویه به طور خودکار به پایان برسانید:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم فعالیت های A و B. A به پایان رسید ، که B نیز به پایان می رسد و پنجره کار را خالی می کند.

تقسیم فعالیت های A و B. B به پایان رسید و یک تنها در پنجره کار باقی می ماند.

فعالیت ها را با هم به پایان برسانید وقتی همه فعالیت ها در ظرف اولیه یا ثانویه به پایان برسد:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم فعالیت های A و B. A به پایان رسید ، که B نیز به پایان می رسد و پنجره کار را خالی می کند.

تقسیم فعالیت های A و B. B به پایان رسیده است ، که همچنین A را به پایان می رساند و پنجره کار را خالی می کند.

چندین فعالیت را در ظروف به پایان برسانید

اگر چندین فعالیت در یک ظرف تقسیم شده جمع شود ، پایان دادن به فعالیت در پایین پشته ، به طور خودکار فعالیت ها را در بالا به پایان نمی رساند.

به عنوان مثال ، اگر دو فعالیت در ظرف ثانویه باشد ، C در بالای B:

پشته فعالیت ثانویه حاوی فعالیت C که در بالای B جمع شده است در بالای پشته فعالیت prmary حاوی فعالیت A. جمع می شود.

و پیکربندی تقسیم با پیکربندی فعالیت های A و B تعریف می شود:

<SplitPairRule>
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

به پایان رساندن فعالیت برتر ، تقسیم را حفظ می کند.

تقسیم با فعالیت A در کانتینر اولیه و فعالیت های B و C در ثانویه ، C در بالای B. C به پایان رسیده و A و B را در تقسیم فعالیت باقی می گذارد.

به پایان رساندن فعالیت پایین (ریشه) ظرف ثانویه ، فعالیت های موجود در آن را از بین نمی برد. و بنابراین ، تقسیم را نیز حفظ می کند.

تقسیم با فعالیت A در ظروف اولیه و فعالیت های B و C در ثانویه ، C در بالای B. B به پایان می رسد و A و C را در تقسیم فعالیت باقی می گذارد.

هرگونه قانون اضافی برای پایان فعالیت در کنار هم ، مانند اتمام فعالیت ثانویه با اولیه ، نیز اجرا می شود:

<SplitPairRule
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم با فعالیت A در ظروف اولیه و فعالیت های B و C در ظرف ثانویه ، C در بالای B. یک پایان B. A Finishs ، همچنین به پایان رساندن B و C.

و هنگامی که تقسیم پیکربندی شده است تا ابتدایی و ثانویه را با هم به پایان برساند:

<SplitPairRule
    window:finishPrimaryWithSecondary="always"
    window:finishSecondaryWithPrimary="always">
    <SplitPairFilter
        window:primaryActivityName=".A"
        window:secondaryActivityName=".B"/>
</SplitPairRule>

تقسیم با فعالیت A در کانتینر اولیه و فعالیت های B و C در ثانویه ، C در بالای B. C به پایان رسیده و A و B را در تقسیم فعالیت باقی می گذارد.

تقسیم با فعالیت A در ظروف اولیه و فعالیت های B و C در ثانویه ، C در بالای B. B به پایان می رسد و A و C را در تقسیم فعالیت باقی می گذارد.

تقسیم با فعالیت A در کانتینر اولیه و فعالیت های B و C در ثانویه ، C در بالای B. A به پایان رسید.

در زمان اجرا ، ویژگی های تقسیم را تغییر دهید

خواص یک تقسیم فعال و قابل مشاهده قابل تغییر نیست. تغییر قوانین تقسیم بر پرتاب فعالیت های اضافی و ظروف جدید تأثیر می گذارد ، اما شکاف های موجود و فعال نیست.

برای تغییر خصوصیات شکاف های فعال ، فعالیت جانبی یا فعالیت های موجود در تقسیم را به پایان برسانید و دوباره با پیکربندی جدید به طرف آن راه اندازی کنید.

فعالیت را از یک تقسیم به پنجره کامل استخراج کنید

یک پیکربندی جدید ایجاد کنید که فعالیت جانبی را به طور کامل نشان می دهد ، و سپس فعالیت را با نیتی که به همان نمونه برطرف می شود ، مجدداً راه اندازی کنید.

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

تعبیه فعالیت در Android 12L (API سطح 32) و بالاتر پشتیبانی می شود ، اما در برخی از دستگاه ها که نسخه های قبلی پلت فرم را نیز اجرا می کنند نیز موجود است. برای بررسی در زمان اجرا برای در دسترس بودن ویژگی ، از ویژگی SplitController.splitSupportStatus یا SplitController.getSplitSupportStatus() استفاده کنید:

کاتلین

if (SplitController.getInstance(this).splitSupportStatus ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

جاوا

if (SplitController.getInstance(this).getSplitSupportStatus() ==
     SplitController.SplitSupportStatus.SPLIT_AVAILABLE) {
     // Device supports split activity features.
}

در صورت عدم پشتیبانی از شکاف ، فعالیت ها در بالای پشته فعالیت (به دنبال مدل تعبیه غیر فعال) راه اندازی می شوند.

جلوگیری از نادیده گرفتن سیستم

تولید کنندگان دستگاه های Android (تولید کنندگان تجهیزات اصلی یا OEM) می توانند فعالیت تعبیه شده را به عنوان تابعی از سیستم دستگاه پیاده سازی کنند. این سیستم قوانین تقسیم شده را برای برنامه های چند فعالیت مشخص می کند و بر رفتار پنجره ها برنامه ها غلبه می کند. سیستم غلبه بر برنامه های چند فعالیت را به یک حالت تعبیه شده فعالیت تعریف شده توسط سیستم سوق می دهد.

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

برنامه شما می تواند با تنظیم یک ویژگی در پرونده مانیفست برنامه ، از تعبیه سیستم جلوگیری یا اجازه دهد: به عنوان مثال:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <property
            android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_ALLOW_SYSTEM_OVERRIDE"
            android:value="true|false" />
    </application>
</manifest>

نام ملک در شیء JetPack Windowmanager WindowProperties تعریف شده است. اگر برنامه شما فعالیت تعبیه شده را انجام می دهد ، یا اگر می خواهید در غیر این صورت مانع از اعمال سیستم تعبیه شده در برنامه خود شوید ، مقدار آن را بر روی false تنظیم کنید. مقدار را به true تنظیم کنید تا سیستم بتواند فعالیت تعریف شده توسط سیستم را در برنامه خود اعمال کند.

محدودیت ها ، محدودیت ها و احتیاط ها

  • فقط برنامه میزبان کار ، که به عنوان صاحب فعالیت ریشه در کار شناخته می شود ، می تواند فعالیت های دیگری را در این کار سازماندهی و جاسازی کند. اگر فعالیت هایی که از تعبیه و تقسیم پشتیبانی می کنند در کار متفاوتی که متعلق به یک برنامه متفاوت است ، انجام شود ، تعبیه و تقسیم برای آن فعالیت ها کار نمی کند.
  • فعالیت ها فقط می توانند در یک کار واحد سازماندهی شوند. راه اندازی یک فعالیت در یک کار جدید ، همیشه آن را در یک پنجره جدید گسترده در خارج از هر شکاف موجود قرار می دهد.
  • فقط فعالیتهای موجود در همان فرآیند می توانند سازماندهی و تقسیم شوند. پاسخ به تماس SplitInfo فقط فعالیتهایی را که متعلق به همان فرآیند است ، گزارش می دهد ، زیرا هیچ راهی برای دانستن فعالیت در فرآیندهای مختلف وجود ندارد.
  • هر جفت یا قانون فعالیت مفرد فقط برای راه اندازی فعالیتهایی که پس از ثبت قانون اتفاق می افتد اعمال می شود. در حال حاضر هیچ راهی برای به روزرسانی شکاف های موجود یا خصوصیات بصری آنها وجود ندارد.
  • پیکربندی فیلتر Split Pair باید با اهداف مورد استفاده در هنگام راه اندازی کامل فعالیت ها مطابقت داشته باشد. تطبیق در جایی اتفاق می افتد که فعالیت جدیدی از فرآیند درخواست آغاز می شود ، بنابراین ممکن است در مورد نام های مؤلفه ای که بعداً در فرآیند سیستم هنگام استفاده از اهداف ضمنی حل می شوند ، آگاهی نداشته باشد. اگر در زمان راه اندازی یک نام جزء مشخص نباشد ، می توان از کارت وحشی به جای آن ("*/*") استفاده کرد و فیلتر می تواند بر اساس عمل قصد انجام شود.
  • در حال حاضر هیچ راهی برای جابجایی فعالیت ها بین ظروف یا درون و خارج از شکاف پس از ایجاد وجود ندارد. شکاف ها فقط توسط کتابخانه Windowmanager ایجاد می شوند که فعالیت های جدید با قوانین مطابق با آنها راه اندازی می شود و با پایان یافتن آخرین فعالیت در یک ظرف تقسیم ، شکاف ها از بین می روند.
  • فعالیت ها می توانند هنگامی که پیکربندی تغییر می کند ، مجدداً راه اندازی شود ، بنابراین وقتی شکاف ایجاد می شود یا حذف می شود و محدودیت فعالیت ها تغییر می کنند ، فعالیت می تواند از طریق تخریب کامل نمونه قبلی و ایجاد یک مورد جدید انجام شود. در نتیجه ، توسعه دهندگان برنامه باید در مورد مواردی مانند راه اندازی فعالیت های جدید از تماس های چرخه عمر ، مراقب باشند.
  • دستگاه ها برای پشتیبانی از تعبیه فعالیت باید رابط افزودنی پنجره را شامل شوند. تقریباً تمام دستگاه های صفحه نمایش بزرگ که Android 12L (سطح API 32) یا بالاتر را اجرا می کنند ، شامل رابط هستند. با این حال ، برخی از دستگاه های صفحه نمایش بزرگ که قادر به انجام چندین فعالیت نیستند ، شامل رابط افزودنی پنجره نیستند. اگر یک دستگاه صفحه نمایش بزرگ از حالت چند پنجره پشتیبانی نمی کند ، ممکن است از تعبیه فعالیت پشتیبانی نکند.

منابع اضافی

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}