编程知识 cdmana.com

Compose | to understand the magical modifier

Write at the top

Jetpack Compose The preview version of has been out for a long time , I believe many readers have made some attempts . Be careful : Unless otherwise specified below ,Compose All refer to Jetpack Compose

so to speak ,Compose When declaring a layout , Its style and React Of JSX、Flutter And so on .

And there is a high-frequency content : Modifier, namely Decorators , seeing the name of a thing one thinks of its function , It's a decorator , stay Compose In the design of , and UI The relevant contents are related to it , for example : Size , shape etc.

This article , Let's learn two parts together :

  • Modifier Source code and design
  • SDK What's in it Modifier Implementation Overview

Of course , The most comprehensive learning document is : official API file , Follow up query API The meaning and design details of , Recommended collection

The codes in this article are all derived from 1.0.1 edition

Make a big move first ,Modifier Of 45 Line code

In fact, the effective lines of code are about 20 That's ok .

Let's start with an example :

Modifier.height(320.dp).fillMaxWidth()
 Copy code 

there Modifier It's the interface androidx.compose.ui.Modifier Anonymous implementation of , This is also an interesting practical technique .

Let's start with a brief overview of the source code , Then interpret :

interface Modifier {
    // ...
    companion object : Modifier {
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R = initial
        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R = initial
        override fun any(predicate: (Element) -> Boolean): Boolean = false
        override fun all(predicate: (Element) -> Boolean): Boolean = true
        override infix fun then(other: Modifier): Modifier = other
        override fun toString() = "Modifier"
    }
}
 Copy code 

The interface itself is :

package androidx.compose.ui

import androidx.compose.runtime.Stable

interface Modifier {

    fun <R> foldIn(initial: R, operation: (R, Element) -> R): R

    fun <R> foldOut(initial: R, operation: (Element, R) -> R): R

    fun any(predicate: (Element) -> Boolean): Boolean

    fun all(predicate: (Element) -> Boolean): Boolean

    infix fun then(other: Modifier): Modifier =
        if (other === Modifier) this else CombinedModifier(this, other)
}
 Copy code 

Modifier The interface implements appreciation by default

First look at Modifier Interface , and Java8 similar ,Kotlin The interface can provide a default implementation , obviously , foldIn and foldOut You can't see the way here , Must be combined with operation Look at , Skip first .

any and all I can't see anything , After all, I deleted all the comments and then The method is a little interesting , Receive one Modifier Interface instance , If the example is Modifier The internal default implementation of , It is considered an invalid operation , Still return to yourself , Otherwise, it returns a CombinedModifier example Combine yourself with other Bind together .

From here , We can read a little taste : The designer will put a series of Modifier Designed into a structure similar to a linked list , And hope we start from Modifier Of companion The implementation starts building .

Actually , Combine notes , We can know Modifier It will indeed form a linked list , also any and all Is to run a judgment expression on the elements of the linked list .

Modifier companion Realize appreciation

Let's go back companion Realization .thenfoldIn,foldOut It's what you give back , Combined with the previous interface default implementation , We can infer that : In normal use , The final linked list does not contain companion Realization , This is from its any and all The implementation of .

Obviously, this is an interesting technique , There is not too much parsing here , But since I describe it like this , You must be able to get it into the linked list .

CombinedModifier Realization

package androidx.compose.ui

import androidx.compose.runtime.Stable

