Modyfikatory grafiki

Oprócz funkcji kompozycyjnej Canvas w aplikacji Compose znajdziesz też przydatne grafiki Modifiers, które ułatwiają rysowanie treści niestandardowych. Te modyfikatory są przydatne ponieważ można je zastosować do każdego elementu kompozycyjnego.

Modyfikatory rysowania

Wszystkie polecenia rysowania są wykonywane przy użyciu modyfikatora rysunku w narzędziu Compose. Istnieją trzy główne modyfikatory rysowania w Compose:

Podstawowy modyfikator rysowania to drawWithContent. Możesz wybrać wartość kolejność rysowania elementu kompozycyjnego oraz polecenia rysowania wysyłane w elemencie modyfikator. drawBehind to wygodny kod wokół drawWithContent, który ma kolejność rysowania ustawiona w tle treści kompozycyjnej. drawWithCache wywołuje w nim onDrawBehind lub onDrawWithContent i zapewnia do buforowania utworzonych obiektów.

Modifier.drawWithContent: wybierz kolejność rysowania

Modifier.drawWithContent umożliwia wykonaj operacje DrawScope przed lub po treści zapytania kompozycyjne. Pamiętaj, by wywołać drawContent, by potem wyświetlić rzeczywistą treść kompozycyjne. Za pomocą tego modyfikatora można określić kolejność działań, jeśli chcesz, aby zawartość była rysowana przed rysunkiem niestandardowym lub po nim operacji.

Jeśli na przykład chcesz renderować gradient promieniowy na swojej treści, aby wywołać w interfejsie efekt dziurki od klucza w latarce, możesz wykonać następujące czynności:

var pointerOffset by remember {
    mutableStateOf(Offset(0f, 0f))
}
Column(
    modifier = Modifier
        .fillMaxSize()
        .pointerInput("dragging") {
            detectDragGestures { change, dragAmount ->
                pointerOffset += dragAmount
            }
        }
        .onSizeChanged {
            pointerOffset = Offset(it.width / 2f, it.height / 2f)
        }
        .drawWithContent {
            drawContent()
            // draws a fully black area with a small keyhole at pointerOffset that’ll show part of the UI.
            drawRect(
                Brush.radialGradient(
                    listOf(Color.Transparent, Color.Black),
                    center = pointerOffset,
                    radius = 100.dp.toPx(),
                )
            )
        }
) {
    // Your composables here
}

Rys. 1. Metoda Modifier.drawWithContent używana nad obiektem kompozycyjnym do utworzenia interfejsu typu latarka.

Modifier.drawBehind: zarysowanie elementu kompozycyjnego

Modifier.drawBehind umożliwia Ci DrawScope operacje za treściami kompozycyjnymi rysowanymi na ekranie. Jeśli jeśli zapoznasz się z implementacją Canvas, możesz zauważyć, że jest tylko wygodnym kodem elementu Modifier.drawBehind.

Aby narysować zaokrąglony prostokąt za elementem Text:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawBehind {
            drawRoundRect(
                Color(0xFFBBAAEE),
                cornerRadius = CornerRadius(10.dp.toPx())
            )
        }
        .padding(4.dp)
)

Co daje następujący wynik:

Tekst i tło utworzone za pomocą metody Modifier.drawBehind
Rysunek 2. Tekst i tło utworzone za pomocą metody Modifier.drawBehind

Modifier.drawWithCache: rysowanie i zapisywanie obiektów rysunkowych w pamięci podręcznej

Modifier.drawWithCache zachowuje obiekty tworzonych w niej w pamięci podręcznej. Obiekty są przechowywane w pamięci podręcznej, jeśli rozmiar obszaru rysowania jest taka sama lub żadne odczytywane obiekty stanu została zmieniona. Ten modyfikator jest przydatny do poprawy wydajności wywołań rysowania, ponieważ Pozwala to uniknąć ponownej alokacji obiektów (np. Brush, Shader, Path). tworzone podczas rysowania.

Można też buforować obiekty za pomocą remember, poza modyfikator. Nie zawsze jest to jednak możliwe, ponieważ nie zawsze masz dostęp do kompozycji. Użycie właściwości drawWithCache może być skuteczniejsze, jeśli obiekty służą tylko do rysowania.

