I haven’t written an article for a long time. I plan to write one. I hope I can get your appreciation

For more than half a month, I learned Jetpack Compose by myself

rendering

Implement separation of ideas

  1. Divide the square into 4 parts to determine the center of the 4 symbols

BoxWithConstraints(modifier = modifier) {
    val circleSizeDp = minOf(maxWidth, maxHeight)
    val density = LocalDensity.current.density
    val circleSizePx = circleSizeDp.value * density
    // Divide 4 equally
    val radius = circleSizePx / 4
    / / right and bottom x, y
    val centerOffset = radius * 3
    
    // Add center point
    var plusOffset by remember { mutableStateOf(Offset(radius, radius)) }
    // Minus center point
    var minusOffset by remember { mutableStateOf(Offset(centerOffset, radius)) }
    // Multiply the center point
    var timesOffset by remember { mutableStateOf(Offset(centerOffset, centerOffset)) }
    // divide the center point
    var divOffset by remember { mutableStateOf(Offset(radius, centerOffset)) }
   
}   
Copy the code
  1. Draw symbols according to the center points of the four symbols
     // Symbol length
     val offset = radius / 2 + 15.dp.value
     Canvas(modifier = modifier.requiredSize(size = circleSizeDp)) {
           / / plus
            drawLine(
                color = lineColor,
                start = Offset(plusOffset.x - offset, plusOffset.y),
                end = Offset(plusOffset.x + offset, plusOffset.y),
                strokeWidth = strokeWidth,
                cap = StrokeCap.Round,
            )

            drawLine(
                color = lineColor,
                start = Offset(plusOffset.x, plusOffset.y - offset),
                end = Offset(plusOffset.x, plusOffset.y + offset),
                strokeWidth = strokeWidth,
                cap = StrokeCap.Round,
            )
            
            / / minus sign
            drawLine(
                color = lineColor,
                start = Offset(minusOffset.x - offset, minusOffset.y),
                end = Offset(minusOffset.x + offset, minusOffset.y),
                strokeWidth = strokeWidth,
                cap = StrokeCap.Round,
            )
            / /]
            rotate(degrees = 45F, pivot = timesOffset) {
                drawLine(
                    color = lineColor,
                    start = Offset(timesOffset.x - offset, timesOffset.y),
                    end = Offset(timesOffset.x + offset, timesOffset.y),
                    strokeWidth = strokeWidth,
                    cap = StrokeCap.Round,
                )
            }
            rotate(degrees = 135F, pivot = timesOffset) {
                drawLine(
                    color = lineColor,
                    start = Offset(timesOffset.x - offset, timesOffset.y),
                    end = Offset(timesOffset.x + offset, timesOffset.y),
                    strokeWidth = strokeWidth,
                    cap = StrokeCap.Round,
                )
            }
	    / / devide
            drawLine(
                color = lineColor,
                start = Offset(divOffset.x - offset, divOffset.y),
                end = Offset(divOffset.x + offset, divOffset.y),
                strokeWidth = strokeWidth,
                cap = StrokeCap.Round,
            )
            // Divide by 2 dots
            drawCircle(
                color = lineColor,
                style = Fill,
                radius = circleRadius,
                center = Offset(divOffset.x, divOffset.y - radius / 3)
            )
            drawCircle(
                color = lineColor,
                style = Fill,
                radius = circleRadius,
                center = Offset(divOffset.x, divOffset.y + radius / 3))}Copy the code

Static drawing effect

  1. Use animation to move

Form a square based on the center points of the 4 symbols, each offset is the length of the side of the square

Use the rememberInfiniteTransition () infinite loop animations 0 to square side length of the animation operation performed continuously Changing the center of the four symbols