class CombinedModifier(
    private val outer: Modifier,
    private val inner: Modifier
) : Modifier {
    override fun <R> foldIn(initial: R, operation: (R, Modifier.Element) -> R): R =
        inner.foldIn(outer.foldIn(initial, operation), operation)

    override fun <R> foldOut(initial: R, operation: (Modifier.Element, R) -> R): R =
        outer.foldOut(inner.foldOut(initial, operation), operation)

    override fun any(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.any(predicate) || inner.any(predicate)

    override fun all(predicate: (Modifier.Element) -> Boolean): Boolean =
        outer.all(predicate) && inner.all(predicate)

    override fun equals(other: Any?): Boolean =
        other is CombinedModifier && outer == other.outer && inner == other.inner

    override fun hashCode(): Int = outer.hashCode() + 31 * inner.hashCode()

    override fun toString() = "[" + foldIn("") { acc, element ->
        if (acc.isEmpty()) element.toString() else "$acc, $element"
    } + "]"
}

 Copy code 

There is still a lack of effective information to interpret foldIn and foldOut Will eventually do something , But you can see the order of execution , In addition, we can see that any and all No moth .

After watching Modifier.Element Then we appreciate foldIn and foldOut Recursion

Modifier.Element

No accident ,SDK Various internal decoration effects will implement this interface , There's no moth .

package androidx.compose.ui

interface Modifier {
    //...

    interface Element : Modifier {
        override fun <R> foldIn(initial: R, operation: (R, Element) -> R): R =
            operation(initial, this)

        override fun <R> foldOut(initial: R, operation: (Element, R) -> R): R =
            operation(this, initial)

        override fun any(predicate: (Element) -> Boolean): Boolean = predicate(this)

        override fun all(predicate: (Element) -> Boolean): Boolean = predicate(this)
    }
}

 Copy code 

foldIn and foldOut appreciation

Here is a chestnut foldIn and foldOut Recursion :

class A : Modifier.Element
class B : Modifier.Element
class C : Modifier.Element

fun Modifier.a() = this.then(A())
fun Modifier.b() = this.then(B())
fun Modifier.c() = this.then(C())
 Copy code 

that Modifier.a().b().c() What did you get ? To look straight , We With CM Generation refers to CombinedModifier

CM (
    outer = CM (
        outer = A(),
        inner = B()
    ),
    inner = C()
)
 Copy code 

Combined with the knowledge obtained from reading the source code , Let's assume another operation:

val initial = StringBuilder()
val operation: (StringBuilder, Element) -> StringBuilder = { builder, e ->
    builder.append(e.toString()).append(";")
    builder
} 
 Copy code 

obviously :

Modifier.a().b().c().foldIn(initial, operation)
 Copy code 

The resulting execution process is :

val ra = operation.invoke(initial,A())
val rb = operation.invoke(ra,B())
return operation.invoke(rb,C())
 Copy code 

From the head of the linked list to the tail of the linked list .

and foldOut On the contrary , From the end of the linked list to the head of the linked list .

Of course , In real use , We don't have to go back all the time initial. But this sum Modifier Do not have what relation , It only affects which object you use Modifier.

SDK What's in it Modifier Implementation Overview

In this paper , We are Modifier Source code and design details Spent a long time on , I believe all readers have fully understood , Let's take a look at some relaxed .

Obviously , The following part I'm familiar with you that will do , As in the Android Native layout in , It doesn't hurt to forget the specific spelling of layout attributes for a while , With the help of SDK Documents can be queried quickly , however I don't know these properties It will affect the development .

Three important packages

  • androidx.compose.foundation.layout: Modifier Layout related extensions
  • androidx.compose.ui.draw: Modifier Drawing related extensions
  • androidx.compose.foundation:Modifier The foundation package , The extended part is mainly click time 、 background 、 Sliding, etc

API The content of the document is very boring , If the reader just wants to be familiar first , Sure Read the following , If you plan to combine carefully API Document research , Sure Fork my WorkShop project , Compare the source code with the effect

foundation-layout library -- androidx.compose.foundation.layout

Concrete API For a list and description, see Api file

This package , Related to layout , Such as : Size 、 Margin 、 Box model, etc , Obviously , There are a lot of contents . About Modifier The content of , We don't list API.

Just as a DSL Design intention of , about Compose for , understand Android Students of native development , Or students who have a little understanding of the front-end field ,70% Of DSL-API You can see its meaning at a glance , And the rest , Most of them need to actually test the effect .

ui library -- androidx.compose.ui.draw

This part is mostly related to drawing , analogy Android Native technology stack , Part of the content is more in-depth , yes When you customize The use of Tools , Fortunately, this part API Not too much , We spend a screen listing , I'm familiar with you .

Concrete API For a list and description, see Api file

  • transparency

Modifier.alpha(alpha: Float)

  • Cut to shape

Modifier.clip(shape: Shape)

  • Cut the content according to the specified boundary , similar Android The son of View The content does not exceed the parent View

Modifier.clipToBounds()

Clip the content to the bounds of a layer defined at this modifier.

  • After that, make a specified drawing

Modifier.drawBehind(onDraw: DrawScope.() -> Unit)

Draw into a Canvas behind the modified content.

  • Cache based rendering , Used when the size has not changed , When the state has not changed

Modifier.drawWithCache(onBuildDrawCache: CacheDrawScope.() -> DrawResult)

  • Manually control the specified drawing before or after the layout

Modifier.drawWithContent(onDraw: ContentDrawScope.() -> Unit)

  • utilize Painter Drawing

Modifier.paint(painter: Painter, sizeToIntrinsics: Boolean, alignment: Alignment, contentScale: ContentScale, alpha: Float, colorFilter: ColorFilter?)

  • Rotate around the center

Modifier.rotate(degrees: Float)

  • The zoom

Modifier.scale(scaleX: Float, scaleY: Float)

  • Scale proportionally

Modifier.scale(scale: Float)

  • Draw shadows

Modifier.shadow(elevation: Dp, shape: Shape, clip: Boolean)

foundation library -- androidx.compose.foundation

Fortunately, this part is not too much , List below

  • Set the background

Modifier.background(color: Color, shape: Shape = RectangleShape)

Modifier.background(brush: Brush, shape: Shape = RectangleShape, alpha: Float = 1.0f)

Brush It's gradual ,Color It's solid

  • Set boundaries , That is, the stroke effect

Modifier.border(border: BorderStroke, shape: Shape = RectangleShape)

Modifier.border(width: Dp, color: Color, shape: Shape = RectangleShape)

Modifier.border(width: Dp, brush: Brush, shape: Shape)

  • Click effect

Modifier.clickable(enabled: Boolean = true, onClickLabel: String? = null, role: Role? = null, onLongClickLabel: String? = null, onLongClick: () -> Unit = null, onDoubleClick: () -> Unit = null, onClick: () -> Unit)

Modifier.clickable(enabled: Boolean = true, interactionState: InteractionState, indication: Indication?, onClickLabel: String? = null, role: Role? = null, onLongClickLabel: String? = null, onLongClick: () -> Unit = null, onDoubleClick: () -> Unit = null, onClick: () -> Unit)

Long press 、 single click 、 Double click to include

  • Sliding

Modifier.horizontalScroll(state: ScrollState, enabled: Boolean = true, reverseScrolling: Boolean = false)

Modifier.verticalScroll(state: ScrollState, enabled: Boolean = true, reverseScrolling: Boolean = false)

Conclusion

This blog is officially starting to learn Jetpack Compose.

This is a new content , To really master it comprehensively, we still need to accumulate a lot of knowledge , It's like getting started Android Develop that , The use of various controls requires learning and memory

But it is also limited to : A new declarative 、 Response type UI Build the framework , Don't be too afraid , Although there is a certain starting cost , But it hasn't overturned the whole Android The development mode of client .

another :WorkShop The demo code in will follow the whole Compose A series of questions , I'm interested in updating some of them , This means that there may be : Some effects mentioned in the blog , but WorkShop It's not written in

版权声明
本文为[leobert-lan]所创,转载请带上原文链接,感谢
https://cdmana.com/2021/08/20210809184641311E.html

Scroll to Top