Jeśli na przykład utworzysz obiekt Brush, aby narysować gradient za obiektem Text, używając funkcji drawWithCache zapisuje w pamięci podręcznej obiekt Brush do momentu zmiany rozmiaru obszaru rysowania zmiany:

Text(
    "Hello Compose!",
    modifier = Modifier
        .drawWithCache {
            val brush = Brush.linearGradient(
                listOf(
                    Color(0xFF9E82F0),
                    Color(0xFF42A5F5)
                )
            )
            onDrawBehind {
                drawRoundRect(
                    brush,
                    cornerRadius = CornerRadius(10.dp.toPx())
                )
            }
        }
)

Buforowanie obiektu Brush za pomocą metody pullWithCache
Rysunek 3. Buforowanie obiektu Brush za pomocą metody pullWithCache

Modyfikatory grafiki

Modifier.graphicsLayer: stosuj przekształcenia do elementów kompozycyjnych

Modifier.graphicsLayer to modyfikator, który przekształca zawartość elementu kompozycyjnego w warstwę rysowania. O warstwa udostępnia kilka funkcji, takich jak:

  • Izolacja w instrukcjach rysowania (podobne do: RenderNode). Rysunek instrukcje uchwycone w ramach warstwy mogą zostać ponownie wydane przez potoku renderowania bez ponownego uruchamiania kodu aplikacji.
  • Przekształcenia, które mają zastosowanie do wszystkich instrukcji rysowania zawartych w warstwę.
  • Rasteryzacja w celu uzyskania możliwości kompozycji. Przy zrasteryzowaniu warstwy instrukcje rysowania są wykonywane, a dane wyjściowe są zapisywane na ekranie poza ekranem bufora. Komponowanie takiego bufora na potrzeby kolejnych klatek jest szybsze niż poszczególnych instrukcji, ale będzie zachowywać się jak bitmapa, gdy takie jak skalowanie czy obrót.

Transformacje

Pole Modifier.graphicsLayer zapewnia izolację instrukcji rysowania. w przypadku można zastosować różne przekształcenia, używając funkcji Modifier.graphicsLayer. Mogą być one animowane lub modyfikowane bez konieczności ponownego uruchamiania rysunku lambda.

Modifier.graphicsLayer nie zmienia mierzonego rozmiaru ani miejsca docelowego kompozycyjne, bo ma wpływ tylko na fazę rysowania. Oznacza to, że funkcja kompozycyjna może nakładać się na inne, jeśli rysuje poza granicami swojego układu.

Przy użyciu tego modyfikatora można zastosować te przekształcenia:

Skala – zwiększ rozmiar

scaleX i scaleY pozwalają powiększać lub pomniejszać treści w orientacji poziomej lub pionowej. kierunku. Wartość 1.0f oznacza brak zmiany skali; wartość 0.5f oznacza połowę wymiaru.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.scaleX = 1.2f
            this.scaleY = 0.8f
        }
)

Rys. 4: skalaX i skalaY zastosowane do elementu kompozycyjnego obrazu
.
Tłumaczenie

Elementy translationX i translationY można zmienić za pomocą tagów graphicsLayer, translationX przesuwa element kompozycyjny w lewo lub w prawo. translationY przesuwa w górę lub w dół.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.translationX = 100.dp.toPx()
            this.translationY = 10.dp.toPx()
        }
)

Rysunek 5.: przesunięcieX i przesunięcieY zastosowane do obrazu z zastosowaniem Modifier.graphicsLayer
.
Obrót

Ustaw rotationX, aby obracać urządzenie w poziomie, a rotationY, aby obracać je w pionie, rotationZ, aby obrócić je wokół osi Z (obrót standardowy). Ta wartość jest określona w stopniach (0–360).

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

Rysunek 6.: obrót X, obrót Y i obrót Z ustawiony za pomocą komponentu Image by Modifier.graphicsLayer
.
Źródło

Można określić transformOrigin. Jest on następnie używany jako punkt, z którego i przekształcania danych. We wszystkich dotychczasowych przykładach użyto TransformOrigin.Center, czyli o (0.5f, 0.5f). Jeśli określisz punkt początkowy (0f, 0f), przekształcenia zaczynają się w lewym górnym rogu okna kompozycyjne.

