لتحسين أداء إنشاء المكوّنات التفاعلية التي تستخدم
Modifier.clickable
، أطلقنا واجهات برمجة تطبيقات جديدة. تتيح واجهات برمجة التطبيقات هذه عمليات تنفيذ Indication
أكثر كفاءة، مثل التموجات.
تتضمّن الإصدارات androidx.compose.foundation:foundation:1.7.0+
وandroidx.compose.material:material-ripple:1.7.0+
تغييرات واجهة برمجة التطبيقات التالية:
تم إيقافها نهائيًا |
الاستبدال |
---|---|
|
|
|
يتم توفير واجهات برمجة التطبيقات الجديدة ملاحظة: في هذا السياق، تشير "مكتبات المواد" إلى |
|
يمكنك إجراء ذلك بإحدى طريقتين:
|
توضّح هذه الصفحة تأثير تغيير السلوك وتعليمات نقل البيانات إلى واجهات برمجة التطبيقات الجديدة.
تغيير السلوك
تتضمّن إصدارات المكتبة التالية تغييرًا في سلوك التموّج:
androidx.compose.material:material:1.7.0+
androidx.compose.material3:material3:1.3.0+
androidx.wear.compose:compose-material:1.4.0+
لم تعُد هذه الإصدارات من مكتبات Material تستخدم rememberRipple()
، بل تستخدم واجهات برمجة التطبيقات الجديدة الخاصة بتأثير التموّج. ونتيجةً لذلك، لا يبحثون عن LocalRippleTheme
.
لذلك، إذا ضبطت LocalRippleTheme
في تطبيقك، لن تستخدم مكوّنات Material هذه القيم.
توضّح الأقسام التالية كيفية نقل البيانات إلى واجهات برمجة التطبيقات الجديدة.
نقل البيانات من rememberRipple
إلى ripple
استخدام مكتبة Material
إذا كنت تستخدم مكتبة Material، استبدِل rememberRipple()
مباشرةً باستدعاء ripple()
من المكتبة المعنية. تنشئ واجهة برمجة التطبيقات هذه تأثير تموّج
باستخدام قيم مشتقة من واجهات برمجة التطبيقات الخاصة بمظهر Material. بعد ذلك، مرِّر العنصر الذي تم عرضه إلى Modifier.clickable
و/أو المكوّنات الأخرى.
على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:
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
مخصّص لتحديد إعدادات التموج، عليك بدلاً من ذلك توفير واجهة برمجة تطبيقات خاصة بالتموج تنقل البيانات إلى واجهات برمجة تطبيقات عقدة التموج المعروضة في material-ripple
. بعد ذلك، يمكن لمكوّناتك استخدام تأثير التموّج الخاص بك الذي يستهلك قيم المظهر مباشرةً. لمزيد من المعلومات، يُرجى الاطّلاع على نقل البيانات
منRippleTheme
.
النقل من RippleTheme
استخدام RippleTheme
لإيقاف تأثير التموّج لمكوّن معيّن
تعرض المكتبتان material
وmaterial3
السمتَين RippleConfiguration
وLocalRippleConfiguration
، ما يتيح لك ضبط مظهر التموجات ضمن شجرة فرعية. يُرجى العِلم أنّ RippleConfiguration
وLocalRippleConfiguration
تجريبيتان، والغرض منهما هو تخصيص كل مكوّن على حدة. لا تتوافق هذه واجهات برمجة التطبيقات مع التخصيص على مستوى التطبيق أو مستوى المظهر. راجِع استخدام RippleTheme
لتغيير جميع التموجات في تطبيق بشكل عام لمزيد من المعلومات حول حالة الاستخدام هذه.
على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:
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
هما واجهتا برمجة تطبيقات تجريبيتان ومخصّصتان فقط لتخصيص كل مكوّن على حدة.
على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:
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
لتحديد سلوك التموج على مستوى المظهر بأكمله. كانت هذه النقطة في الأساس نقطة دمج بين العناصر المحلية المخصّصة لتصميم النظام وRipple. بدلاً من عرض عنصر أساسي عام لتطبيق السمات، يعرض material-ripple
الآن الدالة createRippleModifierNode()
. تسمح هذه الدالة لمكتبات نظام التصميم بإنشاء تنفيذ wrapper
ذي ترتيب أعلى، والذي يستعلم عن قيم السمة ثم يفوّض تنفيذ التموج إلى العقدة التي تم إنشاؤها بواسطة هذه الدالة.
يتيح ذلك لأنظمة التصميم الاستعلام مباشرةً عن ما تحتاجه، وعرض أي طبقات مطلوبة لتحديد المظهر يمكن للمستخدم ضبطها في الأعلى بدون الحاجة إلى الالتزام بما يتم توفيره في الطبقة material-ripple
. يؤدي هذا التغيير أيضًا إلى توضيح المظهر أو المواصفات التي يتوافق معها التأثير المرئي، لأنّ واجهة برمجة التطبيقات الخاصة بالتأثير المرئي هي التي تحدّد هذا العقد، بدلاً من أن يتم استنتاجه ضمنيًا من المظهر.
للحصول على إرشادات، راجِع تنفيذ واجهة برمجة التطبيقات ripple في مكتبات Material، واستبدِل عمليات الاستدعاء إلى المتغيرات المحلية الخاصة بتصميم 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
تتشابه مساحة واجهة برمجة التطبيقات الخاصة بـDrawModifierNode
مع مساحة واجهة برمجة التطبيقات الخاصة بـIndicationInstance
، إذ تعرض الدالةContentDrawScope#draw()
التي تتطابق وظيفتها مع وظيفةIndicationInstance#drawContent()
. عليك تغيير هذه الدالة، ثم تنفيذ منطقcollectLatest
داخل العقدة مباشرةً بدلاً منIndication
.على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:
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
بما أنّ منطق التجميع قد تم نقله إلى العقدة، فإنّ هذا العنصر هو عنصر مصنع بسيط جدًا، ومسؤوليته الوحيدة هي إنشاء مثيل عقدة.على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:
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
.