النقل إلى واجهات برمجة تطبيقات المؤشرات والتموج

لتحسين أداء تركيبة المكونات التفاعلية التي تستخدم Modifier.clickable، تم تقديم واجهات برمجة تطبيقات جديدة. تتيح واجهات برمجة التطبيقات هذه المزيد عمليات تنفيذ Indication فعالة، مثل التموجات.

androidx.compose.foundation:foundation:1.7.0+ و تتضمن androidx.compose.material:material-ripple:1.7.0+ واجهة برمجة التطبيقات التالية التغييرات:

تمّت إزالة هذا العمود

الاستبدال

Indication#rememberUpdatedInstance

IndicationNodeFactory

rememberRipple()

تم توفير واجهات برمجة تطبيقات ripple() جديدة في مكتبات Material بدلاً من ذلك.

ملاحظة: في هذا السياق، "مكتبات Material" يشير إلى androidx.compose.material:material وandroidx.compose.material3:material3 وandroidx.wear.compose:compose-material وandroidx.wear.compose:compose-material3..

RippleTheme

يمكنك إجراء ذلك بإحدى طريقتين:

  • استخدام واجهات برمجة تطبيقات RippleConfiguration في مكتبة المواد
  • بناء تطبيق تمويج نظام التصميم الخاص بك

تصف هذه الصفحة تأثير تغيير السلوك وتعليمات الانتقال إلى لواجهات برمجة التطبيقات الجديدة.

تغيير السلوك

تشتمل إصدارات المكتبة التالية على تغيير في سلوك التموج:

  • androidx.compose.material:material:1.7.0+
  • androidx.compose.material3:material3:1.3.0+
  • androidx.wear.compose:compose-material:1.4.0+

لم تعُد هذه الإصدارات من مكتبات المواد تستخدم السمة rememberRipple(). بدلاً من ذلك، فإنهم يستخدمون واجهات برمجة تطبيقات التموّج الجديدة. ونتيجةً لذلك، لا يتم الاستعلام عن LocalRippleTheme. لذلك، إذا ضبطت LocalRippleTheme في تطبيقك، سيتم التعامل مع Material لن تستخدم هذه القيم.

يصف القسم التالي كيفية الرجوع مؤقتًا إلى السلوك القديم. بدون نقل البيانات إلا أننا ننصح بالنقل إلى واجهات برمجة التطبيقات الجديدة. بالنسبة تعليمات النقل، يُرجى الاطّلاع على نقل البيانات من rememberRipple إلى ripple. والأقسام اللاحقة.

ترقية إصدار "مكتبة المواد" بدون نقل البيانات

لإزالة حظر ترقية إصدارات المكتبة، يمكنك استخدام واجهة برمجة تطبيقات LocalUseFallbackRippleImplementation CompositionLocal المطلوب إعدادها مكونات Material التي يتم استخدامها للعودة إلى السلوك القديم:

CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) {
    MaterialTheme {
        App()
    }
}

احرص على ترك هذا خارج MaterialTheme حتى تتمكن الأمواج القديمة عبر LocalIndication.

توضّح الأقسام التالية كيفية الانتقال إلى واجهات برمجة التطبيقات الجديدة.

نقل البيانات من 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

إيقاف تغيير السلوك مؤقتًا

تضم مكتبات المواد سمة CompositionLocal مؤقتة، LocalUseFallbackRippleImplementation، الذي يمكنك استخدامه لإعداد جميع مكونات Material التي يتم استخدامها مرة أخرى باستخدام rememberRipple. بهذه الطريقة، يستمر rememberRipple في طلب البحث عن LocalRippleTheme.

يوضح مقتطف الرمز التالي كيفية استخدام واجهة برمجة تطبيقات LocalUseFallbackRippleImplementation CompositionLocal:

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 تجريبية، وهي مخصّصة فقط لكل مكوّن. التخصيص. لا تتوافق التخصيصات على مستوى العالم أو المظهر مع هذه واجهات برمجة التطبيقات راجع استخدام RippleTheme لتغيير كل التموجات بشكل عام app للحصول على مزيد من المعلومات حول حالة الاستخدام هذه.

على سبيل المثال، يستخدم المقتطف التالي واجهات برمجة التطبيقات المتوقّفة نهائيًا:

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 لتحديد سلوك التموج على مستوى الموضوع. وقد كانت هذه نقطة تكامل بين التخصيص تكوين نظام التصميم المحلي والتموّج. وبدلاً من عرض وصف عام موضوع أساسي، تعرض material-ripple الآن createRippleModifierNode() الأخرى. تسمح هذه الدالة لمكتبات نظام التصميم بإنشاء تنفيذ wrapper، التي تستعلم عن قيم الموضوع ثم تفويض تطبيق الموجات على العقدة التي تم إنشاؤها بواسطة هذه الدالة.

يسمح هذا لأنظمة التصميم بالاستعلام المباشر عما يحتاجون إليه، وكشف أي طبقات موضوعة قابلة للتهيئة بواسطة المستخدم في الأعلى دون الحاجة إلى التوافق مع ما يتم تقديمه في طبقة material-ripple. هذا التغيير أيضًا يجعل وتوضيح الموضوع/المواصفات التي تتوافق معها التموج، حيث واجهة برمجة التطبيقات Ripple API نفسها التي تعرّف هذا العقد، بدلاً من أن تكون ضمنيًا مستمدة من الموضوع.

للحصول على إرشادات، يمكنك الاطّلاع على تنفيذ واجهة برمجة التطبيقات ripple في 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()
        }
    }
}

يمكنك نقل بيانات هذا المستودع من خلال خطوتَين:

  1. نقل بيانات "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()
            }
        }
    }

  2. نقل 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.