Jeśli zmienisz źródło za pomocą przekształcenia rotationZ, możesz zobaczyć, że element obraca się wokół lewego górnego rogu elementu kompozycyjnego:

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "Sunset",
    modifier = Modifier
        .graphicsLayer {
            this.transformOrigin = TransformOrigin(0f, 0f)
            this.rotationX = 90f
            this.rotationY = 275f
            this.rotationZ = 180f
        }
)

Rysunek 7.: Obrót z zastosowanym TransformOrigin ustawionym na 0f, 0f
.

Klip i kształt

Kształt określa kontur, do którego będą przechodzić treści, gdy clip = true. W W tym przykładzie ustawiliśmy 2 pola z 2 różnymi klipami graphicsLayer zmienną klipu, a druga – za pomocą wygodnego opakowania. Modifier.clip

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .size(200.dp)
            .graphicsLayer {
                clip = true
                shape = CircleShape
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }
    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(CircleShape)
            .background(Color(0xFF4DB6AC))
    )
}

Zawartość pierwszego pola (tekst „Hello Compose”) jest przycinana do kształt koła:

Klip został zastosowany do możliwości kompozycyjnej w Box
Rysunek 8. Klip zastosowany do elementu kompozycyjnego Box

Jeśli następnie zastosujesz wymiar translationY do górnego różowego okręgu, zobaczysz, że granice elementów kompozycyjnych są nadal takie same, ale okrąg rysuje się pod spodem okręgu (i poza jego granicami).

Klip został zastosowany z przesunięciem Y i czerwone obramowanie dla konturu
Rys. 9: Zastosowano klips z przesunięciem Y i czerwone obramowanie dla konturu

Aby przyciąć kompozycję do regionu, w którym jest utworzona, możesz dodać kolejny Modifier.clip(RectangleShape) na początku łańcucha modyfikatorów. Treść a potem pozostaje w pierwotnych granicach.

Column(modifier = Modifier.padding(16.dp)) {
    Box(
        modifier = Modifier
            .clip(RectangleShape)
            .size(200.dp)
            .border(2.dp, Color.Black)
            .graphicsLayer {
                clip = true
                shape = CircleShape
                translationY = 50.dp.toPx()
            }
            .background(Color(0xFFF06292))
    ) {
        Text(
            "Hello Compose",
            style = TextStyle(color = Color.Black, fontSize = 46.sp),
            modifier = Modifier.align(Alignment.Center)
        )
    }

    Box(
        modifier = Modifier
            .size(200.dp)
            .clip(RoundedCornerShape(500.dp))
            .background(Color(0xFF4DB6AC))
    )
}

Klip został zastosowany nad przekształceniem warstwy graficznej
Rys. 10: Klip został zastosowany nad przekształceniem warstwy graficznej

Alfa

Za pomocą Modifier.graphicsLayer można ustawić alpha (przezroczystość) dla całego obiektu warstwę. Element 1.0f jest całkowicie nieprzezroczysty, a element 0.0f jest niewidoczny.

Image(
    painter = painterResource(id = R.drawable.sunset),
    contentDescription = "clock",
    modifier = Modifier
        .graphicsLayer {
            this.alpha = 0.5f
        }
)

Obraz z zastosowaną wersją alfa
Rys. 11 Obraz z zastosowanym kodem alfa

Strategia komponowania

Korzystanie z wersji alfa i przejrzystości może nie być tak proste, jak wartości alfa. Poza zmianą kanału alfa możesz też ustawić CompositingStrategygraphicsLayer. CompositingStrategy określa, w jaki sposób treść funkcji kompozycyjnej jest skomponowana (połączona) z drugą treści, które są już narysowane na ekranie.

Dostępne strategie to:

Automatycznie (domyślnie)

