یک طرح بندی صفحه پشتیبان بسازید

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

برای جزئیات بیشتر، به دستورالعمل‌های مربوط به قاب پشتی Material 3 مراجعه کنید.

یک صفحه پشتیبان را با داربست اجرا کنید

NavigableSupportingPaneScaffold یک composable است که پیاده‌سازی طرح‌بندی صفحه پشتیبانی را در Jetpack Compose ساده می‌کند. این SupportingPaneScaffold را در بر می‌گیرد و ناوبری داخلی و مدیریت بازگشتی پیش‌بینی‌کننده را اضافه می‌کند.

یک داربست شیشه‌ای پشتیبان، حداکثر از سه شیشه پشتیبانی می‌کند:

  • صفحه اصلی : محتوای اصلی را نمایش می‌دهد.
  • پنل پشتیبان : زمینه یا ابزارهای اضافی مرتبط با پنل اصلی را فراهم می‌کند.
  • صفحه اضافی (اختیاری) : در صورت نیاز برای محتوای تکمیلی استفاده می‌شود.

داربست بر اساس اندازه پنجره تنظیم می‌شود:

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

    محتوای اصلی که بیشتر صفحه نمایش را اشغال کرده و محتوای پشتیبانی در کنار آن قرار دارد.
    شکل ۱. طرح‌بندی پنجره‌ی پشتیبان.

وابستگی‌ها را اضافه کنید

NavigableSupportingPaneScaffold بخشی از کتابخانه طرح‌بندی تطبیقی ​​Material 3 است.

سه وابستگی مرتبط زیر را به فایل build.gradle برنامه یا ماژول خود اضافه کنید:

کاتلین

implementation("androidx.compose.material3.adaptive:adaptive")
implementation("androidx.compose.material3.adaptive:adaptive-layout")
implementation("androidx.compose.material3.adaptive:adaptive-navigation")

گرووی

implementation 'androidx.compose.material3.adaptive:adaptive'
implementation 'androidx.compose.material3.adaptive:adaptive-layout'
implementation 'androidx.compose.material3.adaptive:adaptive-navigation'
  • تطبیقی : بلوک‌های سازنده سطح پایین مانند HingeInfo و Posture

  • adaptive-layout : طرح‌بندی‌های تطبیقی ​​مانند ListDetailPaneScaffold و SupportingPaneScaffold

  • adaptive-navigation : کامپوننت‌هایی برای پیمایش درون و بین پنل‌ها، و همچنین طرح‌بندی‌های تطبیقی ​​که به طور پیش‌فرض از پیمایش پشتیبانی می‌کنند مانند NavigableListDetailPaneScaffold و NavigableSupportingPaneScaffold

مطمئن شوید که پروژه شما شامل compose-material3-adaptive نسخه ۱.۱.۰-beta1 یا بالاتر است.

از ژست حرکتی پیش‌بینی‌کننده‌ی بازگشت استفاده کنید

برای فعال کردن انیمیشن‌های پیش‌بینی‌کننده‌ی بازگشت در اندروید ۱۵ یا پایین‌تر، باید پشتیبانی از ژست پیش‌بینی‌کننده‌ی بازگشت را انتخاب کنید. برای این کار، android:enableOnBackInvokedCallback="true" به تگ <application> یا تگ‌های <activity> جداگانه در فایل AndroidManifest.xml خود اضافه کنید.

زمانی که برنامه شما اندروید ۱۶ (سطح API ۳۶) یا بالاتر را هدف قرار می‌دهد، پیش‌بینی بازگشت به طور پیش‌فرض فعال می‌شود.

یک ناوبر ایجاد کنید

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

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

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

این scaffold به یک ThreePaneScaffoldNavigator نیاز دارد که رابطی است که وضعیت scaffold، ThreePaneScaffoldValue و یک PaneScaffoldDirective را نشان می‌دهد.

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = { /*...*/ },
    supportingPane = { /*...*/ },
)

صفحه اصلی و صفحه پشتیبان، اجزای ترکیبی حاوی محتوای شما هستند. از AnimatedPane برای اعمال انیمیشن‌های پیش‌فرض صفحه در حین پیمایش استفاده کنید. از مقدار scaffold برای بررسی پنهان بودن صفحه پشتیبان استفاده کنید؛ در این صورت، دکمه‌ای را نمایش دهید که تابع navigateTo(SupportingPaneScaffoldRole.Supporting) را برای نمایش صفحه پشتیبان فراخوانی کند.

برای صفحات نمایش بزرگ، از متد ThreePaneScaffoldNavigator.navigateBack() برای حذف پنل پشتیبان استفاده کنید و ثابت BackNavigationBehavior.PopUntilScaffoldValueChange را به آن ارسال کنید. فراخوانی این متد، ترکیب‌بندی NavigableSupportingPaneScaffold را اجباری می‌کند. در حین ترکیب‌بندی مجدد، ویژگی ThreePaneScaffoldNavigator.currentDestination را بررسی کنید تا مشخص شود که آیا پنل پشتیبان نمایش داده شود یا خیر.

