برای بهبود عملکرد ترکیب اجزای تعاملی که از Modifier.clickable استفاده می کنند، API های جدیدی را معرفی کرده ایم. این APIها امکان پیاده سازی Indication کارآمدتر مانند امواج را فراهم می کنند.
androidx.compose.foundation:foundation:1.7.0+ و androidx.compose.material:material-ripple:1.7.0+ شامل تغییرات API زیر هستند:
منسوخ شده است | جایگزینی |
|---|---|
| |
| API های جدید توجه: در این زمینه، "کتابخانه های مواد" به |
| یا:
|
این صفحه تأثیر تغییر رفتار و دستورالعملهای مهاجرت به APIهای جدید را شرح میدهد.
تغییر رفتار
نسخههای کتابخانه زیر شامل تغییر رفتار ریپل است:
-
androidx.compose.material:material:1.7.0+ -
androidx.compose.material3:material3:1.3.0+ -
androidx.wear.compose:compose-material:1.4.0+
این نسخههای کتابخانههای Material دیگر از rememberRipple() استفاده نمیکنند. در عوض، آنها از APIهای جدید ریپل استفاده می کنند. در نتیجه، LocalRippleTheme پرس و جو نمی کنند. بنابراین، اگر LocalRippleTheme در برنامه خود تنظیم کنید، اجزای Material از این مقادیر استفاده نخواهند کرد .
بخش های زیر نحوه مهاجرت به API های جدید را شرح می دهد.
از rememberRipple به ripple مهاجرت کنید
استفاده از کتابخانه مواد
اگر از یک کتابخانه Material استفاده می کنید، مستقیماً rememberRipple() با یک فراخوانی به ripple() از کتابخانه مربوطه جایگزین کنید. این API با استفاده از مقادیر مشتق شده از APIهای تم Material یک موج ایجاد می کند. سپس، شیء برگشتی را به Modifier.clickable و/یا اجزای دیگر ارسال کنید.
به عنوان مثال، قطعه زیر از API های منسوخ شده استفاده می کند:
Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = rememberRipple() ) ) { // ... }
شما باید قطعه بالا را به این صورت تغییر دهید:
@Composable private fun RippleExample() { Box( Modifier.clickable( onClick = {}, interactionSource = remember { MutableInteractionSource() }, indication = ripple() ) ) { // ... } }
توجه داشته باشید که ripple() دیگر یک تابع قابل ترکیب نیست و نیازی به به خاطر سپردن ندارد. همچنین میتوان از آن در چندین مؤلفه، مشابه اصلاحکنندهها، استفاده مجدد کرد، بنابراین برای ذخیره تخصیصها، ایجاد ریپل را به یک مقدار سطح بالا استخراج کنید.
پیاده سازی سیستم طراحی سفارشی
اگر سیستم طراحی خود را پیادهسازی میکنید، و قبلاً از rememberRipple() به همراه یک RippleTheme سفارشی برای پیکربندی ریپل استفاده میکردید، در عوض باید API ریپل خود را ارائه کنید که APIهای گره ریپل را که در material-ripple در معرض دید قرار میگیرند ارائه دهید. سپس، اجزای شما می توانند از ریپل خود استفاده کنند که مقادیر تم شما را مستقیما مصرف می کند. برای اطلاعات بیشتر، مهاجرت از RippleTheme را ببینید.
از RippleTheme مهاجرت کنید
استفاده از RippleTheme برای غیرفعال کردن ریپل برای یک جزء معین
کتابخانه های material و material3 ، RippleConfiguration و LocalRippleConfiguration را نشان می دهند که به شما امکان می دهد ظاهر امواج درون یک زیردرخت را پیکربندی کنید. توجه داشته باشید که RippleConfiguration و LocalRippleConfiguration آزمایشی هستند و فقط برای سفارشی سازی هر جزء در نظر گرفته شده اند. سفارشیسازی سراسری/تمام با این APIها پشتیبانی نمیشود. برای اطلاعات بیشتر در مورد آن مورد استفاده ، استفاده از RippleTheme برای تغییر سراسری همه امواج در یک برنامه را ببینید.
به عنوان مثال، قطعه زیر از API های منسوخ شده استفاده می کند:
private object DisabledRippleTheme : RippleTheme { @Composable override fun defaultColor(): Color = Color.Transparent @Composable override fun rippleAlpha(): RippleAlpha = RippleAlpha(0f, 0f, 0f, 0f) } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleTheme) { Button { // ... } }
شما باید قطعه بالا را به این صورت تغییر دهید:
CompositionLocalProvider(LocalRippleConfiguration provides null) { Button { // ... } }
استفاده از RippleTheme برای تغییر رنگ/آلفای یک ریپل برای یک جزء معین
همانطور که در بخش قبل توضیح داده شد، RippleConfiguration و LocalRippleConfiguration API های آزمایشی هستند و فقط برای سفارشی سازی هر جزء در نظر گرفته شده اند.
به عنوان مثال، قطعه زیر از API های منسوخ شده استفاده می کند:
private object DisabledRippleThemeColorAndAlpha : RippleTheme { @Composable override fun defaultColor(): Color = Color.Red @Composable override fun rippleAlpha(): RippleAlpha = MyRippleAlpha } // ... CompositionLocalProvider(LocalRippleTheme provides DisabledRippleThemeColorAndAlpha) { Button { // ... } }
شما باید قطعه بالا را به این صورت تغییر دهید:
@OptIn(ExperimentalMaterialApi::class) private val MyRippleConfiguration = RippleConfiguration(color = Color.Red, rippleAlpha = MyRippleAlpha) // ... CompositionLocalProvider(LocalRippleConfiguration provides MyRippleConfiguration) { Button { // ... } }
استفاده از RippleTheme برای تغییر سراسری تمام امواج در یک برنامه
قبلاً، میتوانید از LocalRippleTheme برای تعریف رفتار ریپل در سطح تم استفاده کنید. این اساساً یک نقطه ادغام بین افراد محلی ترکیب سیستم طراحی سفارشی و ریپل بود. بهجای افشای یک موضوع اولیه، material-ripple اکنون یک تابع createRippleModifierNode() نمایش میدهد. این تابع به کتابخانههای سیستم طراحی اجازه میدهد تا پیادهسازی wrapper مرتبه بالاتر را ایجاد کنند، که مقادیر موضوع خود را پرس و جو کرده و سپس پیادهسازی ریپل را به گره ایجاد شده توسط این تابع واگذار کند.
این به سیستمهای طراحی اجازه میدهد تا مستقیماً آنچه را که نیاز دارند پرس و جو کنند و لایههای موضوعی قابل تنظیم توسط کاربر مورد نیاز را بدون نیاز به مطابقت با آنچه در لایه material-ripple ارائه شده است، در معرض دید قرار دهند. این تغییر همچنین واضحتر میکند که ریپل با چه تم/مشخصاتی مطابقت دارد، زیرا این خود API ریپل است که آن قرارداد را تعریف میکند، نه اینکه به طور ضمنی از موضوع مشتق شود.
برای راهنمایی، اجرای Ripple API را در کتابخانههای Material ببینید، و در صورت نیاز برای سیستم طراحی خود، تماسهای محلی ترکیب مواد را جایگزین کنید.
از Indication به IndicationNodeFactory مهاجرت کنید
عبور از اطراف Indication
اگر فقط یک Indication برای عبور ایجاد می کنید، مانند ایجاد یک موج برای ارسال به Modifier.clickable یا Modifier.indication ، نیازی به ایجاد هیچ تغییری ندارید. IndicationNodeFactory از Indication به ارث می رسد، بنابراین همه چیز به کامپایل و کار ادامه خواهد داد.
ایجاد Indication
اگر پیادهسازی Indication خود را ایجاد میکنید، مهاجرت در بیشتر موارد باید ساده باشد. به عنوان مثال، Indication در نظر بگیرید که یک اثر مقیاس را روی پرس اعمال می کند:
object ScaleIndication : Indication { @Composable override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance { // key the remember against interactionSource, so if it changes we create a new instance val instance = remember(interactionSource) { ScaleIndicationInstance() } LaunchedEffect(interactionSource) { interactionSource.interactions.collectLatest { interaction -> when (interaction) { is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition) is PressInteraction.Release -> instance.animateToResting() is PressInteraction.Cancel -> instance.animateToResting() } } } return instance } } private class ScaleIndicationInstance : IndicationInstance { var currentPressPosition: Offset = Offset.Zero val animatedScalePercent = Animatable(1f) suspend fun animateToPressed(pressPosition: Offset) { currentPressPosition = pressPosition animatedScalePercent.animateTo(0.9f, spring()) } suspend fun animateToResting() { animatedScalePercent.animateTo(1f, spring()) } override fun ContentDrawScope.drawIndication() { scale( scale = animatedScalePercent.value, pivot = currentPressPosition ) { this@drawIndication.drawContent() } } }
شما می توانید این را در دو مرحله انتقال دهید:
ScaleIndicationInstanceانتقال دهید تاDrawModifierNodeباشد. سطح API برایDrawModifierNodeبسیار شبیه بهIndicationInstanceاست: تابعContentDrawScope#draw()را نشان می دهد که از نظر عملکردی معادلIndicationInstance#drawContent()است. شما باید آن تابع را تغییر دهید، و سپس به جایIndication، منطقcollectLatestمستقیماً در داخل گره پیاده سازی کنید.به عنوان مثال، قطعه زیر از API های منسوخ شده استفاده می کند:
private class ScaleIndicationInstance : IndicationInstance { var currentPressPosition: Offset = Offset.Zero val animatedScalePercent = Animatable(1f) suspend fun animateToPressed(pressPosition: Offset) { currentPressPosition = pressPosition animatedScalePercent.animateTo(0.9f, spring()) } suspend fun animateToResting() { animatedScalePercent.animateTo(1f, spring()) } override fun ContentDrawScope.drawIndication() { scale( scale = animatedScalePercent.value, pivot = currentPressPosition ) { this@drawIndication.drawContent() } } }
شما باید قطعه بالا را به این صورت تغییر دهید:
private class ScaleIndicationNode( private val interactionSource: InteractionSource ) : Modifier.Node(), DrawModifierNode { var currentPressPosition: Offset = Offset.Zero val animatedScalePercent = Animatable(1f) private suspend fun animateToPressed(pressPosition: Offset) { currentPressPosition = pressPosition animatedScalePercent.animateTo(0.9f, spring()) } private suspend fun animateToResting() { animatedScalePercent.animateTo(1f, spring()) } override fun onAttach() { coroutineScope.launch { interactionSource.interactions.collectLatest { interaction -> when (interaction) { is PressInteraction.Press -> animateToPressed(interaction.pressPosition) is PressInteraction.Release -> animateToResting() is PressInteraction.Cancel -> animateToResting() } } } } override fun ContentDrawScope.draw() { scale( scale = animatedScalePercent.value, pivot = currentPressPosition ) { this@draw.drawContent() } } }
ScaleIndicationبرای پیاده سازیIndicationNodeFactoryمنتقل کنید. از آنجا که منطق مجموعه اکنون به گره منتقل شده است، این یک شی کارخانه بسیار ساده است که تنها مسئولیت آن ایجاد یک نمونه گره است.به عنوان مثال، قطعه زیر از API های منسوخ شده استفاده می کند:
object ScaleIndication : Indication { @Composable override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance { // key the remember against interactionSource, so if it changes we create a new instance val instance = remember(interactionSource) { ScaleIndicationInstance() } LaunchedEffect(interactionSource) { interactionSource.interactions.collectLatest { interaction -> when (interaction) { is PressInteraction.Press -> instance.animateToPressed(interaction.pressPosition) is PressInteraction.Release -> instance.animateToResting() is PressInteraction.Cancel -> instance.animateToResting() } } } return instance } }
شما باید قطعه بالا را به این صورت تغییر دهید:
object ScaleIndicationNodeFactory : IndicationNodeFactory { override fun create(interactionSource: InteractionSource): DelegatableNode { return ScaleIndicationNode(interactionSource) } override fun hashCode(): Int = -1 override fun equals(other: Any?) = other === this }
استفاده از Indication برای ایجاد IndicationInstance
در بیشتر موارد، باید از Modifier.indication برای نمایش Indication برای یک جزء استفاده کنید. با این حال، در موارد نادری که به صورت دستی یک IndicationInstance با استفاده از rememberUpdatedInstance ایجاد میکنید، باید پیادهسازی خود را بهروزرسانی کنید تا بررسی کنید آیا Indication یک IndicationNodeFactory است یا خیر تا بتوانید از پیادهسازی سبکتر استفاده کنید. به عنوان مثال، اگر یک IndicationNodeFactory باشد، Modifier.indication به صورت داخلی به گره ایجاد شده واگذار می شود. اگر نه، از Modifier.composed برای فراخوانی rememberUpdatedInstance استفاده می کند.