Strategia komponowania jest określana przez pozostałe pola graphicsLayer . Wyrenderuje warstwę w buforze poza ekranem, jeśli poziom alfa jest mniejszy niż Ustawiono wartość 1.0f lub RenderEffect. Jeśli alfa jest mniejsza niż 1f, warstwa komponowania jest tworzona automatycznie w celu renderowania zawartości, a następnie rysowana ten bufor poza ekranem do miejsca docelowego z odpowiednim alfa. Ustawienie RenderEffect lub nadmiernie przewijanie zawsze renderuje treść poza ekranem bufor niezależnie od ustawienia CompositingStrategy.

Poza ekranem

Zawartość funkcji kompozycyjnej jest zawsze zrastrowana na obszar poza ekranem tekstury lub bitmapy przed renderowaniem w miejscu docelowym. Przydaje się to w przypadku stosowania operacji BlendMode do maskowania treści oraz do wydajności, gdy złożonych instrukcji rysowania.

Przykład użycia CompositingStrategy.Offscreen z BlendModes. Patrząc na poniższy przykład, Załóżmy, że chcesz usunąć fragmenty elementu Image kompozycyjnego, uruchamiając polecenie rysowania, które używa BlendMode.Clear. Jeśli nie ustawisz właściwości compositingStrategy na CompositingStrategy.Offscreen, BlendMode współdziała ze wszystkimi treściami pod nim.

Image(painter = painterResource(id = R.drawable.dog),
   contentDescription = "Dog",
   contentScale = ContentScale.Crop,
   modifier = Modifier
       .size(120.dp)
       .aspectRatio(1f)
       .background(
           Brush.linearGradient(
               listOf(
                   Color(0xFFC5E1A5),
                   Color(0xFF80DEEA)
               )
           )
       )
       .padding(8.dp)
       .graphicsLayer {
           compositingStrategy = CompositingStrategy.Offscreen
       }
       .drawWithCache {
           val path = Path()
           path.addOval(
               Rect(
                   topLeft = Offset.Zero,
                   bottomRight = Offset(size.width, size.height)
               )
           )
           onDrawWithContent {
               clipPath(path) {
                   // this draws the actual image - if you don't call drawContent, it wont
                   // render anything
                   this@onDrawWithContent.drawContent()
               }
               val dotSize = size.width / 8f
               // Clip a white border for the content
               drawCircle(
                   Color.Black,
                   radius = dotSize,
                   center = Offset(
                       x = size.width - dotSize,
                       y = size.height - dotSize
                   ),
                   blendMode = BlendMode.Clear
               )
               // draw the red circle indication
               drawCircle(
                   Color(0xFFEF5350), radius = dotSize * 0.8f,
                   center = Offset(
                       x = size.width - dotSize,
                       y = size.height - dotSize
                   )
               )
           }

       }
)

Jeśli CompositingStrategy ma wartość Offscreen, powoduje to utworzenie elementu poza ekranem tekstury do wykonania poleceń (stosując BlendMode tylko do zawartości tego elementu kompozycyjnego). Następnie renderuje ją nad tym, co jest już renderowane na ekranie i nie mają wpływu na treść już narysowaną.

Modifier.drawWithContent na obrazie z oznaczeniem okręgu i użyciem funkcji BlendMode.Clear w aplikacji.
Rysunek 12. Obraz z komponentem Modifier.drawWithContent na obrazie z okręgiem, a w aplikacji BlendMode.Clear and CompositingStrategy.Offscreen

Jeśli nie używasz karty CompositingStrategy.Offscreen, wyniki zastosowania BlendMode.Clear usuwa wszystkie piksele w miejscu docelowym, niezależnie od tego, została już ustawiona, pozostawiając czarny bufor renderowania okna. Wiele spośród BlendModes, w których występuje wersja alfa, nie będzie działać zgodnie z oczekiwaniami bez bufor poza ekranem. Zwróć uwagę na czarny pierścień wokół wskaźnika czerwonego okręgu:

Modifier.drawWithContent na obrazie z zaznaczonym okręgiem, z ustawieniem BlendMode.Clear i bez ustawionej strategii tworzenia kompozycji
Rysunek 13. Element Modifier.drawWithContent na obrazie z zaznaczonym okręgiem, z ustawieniem BlendMode.Clear i bez ustawionej strategii kompozycji

