Jetpack Compose from entry to entry (6)

This article talks about Canvas in Compose.

1. Canvas

@Composable
fun Canvas(
	modifier: Modifier,
	onDraw: DrawScope.() -> Unit
) = Spacer(modifier.drawBehind(onDraw))
  • modifier: The main function here is to specify the size of the canvas.
  • onDrawIt is to perform specific drawing. It can be seen that it provides a scope of drawing environment DrawScope, where we provide the drawing api and properties we often use, such as drawLine, , sizeand so on.

Let's take a simple example to see how to use it:

Canvas(modifier = Modifier.fillMaxSize()) {
    
    
    val canvasWidth = size.width
    val canvasHeight = size.height

    drawLine(
        start = Offset(x = canvasWidth, y = 0f),
        end = Offset(x = 0f, y = canvasHeight),
        color = Color.Blue
    )
}

Draw a line that starts and ends at the upper-right and lower-left corners of the canvas, respectively. The effect is as follows:

insert image description here

2. Drawing method

1. drawLine

drawLineIt is briefly explained in the above example, of course it is more than these functions.

	fun drawLine(
        color: Color, //或 brush: Brush,
        start: Offset,
        end: Offset,
        strokeWidth: Float = Stroke.HairlineWidth,
        cap: StrokeCap = Stroke.DefaultCap,
        pathEffect: PathEffect? = null,
        /*FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • Specify the color of the line withcolor
  • Gradient colors can be used brush, as explained in the third article of this series.
  • strokeWidthIs the width of the line, the default is 1px.
  • capIt is the shape of the thread head, and the default is StrokeCap.Buttflat head. And StrokeCap.Roundround head, StrokeCap.Squaresquare head. This part is the same as Paint in Android. The difference between a flat head and a square head is whether it is an extended part.

insert image description here

  • pathEffectIt is the effect of a line segment, such as a dotted line, which is used PathEffect.dashPathEffect(intervals: FloatArray, phase: Float = 0f), for example:
	PathEffect.dashPathEffect(floatArrayOf(20f, 10f), 10f)

intervals20f in represents the width of the dotted line, and 10f is the interval width. phaseThe 10f represents the initial offset distance. Therefore, the offset of 10f at the beginning will cause the line segment of the first segment to be "cut" by 10f, and the specific effect is as follows:

insert image description here

  • alphais the transparency of the line segment.
  • colorFilterIt is a color filter. It is explained in the fourth article of this series, so I won’t repeat it here.
  • blendMode: blend mode. This is beyond the scope of this article, and I will talk about it in detail later when I have a chance. If you are interested, you can read the reference article at the end of the article first.

2. drawRect

The method of drawing a rectangle has the same properties as the method drawLine, and some differences are mentioned below.

	fun drawRect(
        color: Color,
        topLeft: Offset = Offset.Zero,
        size: Size = this.size.offsetSize(topLeft),
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • topLeftIt is used to specify the offset of the upper left corner. If not specified, it will start from the upper left corner of the current canvas by default.
  • sizeUsed to specify the size of the rectangle, if not specified, the default is the size of the current canvas.
  • styleIs solid or hollow. The default Fillis solid and Strokehollow.

3. drawRoundRect

Drawing a rounded rectangle is basically the same as a rectangle, but there is one more parameter to set the size of the rounded corners drawRoundRect, so I won’t explain it here.

4. drawImage

Draw picture method

	fun drawImage(
        image: ImageBitmap,
        srcOffset: IntOffset = IntOffset.Zero,
        srcSize: IntSize = IntSize(image.width, image.height),
        dstOffset: IntOffset = IntOffset.Zero,
        dstSize: IntSize = srcSize,
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • image: The picture to be drawn, which can be ImageBitmap.imageResource(id = R.drawable.xxx)obtained by using the method.
  • srcOffset: The offset of the upper left corner of the image that needs to be drawn, which defaults to the origin of the image.
  • srcSize: The relative srcOffsetsize of the image, the default is the width and height of the image.
  • dstOffset: The offset relative to the upper left corner of the drawn image, which defaults to the origin of the image.
  • dstSize: The size of the drawn image, the default is srcSize.

The following code is to draw the lower right corner area of ​​a picture, offset 50 * 50 relative to the canvas, and the drawn size is 200 * 200.

 	val imageBitmap = ImageBitmap.imageResource(id = R.mipmap.ic_launcher)

    Canvas(modifier = Modifier.fillMaxSize()) {
    
    
        drawImage(
            image = imageBitmap,
            srcOffset = IntOffset(imageBitmap.width / 2,imageBitmap.height / 2),
            srcSize = IntSize(imageBitmap.width, imageBitmap.height),
            dstOffset = IntOffset(50,50),
            dstSize = IntSize(200,200)
        )
    }

The effect is as follows:
insert image description here

5. drawCircle

draw circle method

	fun drawCircle(
        color: Color,
        radius: Float = size.minDimension / 2.0f,
        center: Offset = this.center,
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • radius: The radius of the circle.
  • center: The position of the center of the circle.

6. drawArc

drawArcCan be used to draw arcs or sectors

	fun drawArc(
        color: Color,
        startAngle: Float,
        sweepAngle: Float,
        useCenter: Boolean,
        topLeft: Offset = Offset.Zero,
        size: Size = this.size.offsetSize(topLeft),
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • startAngle: start angle
  • sweepAngle: Angle swept by the arc
  • useCenter: Whether the arc passes through the center of the circle

I won’t give an example here, you can use styleand useCenterattribute to combine and try.

7. drawPath

draw path method

	fun drawPath(
        path: Path,
        color: Color,
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        style: DrawStyle = Fill,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )

In fact, it is the same as the use of path in Android, and the methods of moveTo, lineTo, and close will not be described in detail.

8. drawPoints

method of plotting points

	fun drawPoints(
        points: List<Offset>,
        pointMode: PointMode,
        color: Color,
        strokeWidth: Float = Stroke.HairlineWidth,
        cap: StrokeCap = StrokeCap.Butt,
        pathEffect: PathEffect? = null,
        /*@FloatRange(from = 0.0, to = 1.0)*/
        alpha: Float = 1.0f,
        colorFilter: ColorFilter? = null,
        blendMode: BlendMode = DefaultBlendMode
    )
  • points: the offset position of the point
  • pointMode: Point drawing mode, PointMode.Pointsdraw each point separately. PointMode.LinesEvery two points are drawn as a line segment. If the number of points is odd, the last point is ignored. PointMode.PolygonConnect all the dots.

