سفارشی‌سازی انتقال عنصر هم‌رسانی‌شده

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

مشخصات پویانمایی

برای تغییر دادن مشخصات پویانمایی مورداستفاده برای حرکت اندازه و موقعیت، می‌توانید پارامتر boundsTransform متفاوتی را در Modifier.sharedElement() مشخص کنید. این کار موقعیت اولیه Rect و موقعیت هدف Rect را ارائه می‌دهد.

برای مثال، برای اینکه نوشتار در مثال قبلی با حرکت قوسی حرکت کند، پارامتر boundsTransform را برای استفاده از مشخصات keyframes مشخص کنید:

val textBoundsTransform = BoundsTransform { initialBounds, targetBounds ->
    keyframes {
        durationMillis = boundsAnimationDurationMillis
        initialBounds at 0 using ArcMode.ArcBelow using FastOutSlowInEasing
        targetBounds at boundsAnimationDurationMillis
    }
}
Text(
    "Cupcake", fontSize = 28.sp,
    modifier = Modifier.sharedBounds(
        rememberSharedContentState(key = "title"),
        animatedVisibilityScope = animatedVisibilityScope,
        boundsTransform = textBoundsTransform
    )
)

می‌توانید از هر AnimationSpec استفاده کنید. این نمونه از مشخصات keyframes استفاده می‌کند.

شکل ۱. نمونه‌ای که پارامترهای boundsTransform مختلف را نشان می‌دهد

حالت تغییر اندازه

هنگام پویانمایی بین دو مرز مشترک، می‌توانید پارامتر resizeMode را روی SharedTransitionScope.ResizeMode.RemeasureToBounds یا SharedTransitionScope.ResizeMode.scaleToBounds() تنظیم کنید. این پارامتر تعیین می‌کند که عنصر مشترک چگونه بین دو حالت گذار می‌کند. scaleToBounds() ابتدا چیدمان فرزند را با محدودیت‌های پیش‌نگری (یا هدف) اندازه‌گیری می‌کند. سپس، چیدمان پایدار فرزند مقیاس‌بندی می‌شود تا در مرزهای مشترک قرار گیرد. scaleToBounds() را می‌توان به‌عنوان «مقیاس گرافیکی» بین حالت‌ها درنظر گرفت.

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

برای Text ترکیب‌پذیر، scaleToBounds() توصیه می‌شود، زیرا از بازآرایی و بازجاری شدن نوشتار در خطوط مختلف جلوگیری می‌کند. RemeasureToBounds برای مرزهایی که نسبت ابعادی متفاوتی دارند و اگر می‌خواهید تداوم روانی بین دو عنصر مشترک وجود داشته باشد توصیه می‌شود.

تفاوت بین دو حالت تغییر اندازه را می‌توان در مثال‌های زیر مشاهده کرد:

scaleToBounds()

RemeasureToBounds

فعال و غیرفعال کردن عناصر هم‌رسانی‌شده به‌صورت پویا

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

برای کنترل اینکه آیا انتقال عنصر مشترک رخ می‌دهد یا نه، می‌توانید SharedContentConfig ارسال‌شده به rememberSharedContentState() را سفارشی‌سازی کنید. خصوصیت isEnabled تعیین می‌کند که آیا عنصر هم‌رسانی‌شده فعال است یا نه.

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

SharedTransitionLayout {
    val transition = updateTransition(currentState)
    transition.AnimatedContent { targetState ->
        // Create the configuration that depends on state changing.
        fun animationConfig() : SharedTransitionScope.SharedContentConfig {
            return object : SharedTransitionScope.SharedContentConfig {
                override val SharedTransitionScope.SharedContentState.isEnabled: Boolean
                    // For this example, we only enable the transition in one direction
                    // from A -> B and not the other way around.
                    get() =
                        transition.currentState == "A" && transition.targetState == "B"
            }
        }
        when (targetState) {
            "A" -> Box(
                modifier = Modifier
                    .sharedElement(
                        rememberSharedContentState(
                            key = "shared_box",
                            config = animationConfig()
                        ),
                        animatedVisibilityScope = this
                    )
                    // ...
            ) {
                // Your content
            }
            "B" -> {
                Box(
                    modifier = Modifier
                        .sharedElement(
                            rememberSharedContentState(
                                key = "shared_box",
                                config = animationConfig()
                            ),
                            animatedVisibilityScope = this
                        )
                        // ...
                ) {
                    // Your content
                }
            }
        }
    }
}

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