// Move the length
val animateSize = radius * 2
// Count the rotations
var currentCount by remember { mutableStateOf(0)}/ / rememberInfiniteTransition infinite animation ()
val animateValue by rememberInfiniteTransition().animateFloat(
    initialValue = 0f,
    targetValue = animateSize,
    // Keyframes are computed in time segments
    // LinearEasing smoothen the transition
    animationSpec = infiniteRepeatable(
        animation = keyframes {
            durationMillis = 800
            0f at 80 with LinearEasing
            0.1 f * animateSize at 150 with LinearEasing
            0.2 f * animateSize at 200 with LinearEasing
            0.3 f * animateSize at 250 with LinearEasing
            0.4 f * animateSize at 300 with LinearEasing
            0.5 f * animateSize at 400 with LinearEasing
            0.6 f * animateSize at 500 with LinearEasing
            0.7 f * animateSize at 600
            0.8 f * animateSize at 700
            0.9 f * animateSize at 750
            animateSize at 800
        },
        repeatMode = RepeatMode.Restart
    )
)
// Monitor animation result changes on 4 breaks
LaunchedEffect(animateValue) {
    // Use animateValue ==0 to determine each re-execution of the animation
    if (animateValue == 0f) {
        // Each time you start over, you add up 1
        currentCount += 1
        if (currentCount > 4) {
            currentCount = 1}}val plus = radius + animateValue
    val minus = centerOffset - animateValue
    // Mark the stage of the animation according to currentCount
    when (currentCount) {
        1- > {// The plus sign goes from left to right
            plusOffset = Offset(plus, radius)
            minusOffset = Offset(centerOffset, plus)

            timesOffset = Offset(minus, centerOffset)
            divOffset = Offset(radius, minus)
        }
        2- > {// The plus sign goes down from the right
            plusOffset = Offset(centerOffset, plus)
            minusOffset = Offset(minus, centerOffset)

            timesOffset = Offset(radius, minus)
            divOffset = Offset(plus, radius)
        }
        3- > {// The plus sign goes from bottom to left
            plusOffset = Offset(minus, centerOffset)
            minusOffset = Offset(radius, minus)

            timesOffset = Offset(plus, radius)
            divOffset = Offset(centerOffset, plus)
        }
        4 -> {
            plusOffset = Offset(radius, minus)
            minusOffset = Offset(plus, radius)

            timesOffset = Offset(centerOffset, plus)
            divOffset = Offset(minus, centerOffset)
        }
    }
}
Copy the code

Animation to realize the process is a little pain, the Compose of animation no better support to the fine-grained monitoring rememberInfiniteTransition () is an infinite loop animation, But there is no animation Restart, start, end exposed monitoring interface at the same time the difference can not meet the needs, can only be calculated through keyframes bit by bit if there are workers have a good way also hope not stingy inform here is basically completed

extension

Use ModifierdrawWithContent to implement red dot prompt for unread messages

fun Modifier.redPoint(num: String): Modifier = drawWithContent {
    drawContent()
    drawIntoCanvas {
        val padding = 6.dp.toPx()
        val topPadding = 3.dp.toPx()

        val paint = Paint().apply {
            color = Color.Red
        }
        val paintTextSize= 14.sp.toPx()
        // Draw text using FrameworkPaint
        val textPaint = Paint().asFrameworkPaint().apply {
            isAntiAlias = true
            isDither = true
            color=Color.White.toArgb()
            textSize = paintTextSize
            typeface = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL)
            textAlign = android.graphics.Paint.Align.CENTER
        }
        // Measure the width of the text
        val textWidth = textPaint.measureText(num)

        val radius =20.dp.toPx()
        val offset=(textWidth+padding*2)
        // Draw the background
        it.drawRoundRect(
            left = size.width-offset,
            top = 0f,
            right = size.width,
            bottom = radius,
            radiusX= 10.dp.toPx(),
            radiusY= 10.dp.toPx(),
            paint = paint
        )
        // Draw text
        it.nativeCanvas.drawText(num, size.width-offset/2, radius-(radius-paintTextSize)/2-topPadding, textPaint)
    }
}
Copy the code

call

@Composable
fun ImageDemo(a) {
        Image(
            painter = painterResource(id = R.drawable.message),
            contentDescription = "",
            modifier = Modifier
                .size(width = 56.dp, height = 56.dp)
                .redPoint("99"),
            contentScale = ContentScale.FillBounds, 
            alignment = Alignment.CenterEnd,
        )
}
Copy the code