Compose, yaygın davranışlar için kullanıma hazır birçok değiştirici sunar. ancak kendi özel değiştiricilerinizi de oluşturabilirsiniz.
Değiştiricilerin birden çok bölümü vardır:
- Değiştirici fabrika
- Bu,
Modifier
üzerinde deyimsel API sağlayan bir uzantı işlevidir. değiştiricilerinizin kolayca birbirine bağlanmasını sağlar. İlgili içeriği oluşturmak için kullanılan değiştiricisi fabrika, Compose tarafından değişiklik yapmak için kullanılan değiştirici öğeleri üretir emin olun.
- Bu,
- Değiştirici öğe
- Değiştiricinizin davranışını burada uygulayabilirsiniz.
Özel değiştiriciyi uygulamanın, kullanım şeklinize göre
emin olmanız gerekir. Özel değiştiriciyi uygulamanın en kolay yolu genellikle
önceden tanımlanmış diğer öğeleri birleştiren bir özel değiştirici fabrika uygulaması
birlikte kullanır. Daha fazla özel davranışa ihtiyacınız varsa
daha düşük düzeyde ancak Modifier.Node
API'lerini kullanan bir değiştirici öğe
daha fazla esneklik sağlayabilir.
Mevcut değiştiricileri birbirine bağlayın
Özel değiştiricileri oluşturmak çoğu zaman yalnızca mevcut
kullanabilirsiniz. Örneğin Modifier.clip()
,
graphicsLayer
değiştiricisi. Bu stratejide mevcut değiştirici öğeler kullanılır ve
kendi özel değiştirici fabrikanızı sağlayın.
Kendi özel değiştiricinizi uygulamadan önce, aynı düzenleyiciyi kullanıp kullanamayacağınıza bakın üzerine konuşacağız.
fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)
Aynı değiştirici grubu sık sık tekrar ediyorsanız bunları kendi değiştiricinizde toplayın:
fun Modifier.myBackground(color: Color) = padding(16.dp) .clip(RoundedCornerShape(8.dp)) .background(color)
Fabrika ayarı kullanarak özel değiştirici oluşturun
Değerleri iletmek için composable işlev kullanarak özel bir değiştirici de oluşturabilirsiniz. mevcut bir değiştiriciye dönüştürülebilir. Bu, composable değiştirici fabrikası olarak bilinir.
Değiştirici oluşturmak için composable değiştirici fabrika kullanmak,
animate*AsState
ve diğer Compose'lar gibi daha üst düzey yazma API'leri
durum destekli animasyon API'leri hakkında daha fazla bilgi edinin. Örneğin, aşağıdaki snippet'te bir
etkinleştirildiğinde/devre dışı bırakıldığında alfa değişikliği canlandıran değiştiricisi:
@Composable fun Modifier.fade(enable: Boolean): Modifier { val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f) return this then Modifier.graphicsLayer { this.alpha = alpha } }
Özel değiştiriciniz, bir
CompositionLocal
, bunu uygulamanın en kolay yolu bir composable
değiştirici fabrikası:
@Composable fun Modifier.fadedBackground(): Modifier { val color = LocalContentColor.current return this then Modifier.background(color.copy(alpha = 0.5f)) }
Bu yaklaşımda, aşağıda ayrıntıları verilen bazı uyarılar bulunmaktadır.
CompositionLocal
değerleri, değiştirici fabrikanın çağrı sitesinde çözümlenir
Besteci değiştirici fabrikası kullanarak özel değiştirici oluştururken kompozisyonun içeriği yerel kullanıcılar değeri, oluşturuldukları kompozisyon ağacından alır, kullanılır. Bu durum beklenmedik sonuçlara yol açabilir. Örneğin, bu bölümün yukarıdaki yerel değiştirici örneği, composable işlev:
@Composable fun Modifier.myBackground(): Modifier { val color = LocalContentColor.current return this then Modifier.background(color.copy(alpha = 0.5f)) } @Composable fun MyScreen() { CompositionLocalProvider(LocalContentColor provides Color.Green) { // Background modifier created with green background val backgroundModifier = Modifier.myBackground() // LocalContentColor updated to red CompositionLocalProvider(LocalContentColor provides Color.Red) { // Box will have green background, not red as expected. Box(modifier = backgroundModifier) } } }
Değiştiricinizin bu şekilde çalışmasını beklemiyorsanız özel bir
Kompozisyonun yerel halkı olacağı için bunun yerine Modifier.Node
yerinde doğru şekilde çözülür ve güvenli bir şekilde kaldırılabilir.
Özelleştirilebilir işlev değiştiricileri hiçbir zaman atlanmaz
Birleştirilebilir fabrika değiştiricileri, composable işlevler nedeniyle hiçbir zaman atlanmaz döndürülen URL'ler atlanamaz. Bu, değiştirici fonksiyonunuzun her yeniden bestede çağrılacaktır; bu da yeniden oluşturulursa pahalı olabilir alabilir.
Özelleştirilebilir işlev değiştiricileri, composable işlev içinde çağrılmalıdır
Tüm composable işlevler gibi bir composable fabrika değiştiricisi de müzakere tekniği de eklediniz. Bu, bir değiştiricinin kaldırılabileceği yerleri asla düzenlemenin dışına çıkmamalıdır. Buna karşılık, composable olmayan değiştirici fabrikalar, composable fonksiyonların dışına çıkarılarak yeniden kullanım ve performansı artırmak için:
val extractedModifier = Modifier.background(Color.Red) // Hoisted to save allocations @Composable fun Modifier.composableModifier(): Modifier { val color = LocalContentColor.current.copy(alpha = 0.5f) return this then Modifier.background(color) } @Composable fun MyComposable() { val composedModifier = Modifier.composableModifier() // Cannot be extracted any higher }
Modifier.Node
kullanarak özel değiştirici davranışı uygulayın
Modifier.Node
, Compose'da değiştirici oluşturmak için kullanılan alt düzey bir API'dir. Google
Compose'un kendi değiştiricilerini uyguladığı API ile aynıdır.
en iyi performans gösteren yöntemidir.
Modifier.Node
kullanarak özel değiştirici uygulayın
Değiştirici.Düğüm'ü kullanarak özel değiştirici uygulamanın üç bölümü vardır:
- Mantık ve kapsayıcılığın geçerli olduğu bir
Modifier.Node
uygulaması değiştiricinizin durumuna göre değişir. - Değiştirici oluşturan ve güncelleyen bir
ModifierNodeElement
düğüm örneği olabilir. - Yukarıda açıklandığı şekilde isteğe bağlı bir değiştirici fabrikası.
ModifierNodeElement
sınıf durum bilgisizdir ve her bir sınıfa yeni örnekler ayrılır
yeniden oluşturma işlemi sırasında, Modifier.Node
sınıfları durum bilgili olabilir ve
yeniden bileşime dahil edilir ve hatta yeniden kullanılabilir.
Aşağıdaki bölümde her bölüm açıklanmış ve bir özel değiştiriciyi tıklayın.
Modifier.Node
Modifier.Node
uygulaması (bu örnekte CircleNode
)
"the"
işlevleri ekleyin.
// Modifier.Node private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() { override fun ContentDrawScope.draw() { drawCircle(color) } }
Bu örnekte, değiştiriciye aktarılan renk ile daireyi çiziyor işlevini kullanın.
Bir düğüm, Modifier.Node
özelliğinin yanı sıra sıfır veya daha fazla düğüm türünü de uygular. Her biri 100'den az gösterim alan
değiştiricinizin gerektirdiği işlevlere göre farklı düğüm türleri seçin. İlgili içeriği oluşturmak için kullanılan
örneğinin çizim yapabilmesi gerekir; bu nedenle DrawModifierNode
öğesini uygular.
Bu, çizim yöntemini geçersiz kılmasına olanak tanır.
Kullanılabilir türler şunlardır:
Düğüm |
Kullanım |
Örnek Bağlantı |
Sarmalanmış içeriğin ölçülme ve gösterilme şeklini değiştiren bir |
||
Düzenin alanını kullanan bir |
||
Bu arayüzün uygulanması, |
||
Test, erişilebilirlik ve benzer kullanım alanlarında kullanım için anlamsal anahtar/değer çifti ekleyen |
||
PointerInputChanges alan bir |
||
Üst düzene veri sağlayan bir |
||
|
||
İçeriğin genel konumu değişmiş olduğunda düzenin son |
||
|
||
Diğer Bu, birden fazla düğüm uygulamasının tek bir uygulamada oluşturulması yararlı olabilir. |
||
|
Karşılık gelen konumlarda güncelleme çağrıldığında düğümler otomatik olarak geçersiz kılınır
öğesine dokunun. Örneğimiz bir DrawModifierNode
olduğundan, her zaman güncellemesi
düğüm, bir yeniden çizimi tetikler ve rengi doğru şekilde güncellenir. Evet
aşağıda açıklandığı şekilde otomatik geçersiz kılmayı devre dışı bırakmanızı öneririz.
ModifierNodeElement
ModifierNodeElement
, oluşturulacak verileri barındıran sabit bir sınıftır
özel değiştiricinizi güncelleyin:
// ModifierNodeElement private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() { override fun create() = CircleNode(color) override fun update(node: CircleNode) { node.color = color } }
ModifierNodeElement
uygulamalarının aşağıdaki yöntemleri geçersiz kılması gerekir:
create
: Bu, değiştirici düğümünüzü örneklendiren işlevdir. Bu, , değiştiriciniz ilk uygulandığında düğümü oluşturmak için çağrılır. Genellikle bu, düğüm oluşturmak ve onu devre dışı bırakmak için kullanılan parametrelerle değiştirici fabrikaya geçirildi.update
: Bu işlev, bu düğüm zaten var, ancak bir özellik değişmiş. Bu sınıfınequals
yöntemi tarafından belirlenir. Önceki değiştirici düğüm daha önce oluşturulmuş olanlarupdate
çağrısına parametre olarak gönderilir. Bu noktada düğümlerinizi güncellemeniz gerekir. karşılık gelecek şekilde, parametreleridir. Düğümlerin bu şekilde yeniden kullanılabilmesiModifier.Node
sağlanan performans kazancı; bu nedenle, yeni bir düğüm oluşturmak yerineupdate
yöntemini kullanmanız gerekir. Şurada: daire örneğinde, düğümün rengi güncellenir.
Ayrıca, ModifierNodeElement
uygulamalarının equals
ve hashCode
. update
, yalnızca
önceki öğe false (yanlış) değerini döndürür.
Yukarıdaki örnekte, bunu başarmak için bir veri sınıfı kullanılmaktadır. Bu yöntemler,
bir düğümün güncellenmesi gerekip gerekmediğini kontrol edebilirsiniz. Öğenizin özellikleri
Bir düğümün güncellenmesi gerekip gerekmediğine veya verilerden kaçınmak istediğinize
uyumlu olması durumunda, manuel olarak equals
uygulayabilirsiniz.
ve hashCode
ör. dolgu değiştirici öğesini kullanın.
Değiştirici fabrikası
Bu, değiştiricinizin herkese açık API yüzeyidir. Çoğu uygulama, değiştirici öğeyi oluşturun ve değiştirici zincirine ekleyin:
// Modifier factory fun Modifier.circle(color: Color) = this then CircleElement(color)
Tam örnek
Bu üç bölüm bir araya getirilerek özel değiştiriciyi oluşturarak daire çizin
(Modifier.Node
API'leri) kullanarak:
// Modifier factory fun Modifier.circle(color: Color) = this then CircleElement(color) // ModifierNodeElement private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() { override fun create() = CircleNode(color) override fun update(node: CircleNode) { node.color = color } } // Modifier.Node private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() { override fun ContentDrawScope.draw() { drawCircle(color) } }
Modifier.Node
kullanımıyla ilgili yaygın durumlar
Modifier.Node
ile özel değiştiriciler oluştururken karşılaşabileceğiniz bazı yaygın durumlar şunlardır:
bahsedeceğim.
Sıfır parametre
Değiştiricinizde parametre yoksa hiçbir zaman güncellenmesi gerekmez. bir veri sınıfı olması gerekmez. Burada, örnek bir uygulama composable'a sabit miktarda dolgu uygulayan bir değiştiricinin örneğidir:
fun Modifier.fixedPadding() = this then FixedPaddingElement data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>() { override fun create() = FixedPaddingNode() override fun update(node: FixedPaddingNode) {} } class FixedPaddingNode : LayoutModifierNode, Modifier.Node() { private val PADDING = 16.dp override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val paddingPx = PADDING.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val placeable = measurable.measure(constraints.offset(-horizontal, -vertical)) val width = constraints.constrainWidth(placeable.width + horizontal) val height = constraints.constrainHeight(placeable.height + vertical) return layout(width, height) { placeable.place(paddingPx, paddingPx) } } }
Kompozisyon yerellerine referans verme
Modifier.Node
değiştiricileri, Oluştur durumundaki değişiklikleri otomatik olarak gözlemlemez
nesneler (CompositionLocal
gibi). Modifier.Node
değiştiricilerinin daha fazla avantajı
değiştiricilerinden biri, composable fabrikasında
değiştiricinin kullanıcı arayüzünde kullanıldığı yerin yerel değeri
değiştiricinin ayrıldığı yer değil, currentValueOf
kullanılır.
Bununla birlikte, değiştirici düğüm örnekleri durum değişikliklerini otomatik olarak gözlemlemez. Alıcı: bir bestenin yerel değişikliklerine otomatik olarak tepki verdiğini gösterirse, bir kapsamdaki değeri belirtin:
DrawModifierNode
:ContentDrawScope
LayoutModifierNode
:MeasureScope
veIntrinsicMeasureScope
SemanticsModifierNode
:SemanticsPropertyReceiver
Bu örnekte, temel olarak temelli arka plan çizmek için LocalContentColor
değeri gösterilmektedir
rengine bağlıyorum. ContentDrawScope
anlık görüntü değişiklikleri gözlemlediğinden,
LocalContentColor
değeri değiştiğinde otomatik olarak yeniden çizim yapar:
class BackgroundColorConsumerNode : Modifier.Node(), DrawModifierNode, CompositionLocalConsumerModifierNode { override fun ContentDrawScope.draw() { val currentColor = currentValueOf(LocalContentColor) drawRect(color = currentColor) drawContent() } }
Kapsam dışındaki durum değişikliklerine tepki vermek ve
değiştiricisi varsa ObserverModifierNode
kullanın.
Örneğin, Modifier.scrollable
bu tekniği şu amaçlarla kullanır:
LocalDensity
metriğindeki değişiklikleri gözlemleyin. Aşağıda basitleştirilmiş bir örnek gösterilmektedir:
class ScrollableNode : Modifier.Node(), ObserverModifierNode, CompositionLocalConsumerModifierNode { // Place holder fling behavior, we'll initialize it when the density is available. val defaultFlingBehavior = DefaultFlingBehavior(splineBasedDecay(UnityDensity)) override fun onAttach() { updateDefaultFlingBehavior() observeReads { currentValueOf(LocalDensity) } // monitor change in Density } override fun onObservedReadsChanged() { // if density changes, update the default fling behavior. updateDefaultFlingBehavior() } private fun updateDefaultFlingBehavior() { val density = currentValueOf(LocalDensity) defaultFlingBehavior.flingDecay = splineBasedDecay(density) } }
Animasyon değiştirici
Modifier.Node
uygulamalarının coroutineScope
öğesine erişimi var. Bu da
Oluşturulabilir API'lerin kullanımı. Örneğin, bu snippet
Yukarıdan arka arkaya şeffaflaşma ve azalma için CircleNode
:
class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode { private val alpha = Animatable(1f) override fun ContentDrawScope.draw() { drawCircle(color = color, alpha = alpha.value) drawContent() } override fun onAttach() { coroutineScope.launch { alpha.animateTo( 0f, infiniteRepeatable(tween(1000), RepeatMode.Reverse) ) { } } } }
Yetkiyi kullanarak değiştiriciler arasında paylaşım durumu
Modifier.Node
değiştiricileri başka düğümlere yetki verebilir. Pek çok kullanım alanı vardır
Örneğin, farklı değiştiricilerdeki yaygın uygulamaları ayıklama ve
ancak değiştiriciler arasında ortak bir durumu paylaşmak için de kullanılabilir.
Örneğin, aynı öğenin birden fazla kez kullanıldığı tıklanabilir etkileşim verileri:
class ClickableNode : DelegatingNode() { val interactionData = InteractionData() val focusableNode = delegate( FocusableNode(interactionData) ) val indicationNode = delegate( IndicationNode(interactionData) ) }
Düğümlerin otomatik olarak geçersiz kılınmasının kapsamı dışında kalma
Modifier.Node
düğüm, karşılık gelen
ModifierNodeElement
araması güncellemesi. Bazen daha karmaşık bir değiştiricide
kontrol edebilmek için bu davranışı devre dışı bırakmak
değiştiriciniz aşamaları geçersiz kılar.
Bu, özel değiştiriciniz hem düzeni hem de
çizin. Otomatik geçersiz kılmayı devre dışı bırakmak, yalnızca şu durumlarda çizimi geçersiz kılmanıza olanak tanır:
yalnızca color
gibi çizimle ilgili özellikleri değiştirin, düzeni geçersiz kılmayın.
Bu, değiştiricinizin performansını artırabilir.
Bunun varsayımsal bir örneği, color
değerine sahip bir değiştiriciyle gösterilmiştir.
size
ve onClick
lambda özellikleri. Bu değiştirici yalnızca
gerekir ve aşağıdaki olmayan geçersiz kılma işlemlerini atlar:
class SampleInvalidatingNode( var color: Color, var size: IntSize, var onClick: () -> Unit ) : DelegatingNode(), LayoutModifierNode, DrawModifierNode { override val shouldAutoInvalidate: Boolean get() = false private val clickableNode = delegate( ClickablePointerInputNode(onClick) ) fun update(color: Color, size: IntSize, onClick: () -> Unit) { if (this.color != color) { this.color = color // Only invalidate draw when color changes invalidateDraw() } if (this.size != size) { this.size = size // Only invalidate layout when size changes invalidateMeasurement() } // If only onClick changes, we don't need to invalidate anything clickableNode.update(onClick) } override fun ContentDrawScope.draw() { drawRect(color) } override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val size = constraints.constrain(size) val placeable = measurable.measure(constraints) return layout(size.width, size.height) { placeable.place(0, 0) } } }