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
- 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
- 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
- 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