zoha131
10/19/2020, 1:21 PMimport androidx.compose.animation.animate
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
import androidx.compose.foundation.Text
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Layout
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.ui.tooling.preview.Preview
@Composable
fun CategoryButton(modifier: Modifier = Modifier) {
val (checked, setChecked) = remember { mutableStateOf(false) }
Surface(
modifier = modifier
.clip(RoundedCornerShape(4.dp))
.clickable {
setChecked(!checked)
},
shape = RoundedCornerShape(4.dp)
) {
CategoryButtonLayout(isChecked = checked, modifier = modifier)
}
}
@Composable
private fun CategoryButtonLayout(isChecked: Boolean, modifier: Modifier) {
Layout(
children = { CategoryButtonContent(isChecked) },
modifier = modifier,
measureBlock = { miserables, constraints ->
val width = constraints.maxWidth
val height = 48.dp.toIntPx()
val canvasConstraints = Constraints(
maxHeight = height,
minHeight = height,
maxWidth = width,
minWidth = width
)
val iconSize = 24.dp.toIntPx()
val iconConstraints = Constraints(
minHeight = iconSize,
maxHeight = iconSize,
minWidth = iconSize,
maxWidth = iconSize
)
val canvasPlaceable = miserables[0].measure(canvasConstraints)
val iconPlaceable = miserables[1].measure(iconConstraints)
val textPlaceable = miserables[2].measure(constraints)
layout(width, height) {
canvasPlaceable.place(0, 0)
iconPlaceable.place(8, height / 2 - iconPlaceable.height / 2)
textPlaceable.place(iconSize, height / 2 - textPlaceable.height / 2)
}
}
)
}
@Composable
private fun CategoryButtonContent(isChecked: Boolean) {
val textColor = if (isChecked) Color.White else Color.Black
val textColorAnimated = animate(textColor)
val radius = if (isChecked) 550.dp else 4.dp
val radiusAnimated = animate(radius)
Canvas(
modifier = Modifier.fillMaxSize(),
onDraw = {
val left = size.width / 2
// To move the center of the circle
translate(left, 0f) {
drawCircle(color = Color.Red, radius = radiusAnimated.toIntPx().toFloat())
}
drawLine(
color = Color.Red,
start = Offset(size.width, 0f),
end = Offset(size.width - 8.dp.toIntPx().toFloat(), 0f),
strokeWidth = size.width
)
}
)
Image(
asset = Icons.Filled.Close,
colorFilter = ColorFilter.tint(textColorAnimated),
contentScale = ContentScale.Fit
)
Text(
style = MaterialTheme.typography.button.copy(color = textColorAnimated),
text = "COVID 19",
maxLines = 1
)
}
@Preview
@Composable
fun CategoryButtonPreview() {
Box(
Modifier.fillMaxWidth()
) {
CategoryButton()
}
}
@Preview
@Composable
fun CategoryButtonRowBoxPreview() {
Row(
Modifier.fillMaxWidth()
) {
Box(
Modifier.fillMaxWidth().weight(1f)
) {
CategoryButton()
}
Box(
Modifier.fillMaxWidth().weight(1f)
) {
CategoryButton()
}
}
}
@Preview
@Composable
fun CategoryButtonRowPreview() {
Row(
Modifier.fillMaxWidth()
) {
CategoryButton(
modifier = Modifier.fillMaxWidth().weight(1f)
)
CategoryButton(
modifier = Modifier.fillMaxWidth().weight(1f)
)
}
}
Afzal Najam
10/19/2020, 1:36 PMZach Klippenstein (he/him) [MOD]
10/19/2020, 1:57 PMtoggleable
modifier instead of clickable
- it will give you the correct semantics.
2. For "tight" constraints where min = max, you can use the Constraints.fixed(width, height)
factory function.
3. In custom layouts, it's better to use placeRelative
than place
, so you get automatic RTL support.
4. Shouldn't you be adding 8 to your text x to account for the spacing you give the icon?
5. I don't think it makes sense to use fillMaxWidth()
and weight()
together, since they both control width. I believe fillMaxWidth is unnecessary and ends up getting ignored, so I would remove it.textAlign
property to your Text, or a Modifier.wrapContentWidth(Start)
?zoha131
10/19/2020, 2:41 PMModifier.wrapContentWidth(Start)
worked for me. thanks for help.