Jetpack Compose #1의 그리기 및 페인팅

최근에 나는 . 보시다시피 RectangleShape , CircleShapeGenericShape 는 컴포저블에 단순한 형태(모양)를 적용하는 데 적합합니다. 그러나 채워지지 않았거나 열린 면이 있는 선, 점 또는 원을 그리는 경우는 어떻습니까? 우리가 예술가가 될 수 있는 방법을 조사해 봅시다. 자, 그것은 약간 이상하게 들릴 수 있습니다. 하지만 일부러 그랬어요. 예술가들은 종종 캔버스에 그림을 그리기 때문에 Canvas는 우리에게도 매우 중요합니다. 구경하다:



@Composable
fun SimpleCanvas() {
  Canvas(modifier = Modifier.fillMaxWidth().preferredHeight(128.dp),
    onDraw = {
      drawLine(
        Color.Black, Offset(0f, 0f),
        Offset(size.width - 1, size.height - 1)
      )
      drawLine(
        Color.Black, Offset(0f, size.height - 1),
        Offset(size.width - 1, 0f)
      )
      drawCircle(
        Color.Red, 64f,
        Offset(size.width / 2, size.height / 2)
      )
    })
}

Canvasallows you to을 구성하는 컴포저블입니다.

specify an area on the screen and perform canvas drawing
on this area. You MUST specify size with modifier, whether
with exact sizes via Modifier.size modifier, or relative to
parent, via Modifier.fillMaxSize, ColumnScope.weight, etc.
If parent wraps this child, only exact sizes must be specified.



그리기 지침은 내부에 제공됩니다onDraw . 내 예제는 두 줄과 채워진 원을 생성합니다. 예를 들어 단색 빨간색 대신 선형 그래디언트를 사용할 수 있습니다. 그러나 이를 달성하는 방법을 보여주기 전에 대신 개요를 작성해 보겠습니다.



drawCircle(
  Color.Red, 64f,
  Offset(size.width / 2, size.height / 2),
  style = Stroke(width = 8f,
    pathEffect = DashPathEffect(floatArrayOf(10f, 10f), 0f)
  ),
)

Stroke는 컴포저블이 아니라 클래스입니다. 그것의 pathEffectNativePathEffect 를 받는데, 이것은 android.graphics.PathEffect 의 typealias입니다. android.graphics.DashPathEffect에 의해 확장됩니다. Its documentation explains :

The intervals array must contain an even number of entries
(>=2), with the even indices specifying the "on" intervals,
and the odd indices specifying the "off" intervals.



따라서 내 예에서 "on"과 "off"의 거리는 동일합니다( 10f ). Stroke의 너비(또는 두께)는 8f입니다. 이것이 얼마나 큰지는 장치 구성에 따라 다릅니다. 이제 그래디언트로 돌아가 볼까요?



@Composable
fun CanvasWithGradient() {
  Canvas(modifier = Modifier.fillMaxWidth().preferredHeight(128.dp),
    onDraw = {
      val gradient = LinearGradient(
        listOf(Color.Blue, Color.Black),
        startX = size.width / 2 - 64, startY = size.height / 2 - 64,
        endX = size.width / 2 + 64, endY = size.height / 2 + 64,
        tileMode = TileMode.Clamp
      )
      drawCircle(
        gradient, 64f,
      )
    })
}


내가 원의 중심을 제공하지 않았다는 것을 눈치채셨나요? drawCircle()의 기본값 덕분에 이것은 필요하지 않습니다. 그럼에도 불구하고 저는 값( size.width / 2size.height / 2 )을 사용(즉, 계산)하고 있습니다. 선형 그래디언트의 정의에 값이 필요하기 때문입니다. 나는 그것이 원의 영역만을 덮기를 원합니다. 왼쪽 위 모서리는 startX , startY 이고 오른쪽 아래 모서리는 endX , endY 입니다.

방사형 그래디언트는 다음과 같습니다.



val gradient = RadialGradient(
  listOf(Color.Black, Color.Blue),
  centerX = center.x, centerY = center.y,
  radius = 64f
)
drawCircle(
  gradient, 64f,
)

center.xcenter.y는 내 캔버스의 중심을 지정합니다. 이것은 원의 중심(기본값)에 사용되므로 방사형 그래디언트에 다시 사용할 수 있습니다. 더 많은 그라데이션이 있습니다. 예를 들어 VerticalGradient 또는 HorizontalGradient 를 살펴볼 수 있습니다.

이 게시물을 마무리하기 위해 몇 가지 개별 픽셀을 그려봅시다. 다음은 직교 좌표계에서 사인 곡선을 그리는 컴포저블입니다.



@Composable
fun SinusPlotter() {
  Canvas(modifier = Modifier.fillMaxSize(),
    onDraw = {
      val middleW = size.width / 2
      val middleH = size.height / 2
      drawLine(Color.Gray, Offset(0f, middleH), Offset(size.width - 1, middleH))
      drawLine(Color.Gray, Offset(middleW, 0f), Offset(middleW, size.height - 1))
      val points = mutableListOf<Offset>()
      for (x in 0 until size.width.toInt()) {
        val y = (sin(x * (2f * PI / size.width)) * middleH + middleH).toFloat()
        points.add(Offset(x.toFloat(), y))
      }
      drawPoints(
        points = points,
        strokeWidth = 4f,
        pointMode = PointMode.Points,
        color = Color.Blue
      )
    }
  )
}

drawPoints()Offset 인스턴스인 경우 목록을 받습니다. PointMode.Points 의미: 개별 점을 그립니다. 기본값strokeWidth으로 출력이 보이지 않기 때문에 일부러 Stroke.HairlineWidth로 설정했습니다.

한 가지 더... 좌표계의 축이 어떻게 화살표처럼 보일 수 있는지 궁금할 것입니다. drawLine()strokeWidthcap를 모두 수신할 수 있지만 후자는 양쪽 끝에 사용됩니다. 또한 StrokeCap.Round , SquareButt 가 있는 반면 Arrow 는 없는 것 같습니다. 그래서 지금 내 결심은 혼자서 화살표를 그리는 것입니다. 다음은 가로 축에 대한 모양입니다.

drawPath(
  path = Path().apply {
    moveTo(size.width - 1, middleH)
    relativeLineTo(-20f, 20f)
    relativeLineTo(0f, -40F)
    close()
  },
  Color.Gray,
)


보시다시피, 저는 오랜 친구인 Path 를 사용하고 있으며, 이 친구는 drawPath() 로 전달됩니다. 세로 축에는 그리기 지침이 약간 변경되었습니다.

moveTo(size.width - 1, middleH)
relativeLineTo(-20f, 20f)
relativeLineTo(0f, -40F)


오늘은 여기까지입니다. 이에 대한 후속 조치가 곧 이루어지기를 바랍니다. 댓글로 여러분의 생각을 공유해주세요.


source

좋은 웹페이지 즐겨찾기