Jeśli aplikacja ma półprzezroczyste okno, i nie użyto CompositingStrategy.Offscreen, Aplikacja BlendMode będzie wchodzić w interakcję z całą aplikacją. Wszystkie piksele zostaną usunięte aplikację lub tapetę poniżej, jak w tym przykładzie:

Nie ustawiono strategii tworzenia kompozycji i nie używasz BlendMode.Clear w aplikacji z przezroczystym tłem okna. Wokół czerwonego okręgu stanu wyświetla się różowa tapeta.
Rysunek 14. Nie ustawiono strategii kompozycji i użyto funkcji BlendMode.Clear w aplikacji z półprzezroczystym tłem okna. Zwróć uwagę, że różowa tapeta wyświetla się w obszarze wokół czerwonego okręgu stanu.

Warto zauważyć, że podczas korzystania z CompositingStrategy.Offscreen, tekstura o rozmiarze obszaru rysowania jest tworzona i renderowana z powrotem ekranu. Wszystkie polecenia rysowania wykonywane w ramach tej strategii są domyślnie ustawione jako przypięty do tego obszaru. Poniższy fragment kodu ilustruje różnice, gdy przełączenie na używanie tekstur pozaekranowych:

@Composable
fun CompositingStrategyExamples() {
   Column(
       modifier = Modifier
           .fillMaxSize()
           .wrapContentSize(Alignment.Center)
   ) {
       /** Does not clip content even with a graphics layer usage here. By default, graphicsLayer
       does not allocate + rasterize content into a separate layer but instead is used
       for isolation. That is draw invalidations made outside of this graphicsLayer will not
       re-record the drawing instructions in this composable as they have not changed **/
       Canvas(
           modifier = Modifier
               .graphicsLayer()
               .size(100.dp) // Note size of 100 dp here
               .border(2.dp, color = Color.Blue)
       ) {
           // ... and drawing a size of 200 dp here outside the bounds
           drawRect(color = Color.Magenta, size = Size(200.dp.toPx(), 200.dp.toPx()))
       }

       Spacer(modifier = Modifier.size(300.dp))

       /** Clips content as alpha usage here creates an offscreen buffer to rasterize content
       into first then draws to the original destination **/
       Canvas(
           modifier = Modifier
               // force to an offscreen buffer
               .graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen)
               .size(100.dp) // Note size of 100 dp here
               .border(2.dp, color = Color.Blue)
       ) {
           /** ... and drawing a size of 200 dp. However, because of the CompositingStrategy.Offscreen usage above, the
           content gets clipped **/
           drawRect(color = Color.Red, size = Size(200.dp.toPx(), 200.dp.toPx()))
       }
   }
}

CompositingStrategy.Auto vs CompositingStrategy.Offscreen – klipy wyświetlane poza ekranem, w których miejsca nie są dostępne automatycznie
Rysunek 15.: Strategia komponowania.Auto a strategia tworzenia kompozycji. Poza ekranem – klipy wyświetlane poza ekranem w regionie, w którym automatyczna nie działa
ModulateAlpha

Ta strategia kompozycji moduluje współczynnik alfa każdego rysunku instrukcji nagranych w graphicsLayer. Nie utworzy ona bufor poza ekranem dla wersji alfa poniżej 1,0f, chyba że jest ustawiony parametr RenderEffect, dzięki czemu może i efektywniejsze renderowanie w wersji alfa. Może jednak przynieść inne wyniki w przypadku pokrywających się treści. W przypadkach, w których z wyprzedzeniem wiadomo, że treść nie nakładają się, może to zapewnić większą skuteczność niż Funkcja CompositingStrategy.Auto o wartościach alfa mniejszych niż 1.

Inny przykład różnych strategii kompozycji znajdziesz poniżej – z zastosowaniem różnych alfa do różnych części elementów kompozycyjnych oraz zastosowanie funkcji Modulate strategia:

