لتحسين أداء تركيبة المكونات التفاعلية التي تستخدم
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
في تطبيقك، لن تستخدم مكونات المواد هذه القيم.
يوضِّح القسم التالي كيفية العودة مؤقتًا إلى السلوك القديم بدون نقل البيانات، إلا أنّنا ننصح بالانتقال إلى واجهات برمجة التطبيقات الجديدة. للحصول على تعليمات حول نقل البيانات، يمكنك الاطّلاع على نقل البيانات من rememberRipple
إلى ripple
والأقسام اللاحقة.
ترقية إصدار مكتبة Material بدون نقل البيانات
لإزالة حظر ترقية إصدارات المكتبات، يمكنك استخدام واجهة برمجة التطبيقات LocalUseFallbackRippleImplementation CompositionLocal
المؤقتة لضبط مكوّنات Material من أجل العودة إلى السلوك القديم:
CompositionLocalProvider(LocalUseFallbackRippleImplementation provides true) { MaterialTheme { App() } }
احرص على توفير هذه الخوارزمية خارج MaterialTheme
حتى يمكن توفير الأمواج القديمة من خلال LocalIndication
.
توضِّح الأقسام التالية كيفية نقل البيانات إلى واجهات برمجة التطبيقات الجديدة.
نقل البيانات من rememberRipple
إلى ripple
استخدام مكتبة Material
إذا كنت تستخدم مكتبة Material، استبدِل rememberRipple()
مباشرةً باستدعاء ripple()
من المكتبة المقابلة. تنشئ واجهة برمجة التطبيقات هذه تمويجًا باستخدام القيم
المستمدة من واجهات برمجة تطبيقات Materials. بعد ذلك، مرِّر الكائن الذي تم إرجاعه إلى 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
"
إيقاف خيار تغيير السلوك مؤقتًا
وتشتمل مكتبات Material على سمة 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) } }
للحصول على مزيد من المعلومات، يمكنك الاطّلاع على القسم ترقية إصدار مكتبة Material بدون نقل البيانات.
استخدام 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 { // ... } }
عليك تعديل المقتطف أعلاه لإجراء ما يلي:
@OptIn(ExperimentalMaterialApi::class) private val DisabledRippleConfiguration = RippleConfiguration(isEnabled = false) // ... CompositionLocalProvider(LocalRippleConfiguration provides DisabledRippleConfiguration) { 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 في "مكتبات 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
.