برای بهبود عملکرد ترکیب اجزای تعاملی که از 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 را بدون مهاجرت ارتقا دهید
برای رفع انسداد نسخههای کتابخانه در حال ارتقا، میتوانید از LocalUseFallbackRippleImplementation CompositionLocal
API موقت برای پیکربندی اجزای Material برای بازگشت به رفتار قبلی استفاده کنید:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
مطمئن شوید که این را خارج از MaterialTheme
ارائه میکنید تا ریپلهای قدیمی از طریق LocalIndication
ارائه شوند.
بخش های زیر نحوه مهاجرت به 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
مهاجرت کنید
انصراف موقت از تغییر رفتار
کتابخانه های متریال دارای یک CompositionLocal
به نام LocalUseFallbackRippleImplementation
موقت هستند که می توانید از آن برای پیکربندی همه اجزای Material استفاده کنید تا به استفاده از rememberRipple
برگردید. به این ترتیب، rememberRipple
به پرس و جوی LocalRippleTheme
ادامه می دهد.
قطعه کد زیر نحوه استفاده از LocalUseFallbackRippleImplementation CompositionLocal
API را نشان می دهد:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
اگر از یک طرح زمینه برنامه سفارشی استفاده میکنید که بر روی Material ساخته شده است، میتوانید با خیال راحت ترکیب محلی را به عنوان بخشی از طرح زمینه برنامه خود ارائه دهید:
@OptIn(ExperimentalMaterialApi::class) @Composable fun MyAppTheme(content: @Composable () -> Unit) { CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme(content = content) } }
برای اطلاعات بیشتر، به بخش Upgrade Material Library بدون مهاجرت مراجعه کنید.
استفاده از 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
استفاده می کند.