@Preview
@Composable
fun CompositingStratgey_ModulateAlpha() {
  Column(
      modifier = Modifier
          .fillMaxSize()
          .padding(32.dp)
  ) {
      // Base drawing, no alpha applied
      Canvas(
          modifier = Modifier.size(200.dp)
      ) {
          drawSquares()
      }

      Spacer(modifier = Modifier.size(36.dp))

      // Alpha 0.5f applied to whole composable
      Canvas(modifier = Modifier
          .size(200.dp)
          .graphicsLayer {
              alpha = 0.5f
          }) {
          drawSquares()
      }
      Spacer(modifier = Modifier.size(36.dp))

      // 0.75f alpha applied to each draw call when using ModulateAlpha
      Canvas(modifier = Modifier
          .size(200.dp)
          .graphicsLayer {
              compositingStrategy = CompositingStrategy.ModulateAlpha
              alpha = 0.75f
          }) {
          drawSquares()
      }
  }
}

private fun DrawScope.drawSquares() {

  val size = Size(100.dp.toPx(), 100.dp.toPx())
  drawRect(color = Red, size = size)
  drawRect(
      color = Purple, size = size,
      topLeft = Offset(size.width / 4f, size.height / 4f)
  )
  drawRect(
      color = Yellow, size = size,
      topLeft = Offset(size.width / 4f * 2f, size.height / 4f * 2f)
  )
}

val Purple = Color(0xFF7E57C2)
val Yellow = Color(0xFFFFCA28)
val Red = Color(0xFFEF5350)

Modulatalfa stosuje zestaw alfa do każdego polecenia rysowania
Rysunek 16. Modulacja alfa stosuje zestaw alfa do każdego polecenia rysowania

Zapisz zawartość elementu kompozycyjnego w bitmapie

Typowym przypadkiem użycia jest utworzenie Bitmap z funkcji kompozycyjnej. Aby skopiować wartość zawartości funkcji kompozycyjnej w funkcji Bitmap utwórz GraphicsLayer za pomocą funkcji rememberGraphicsLayer()

Przekieruj polecenia rysowania do nowej warstwy za pomocą klawiszy drawWithContent() i graphicsLayer.record{} Następnie narysuj warstwę w widocznym obszarze roboczym za pomocą drawLayer:

val coroutineScope = rememberCoroutineScope()
val graphicsLayer = rememberGraphicsLayer()
Box(
    modifier = Modifier
        .drawWithContent {
            // call record to capture the content in the graphics layer
            graphicsLayer.record {
                // draw the contents of the composable into the graphics layer
                this@drawWithContent.drawContent()
            }
            // draw the graphics layer on the visible canvas
            drawLayer(graphicsLayer)
        }
        .clickable {
            coroutineScope.launch {
                val bitmap = graphicsLayer.toImageBitmap()
                // do something with the newly acquired bitmap
            }
        }
        .background(Color.White)
) {
    Text("Hello Android", fontSize = 26.sp)
}

Możesz zapisać tę mapę bitową na dysku i ją udostępnić. Aby uzyskać więcej informacji, zapoznaj się z pełnym przykładowy fragment kodu. Zanim spróbujesz, sprawdź uprawnienia na urządzeniu aby zapisać na dysku.

Niestandardowy modyfikator rysunku

Aby utworzyć własny modyfikator niestandardowy, zaimplementuj interfejs DrawModifier. Ten daje Ci dostęp do elementu ContentDrawScope, który jest taki sam jak ten, który jest widoczny podczas korzystania z Modifier.drawWithContent(). Następnie możesz wyodrębnić wspólny rysunek za pomocą niestandardowych modyfikatorów rysowania, aby wyczyścić kod i zapewnić wygodne opakowania; np. Modifier.background() to wygodna DrawModifier

Jeśli na przykład chcesz zaimplementować obiekt Modifier, który obraca się w pionie treści, możesz utworzyć jedną z nich w następujący sposób:

class FlippedModifier : DrawModifier {
    override fun ContentDrawScope.draw() {
        scale(1f, -1f) {
            this@draw.drawContent()
        }
    }
}

fun Modifier.flipped() = this.then(FlippedModifier())

Następnie użyj tego odwróconego modyfikatora zastosowanego w elemencie Text:

Text(
    "Hello Compose!",
    modifier = Modifier
        .flipped()
)

Niestandardowy modyfikator odwrócenia tekstu
Rys. 17 Niestandardowy modyfikator odwrócony w tekście

Dodatkowe materiały

Więcej przykładów użycia obiektu graphicsLayer i rysunku niestandardowego znajdziesz w następujące zasoby:

. .