رفتن به چیدمان نهایی

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

مثال زیر نشان می‌دهد که نوشتار توضیحی «Lorem Ipsum» به دو روش مختلف وارد صفحه می‌شود. در مثال اول، نوشتار با ورود به ظرف و افزایش اندازه آن، بازآرایی می‌شود. در مثال دوم، نوشتار با بزرگ شدن دوباره جاری نمی‌شود. افزودن Modifier.skipToLookaheadSize() از بازآرایی با رشد آن جلوگیری می‌کند.

بدون Modifier.skipToLookaheadSize() - به جریان مجدد نوشتار «Lorem Ipsum» توجه کنید

Modifier.skipToLookaheadSize() - توجه کنید که نوشتار «Lorem Ipsum» حالت نهایی خود را در شروع پویانمایی حفظ می‌کند

کلیپ و رونهادها

برای اینکه عناصر هم‌رسانی‌شده بین ترکیب‌پذیرهای مختلف هم‌رسانی شوند، رندر کردن ترکیب‌پذیر به یک لایه هم‌پوشانی ارتقا می‌یابد وقتی که انتقال به مطابقت آن در مقصد شروع می‌شود. تأثیر این کار این است که از مرزهای والد و تبدیل‌های لایه آن (برای مثال، آلفا و مقیاس) فرار می‌کند.

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

برای برش دادن عنصر هم‌رسانی‌شده به یک شکل، از تابع استاندارد Modifier.clip() استفاده کنید. آن را بعداز sharedElement() قرار دهید:

Image(
    painter = painterResource(id = R.drawable.cupcake),
    contentDescription = "Cupcake",
    modifier = Modifier
        .size(100.dp)
        .sharedElement(
            rememberSharedContentState(key = "image"),
            animatedVisibilityScope = this@AnimatedContent
        )
        .clip(RoundedCornerShape(16.dp)),
    contentScale = ContentScale.Crop
)

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

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

برای مثال، در Jetsnack، BottomAppBar باید در بالای عنصر مشترک قرار داده شود تا زمانی که صفحه قابل‌مشاهده نباشد. افزودن اصلاح‌کننده به composable آن را در حالت برافراشته نگه می‌دارد.

بدون Modifier.renderInSharedTransitionScopeOverlay()

ازطریق Modifier.renderInSharedTransitionScopeOverlay()

شاید بخواهید ترکیب‌پذیر غیرهم‌رسانی‌شده‌تان پویانمایی شود و همچنین قبل‌از انتقال، روی ترکیب‌پذیرهای دیگر باقی بماند. در چنین مواردی، از renderInSharedTransitionScopeOverlay().animateEnterExit() برای پویانمایی composable out هنگام اجرای انتقال عنصر مشترک استفاده کنید:

JetsnackBottomBar(
    modifier = Modifier
        .renderInSharedTransitionScopeOverlay(
            zIndexInOverlay = 1f,
        )
        .animateEnterExit(
            enter = fadeIn() + slideInVertically {
                it
            },
            exit = fadeOut() + slideOutVertically {
                it
            }
        )
)

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

در موارد نادری که می‌خواهید عنصر هم‌رسانی‌شده شما در یک هم‌پوشانی ارائه نشود، می‌توانید renderInOverlayDuringTransition را در sharedElement() روی false تنظیم کنید.

اطلاع‌رسانی به چیدمان‌های هم‌نیا درباره تغییرات اندازه عنصر هم‌رسانی‌شده

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

برای انتشار تغییرات اندازه به محفظه والد درحین انتقال، پارامتر placeholderSize را به PlaceholderSize.AnimatedSize تغییر دهید. با این کار، اندازه مورد بزرگ‌تر یا کوچک‌تر می‌شود. همه موارد دیگر در چیدمان به این تغییر واکنش نشان می‌دهند.

‫PlaceholderSize.ContentSize (پیش‌فرض)

PlaceholderSize.AnimatedSize

(توجه کنید که چگونه موارد دیگر در فهرست در پاسخ به رشد یک مورد به پایین حرکت می‌کنند)