Finally, there is another method of drawing an ellipse drawOval. The usage is similar, so I won’t explain it here.

3. DrawScope extension method

1. inset

At the same time transform DrawScopethe coordinate space from left to top and modify the size of the current drawing area.

inline fun DrawScope.inset(
    left: Float,
    top: Float,
    right: Float,
    bottom: Float,
    block: DrawScope.() -> Unit
) {
    
    ...}

insetIt's a bit like embedding a "new" canvas on the original canvas, and the left and top are set to be the corresponding padding.

	Canvas(modifier = Modifier.fillMaxSize()){
    
    
        drawRect(
            color = Color.Blue,
        )
        inset(100f, 100f, 100f, 100f) {
    
    
            drawRect(
                color = Color.Red,
            )
        }
    }

insert image description here

2. translate

Pan the drawing area

inline fun DrawScope.translate(
    left: Float = 0.0f,
    top: Float = 0.0f,
    block: DrawScope.() -> Unit
) {
    
    ...}

You only need to set the moving distance in the left and top directions.

3. rotate、rotateRad

Rotate drawing area

inline fun DrawScope.rotate(
    degrees: Float,
    pivot: Offset = center,
    block: DrawScope.() -> Unit
) {
    
    ...}

inline fun DrawScope.rotateRad(
    radians: Float,
    pivot: Offset = center,
    block: DrawScope.() -> Unit
) {
    
    ...}
  • degreesis the angle of rotation.
  • radiansis how many radians to rotate.
  • pivotis the center point of the rotation, the default is the center.

4. scale

Zoom the drawing area.

inline fun DrawScope.scale(
    scaleX: Float,
    scaleY: Float,
    pivot: Offset = center,
    block: DrawScope.() -> Unit
) {
    
    ...}

Just specify the zoom factor in the x and y directions.

5. clipRect

crop the given rectangular area

inline fun DrawScope.clipRect(
    left: Float = 0.0f,
    top: Float = 0.0f,
    right: Float = size.width,
    bottom: Float = size.height,
    clipOp: ClipOp = ClipOp.Intersect,
    block: DrawScope.() -> Unit
) {
    
    ...}
  • clipOp: ClipOp.IntersectIt is the inside of the clipping rectangle and ClipOp.Differencethe outside of the clipping rectangle.

Let's look at a simple example for your understanding:

	Canvas(modifier = Modifier.fillMaxSize()){
    
    
        drawRect(
            color = Color.Blue,
        )
        clipRect(200f, 200f, clipOp = ClipOp.Intersect) {
    
    
            drawRect(
                color = Color.Yellow,
            )
        }
    }

left is ClipOp.Intersect, right isClipOp.Difference
insert image description here

clipPaththe same way.

6. drawIntoCanvas

You can directly call the underlying Canvas drawing method. We use it to implement the drawLine example at the beginning, drawing a diagonal line:

	Canvas(modifier = Modifier.fillMaxSize()) {
    
    
        val canvasWidth = size.width
        val canvasHeight = size.height
        drawIntoCanvas {
    
    
            val paint = Paint()
            paint.color = Color.Blue
            paint.strokeWidth = 1f
            it.drawLine(
                p1 = Offset(canvasWidth,0f),
                p2 = Offset(0f,canvasHeight),
                paint = paint
            )
        }
    }

Among them drawLine, the method is not DrawScopein the beginning drawLine:

actual typealias NativeCanvas = android.graphics.Canvas
private val EmptyCanvas = android.graphics.Canvas()

@PublishedApi internal class AndroidCanvas() : Canvas {
    
    

    @PublishedApi internal var internalCanvas: NativeCanvas = EmptyCanvas
	
	override fun drawLine(p1: Offset, p2: Offset, paint: Paint) {
    
    
        internalCanvas.drawLine(
            p1.x,
            p1.y,
            p2.x,
            p2.y,
            paint.asFrameworkPaint()
        )
    }
}

You can see that Android's Canvas api is finally called.

7. withTransform

Perform 1 or more transformations. That is, the above translation and rotation can be performed together.

inline fun DrawScope.withTransform(
    transformBlock: DrawTransform.() -> Unit,
    drawBlock: DrawScope.() -> Unit
) {
    
    ...}

This is the end of this article, the next article should be the animation part of compose, so stay tuned~

4. Reference

Guess you like

Origin blog.csdn.net/qq_17766199/article/details/124517391