Я хотел бы создать дизайн, похожий на картинку ниже.
Я просмотрел фигуру в Android Jetpack Compose, но не смог ее найти. Я не уверен, какие представления мне следует использовать для создания этого макета.
Я хочу, чтобы коробка с верхней правой стороной была круглой, как на изображении.
Обычно для реализации таких компонентов пользовательских фигур следует использовать векторы .svg. В случае создания реактивного ранца для рисования фона за содержимым произвольной формы вам следует использовать модификатор .drawBehind, примененный к вашему содержимому, завернутому в Box. Пример кода:
val painter = rememberVectorPainter(image = vector)
Box(modifier = Modifier.drawBehind{
with(painter) {
draw(painter.intrinsicSize)
}
}) {
your_content
}
Чтобы создать дизайн, подобный изображенному на картинке, вы можете использовать нестандартную форму. Вот как вы можете это сделать:
1. Создайте собственную фигуру:
val shape = remember {
GenericShape { size, _ ->
val width = size.width
val height = size.height
moveTo(0f, radius)
quadraticBezierTo(0f, 0f, radius, 0f)
lineTo(width - cornerRadius - radius, 0f)
quadraticBezierTo(width - cornerRadius, 0f, width - cornerRadius, radius)
quadraticBezierTo(width - 1.2f*cornerRadius, 1.2f*cornerRadius, width - radius, cornerRadius)
quadraticBezierTo(width, cornerRadius, width, cornerRadius + radius)
lineTo(width, height - radius)
quadraticBezierTo(width, height, width - radius, height)
lineTo(radius, height)
quadraticBezierTo(0f, height, 0f, height - radius)
lineTo(0f, radius)
}
}
2. Примените форму к Card
:
Card(
modifier = Modifier.fillMaxSize(),
shape = shape
) {
content()
}
3. Добавьте циркуляр Box
:
Box(
modifier = Modifier
.size(cornerRadiusDp - padding)
.align(Alignment.TopEnd)
.background(
color = CardDefaults.cardColors().containerColor,
shape = CircleShape
)
)
4. Соедините Card
и круговую Box
вместе в родительском элементе Box
:
Box(modifier = modifier) {
Card(
modifier = Modifier.fillMaxSize(),
shape = shape
) {
content()
}
Box(
modifier = Modifier
.size(cornerRadiusDp - padding)
.align(Alignment.TopEnd)
.background(
color = CardDefaults.cardColors().containerColor,
shape = CircleShape
)
)
}
Вот полный пример:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.GenericShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CustomCard(modifier = Modifier
.width(300.dp)
.height(250.dp)
) {
val price = buildAnnotatedString {
withStyle(SpanStyle(color = Color.Green)) { append("$") }
append("0.00")
}
val labelStyle = remember {
TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Light)
}
val priceStyle = remember {
TextStyle(fontSize = 18.sp, fontWeight = FontWeight.Bold)
}
Column(
modifier = Modifier.padding(16.dp)
) {
Text(text = "Today", style = labelStyle)
Text(text = price, style = priceStyle)
Spacer(modifier = Modifier.weight(1f))
Text(text = "Yesterday", style = labelStyle)
Text(text = price, style = priceStyle)
Spacer(modifier = Modifier.weight(1f))
Text(text = "Past Week", style = labelStyle)
Text(text = price, style = priceStyle)
}
}
}
}
}
}
@Composable
fun CustomCard(
modifier: Modifier = Modifier,
content: @Composable () -> Unit,
) {
val radius = with(LocalDensity.current) { 10.dp.toPx() }
val cornerRadiusDp = 50.dp
val cornerRadius = with(LocalDensity.current) { cornerRadiusDp.toPx() }
val padding = 5.dp
// Create a custom shape
val shape = remember {
GenericShape { size, _ ->
val width = size.width
val height = size.height
moveTo(0f, radius)
quadraticBezierTo(0f, 0f, radius, 0f)
lineTo(width - cornerRadius - radius, 0f)
quadraticBezierTo(width - cornerRadius, 0f, width - cornerRadius, radius)
quadraticBezierTo(width - 1.2f*cornerRadius, 1.2f*cornerRadius, width - radius, cornerRadius)
quadraticBezierTo(width, cornerRadius, width, cornerRadius + radius)
lineTo(width, height - radius)
quadraticBezierTo(width, height, width - radius, height)
lineTo(radius, height)
quadraticBezierTo(0f, height, 0f, height - radius)
lineTo(0f, radius)
}
}
Box(modifier = modifier) {
Card(
modifier = Modifier.fillMaxSize(),
shape = shape
) {
content()
}
Box(
modifier = Modifier
.size(cornerRadiusDp - padding)
.align(Alignment.TopEnd)
.background(
color = CardDefaults.cardColors().containerColor,
shape = CircleShape
)
)
}
}
Демо:
Другой пример см. в этом ответе.
спасибо, братан. Он работает идеально. <3
Вы можете изучить использование Canvas и его функций, например drawAct()
drawOval()
и т. д. Проверьте документ в Google: https://developer.android.com/develop/ui/compose/graphics/draw/overview
братан, я знаю, но .SVG невозможен, поэтому я спрашиваю об этом здесь, иначе я бы использовал его.