در اینجا پیاده‌سازی کاملی از scaffold آورده شده است:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()
val backNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        AnimatedPane(
            modifier = Modifier
                .safeContentPadding()
                .background(Color.Red)
        ) {
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden) {
                Button(
                    modifier = Modifier
                        .wrapContentSize(),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateTo(SupportingPaneScaffoldRole.Supporting)
                        }
                    }
                ) {
                    Text("Show supporting pane")
                }
            } else {
                Text("Supporting pane is shown")
            }
        }
    },
    supportingPane = {
        AnimatedPane(modifier = Modifier.safeContentPadding()) {
            Column {
                // Allow users to dismiss the supporting pane. Use back navigation to
                // hide an expanded supporting pane.
                if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) {
                    // Material design principles promote the usage of a right-aligned
                    // close (X) button.
                    IconButton(
                        modifier =  Modifier.align(Alignment.End).padding(16.dp),
                        onClick = {
                            scope.launch {
                                scaffoldNavigator.navigateBack(backNavigationBehavior)
                            }
                        }
                    ) {
                        Icon(Icons.Default.Close, contentDescription = "Close")
                    }
                }
                Text("Supporting pane")
            }

        }
    }
)

استخراج کامپوننت‌های پنل

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

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.MainPane(
    shouldShowSupportingPaneButton: Boolean,
    onNavigateToSupportingPane: () -> Unit,
    modifier: Modifier = Modifier,
) {
    AnimatedPane(
        modifier = modifier.safeContentPadding()
    ) {
        // Main pane content
        if (shouldShowSupportingPaneButton) {
            Button(onClick = onNavigateToSupportingPane) {
                Text("Show supporting pane")
            }
        } else {
            Text("Supporting pane is shown")
        }
    }
}

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ThreePaneScaffoldPaneScope.SupportingPane(
    scaffoldNavigator: ThreePaneScaffoldNavigator<Any>,
    modifier: Modifier = Modifier,
    backNavigationBehavior: BackNavigationBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange,
) {
    val scope = rememberCoroutineScope()
    AnimatedPane(modifier = Modifier.safeContentPadding()) {
        Column {
            // Allow users to dismiss the supporting pane. Use back navigation to
            // hide an expanded supporting pane.
            if (scaffoldNavigator.scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Expanded) {
                // Material design principles promote the usage of a right-aligned
                // close (X) button.
                IconButton(
                    modifier =  modifier.align(Alignment.End).padding(16.dp),
                    onClick = {
                        scope.launch {
                            scaffoldNavigator.navigateBack(backNavigationBehavior)
                        }
                    }
                ) {
                    Icon(Icons.Default.Close, contentDescription = "Close")
                }
            }
            Text("Supporting pane")
        }

    }
}

استخراج پنل‌ها به کامپوننت‌ها، استفاده از SupportingPaneScaffold را ساده می‌کند (موارد زیر را با پیاده‌سازی کامل scaffold در بخش قبل مقایسه کنید):

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

NavigableSupportingPaneScaffold(
    navigator = scaffoldNavigator,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) },
)

اگر به کنترل بیشتری بر جنبه‌های خاصی از scaffold نیاز دارید، استفاده SupportingPaneScaffold به جای NavigableSupportingPaneScaffold در نظر بگیرید. این روش یک PaneScaffoldDirective و ThreePaneScaffoldValue یا ThreePaneScaffoldState به طور جداگانه می‌پذیرد. این انعطاف‌پذیری به شما امکان می‌دهد منطق سفارشی برای فاصله‌گذاری بین پنجره‌ها پیاده‌سازی کنید و تعیین کنید که چند پنجره باید همزمان نمایش داده شوند. همچنین می‌توانید با اضافه کردن ThreePaneScaffoldPredictiveBackHandler پشتیبانی از predictive back را فعال کنید.

افزودن ThreePaneScaffoldPredictiveBackHandler

یک back handler پیش‌بین که یک نمونه از scaffold navigator را می‌گیرد، ضمیمه کنید و backBehavior مشخص کنید. این مشخص می‌کند که چگونه مقصدها در طول back navigation از backstack حذف می‌شوند. سپس scaffoldDirective و scaffoldState به SupportingPaneScaffold ارسال کنید. از overload ای که ThreePaneScaffoldState را می‌پذیرد، استفاده کنید و scaffoldNavigator.scaffoldState به آن ارسال کنید.

پنل‌های اصلی و پشتیبان را درون SupportingPaneScaffold تعریف کنید. AnimatedPane برای انیمیشن‌های پیش‌فرض پنل استفاده کنید.

بعد از اجرای این مراحل، کد شما باید مشابه کد زیر باشد:

val scaffoldNavigator = rememberSupportingPaneScaffoldNavigator()
val scope = rememberCoroutineScope()

ThreePaneScaffoldPredictiveBackHandler(
    navigator = scaffoldNavigator,
    backBehavior = BackNavigationBehavior.PopUntilScaffoldValueChange
)

SupportingPaneScaffold(
    directive = scaffoldNavigator.scaffoldDirective,
    scaffoldState = scaffoldNavigator.scaffoldState,
    mainPane = {
        MainPane(
            shouldShowSupportingPaneButton = scaffoldNavigator.scaffoldValue.secondary == PaneAdaptedValue.Hidden,
            onNavigateToSupportingPane = {
                scope.launch {
                    scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Secondary)
                }
            }
        )
    },
    supportingPane = { SupportingPane(scaffoldNavigator = scaffoldNavigator) },
)