לשיפור ביצועי הרכבה של רכיבים אינטראקטיביים שמשתמשים
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 החדשים של Repple. כתוצאה מכך, הם לא שולחים שאילתה לגבי LocalRippleTheme
.
לכן, אם מגדירים את LocalRippleTheme
באפליקציה, חומר
רכיבים לא ישתמשו בערכים האלה.
בקטע הבא מוסבר איך לחזור באופן זמני להתנהגות הישנה
מבלי לבצע העברה. עם זאת, מומלץ לעבור לממשקי ה-API החדשים. עבור
הוראות להעברה זמינות במאמר העברה מ-rememberRipple
אל ripple
ובסעיפים הבאים.
שדרוג הגרסה של ספריית Material ללא העברה
כדי לבטל את החסימה של שדרוג גרסאות של ספריות, אפשר להשתמש
API של LocalUseFallbackRippleImplementation CompositionLocal
להגדרה
רכיבי חומר כדי לחזור להתנהגות הישנה:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
חשוב להקפיד לספק את התוכן הזה מחוץ ל-MaterialTheme
כדי שההדים הישנים יוכלו
מסופקים דרך LocalIndication
.
בקטעים הבאים מתואר איך לעבור לממשקי ה-API החדשים.
העברה מrememberRipple
אל ripple
שימוש בספריית Material
אם אתם משתמשים בספריית 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 של ripple משלך המאציל לצומת של הדגל.
ממשקי API שנחשפו ב-material-ripple
. לאחר מכן, הרכיבים יכולים להשתמש בדגל משלכם
שצורכת את ערכי העיצוב באופן ישיר. מידע נוסף זמין במאמר העברה
החל מ-RippleTheme
.
העברה מ-RippleTheme
ביטול זמני של שינוי בהתנהגות
לספריות של חומרים יש ערך זמני מסוג CompositionLocal
,
LocalUseFallbackRippleImplementation
, ואפשר להשתמש בו כדי להגדיר
רכיבי החומר שצריך לחזור להשתמש בהם באמצעות 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) } }
למידע נוסף, ראו שדרוג הספרייה 'חומרי לימוד' ללא העברה.
שימוש ב-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
. השינוי הזה גורם גם
מפורשות לאיזה נושא/מפרט הגל מתאים, כי זה
את ripple API עצמו שמגדיר את החוזה הזה, במקום להיות מרומז
שנגזרת מהנושא.
לקבלת הנחיות, אפשר לעיין במאמר הטמעה של Ripple API ב-Material של ספריות, ולהחליף את הקריאות למקומיים מסוג Material Processing לפי הצורך מערכת עיצוב משלכם.
העברה מ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()
אתם צריכים לשנות את הפונקציה הזו, ואז להטמיע את הלוגיקהcollectLatest
ישירות בצומת, במקוםIndication
.לדוגמה: קטע הקוד הבא משתמש בממשקי ה-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
,
במקרה של הטמעה קלה יותר. לדוגמה, Modifier.indication
להעניק גישה באופן פנימי לצומת שנוצר, אם הוא IndicationNodeFactory
. אם המיקום
לא, הוא ישתמש ב-Modifier.composed
כדי לקרוא ל-rememberUpdatedInstance
.