I wanna have this layout in Jetpack Compose. How can I place the image at the bottom of the card and have it a bit outside the card borders? Also how to make sure that the content above won't collide with the image, because the content above can be longer. Thanks very much.
A simple way is to use a Box and apply an Offset to the Image
Box() {
val overlayBoxHeight = 40.dp
Card(
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
) {
//...
}
Image(
painterResource(id = R.drawable.xxxx),
contentDescription = "",
modifier = Modifier
.align(BottomCenter)
.offset(x = 0.dp, y = overlayBoxHeight )
)
}
If you want to calculate the offset you can use a Layout composable.
Something like:
Layout( content = {
Card(
elevation = 10.dp,
modifier = Modifier
.layoutId("card")
.fillMaxWidth()
.height(300.dp)
) {
//...
}
Image(
painterResource(id = R.drawable.conero),
contentDescription = "",
modifier = Modifier
.layoutId("image")
)
}){ measurables, incomingConstraints ->
val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
val cardPlaceable =
measurables.find { it.layoutId == "card" }?.measure(constraints)
val imagePlaceable =
measurables.find { it.layoutId == "image" }?.measure(constraints)
//align the icon on the top/end edge
layout(width = widthOrZero(cardPlaceable),
height = heightOrZero(cardPlaceable)+ heightOrZero(imagePlaceable)/2){
cardPlaceable?.placeRelative(0, 0)
imagePlaceable?.placeRelative(widthOrZero(cardPlaceable)/2 - widthOrZero(imagePlaceable)/2,
heightOrZero(cardPlaceable) - heightOrZero(imagePlaceable)/2)
}
}
You can use Card for main view layout.
To have an icon placed like this, you need to place it with Card into a box, apply Alignment.BottomCenter alignment to image and add paddings.
To make image be under your text, I've added Spacer with size calculated on image size.
#Composable
fun TestView() {
CompositionLocalProvider(
LocalContentColor provides Color.White
) {
Column {
LazyRow {
items(10) {
Item()
}
}
Box(Modifier.fillMaxWidth().background(Color.DarkGray)) {
Text("next view")
}
}
}
}
#Composable
fun Item() {
Box(
Modifier
.padding(10.dp)
) {
val imagePainter = painterResource(id = R.drawable.test2)
val imageOffset = 20.dp
Card(
shape = RoundedCornerShape(0.dp),
backgroundColor = Color.DarkGray,
modifier = Modifier
.padding(bottom = imageOffset)
.width(200.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(20.dp)
) {
Text(
"Title",
modifier = Modifier.align(Alignment.Start)
)
Spacer(modifier = Modifier.size(15.dp))
Text("PM2.5")
Text(
"10",
fontSize = 35.sp,
)
Text("m/s")
Spacer(modifier = Modifier.size(15.dp))
Text(
"Very Good",
fontSize = 25.sp,
)
Spacer(
modifier = Modifier
.size(
with(LocalDensity.current) { imagePainter.intrinsicSize.height.toDp() - imageOffset }
)
)
}
}
Image(
painter = imagePainter,
contentDescription = "",
Modifier
.align(Alignment.BottomCenter)
)
}
}
Result:
Related
The following function is called from Navigation graph.
#Composable
fun InformationScreen(
) {
Scaffold(
topBar = {
Box(modifier = Modifier.fillMaxWidth()) {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
IconButton(
onClick = { /*...*/ },
modifier = Modifier
.padding(horizontal = 20.dp, vertical = 20.dp)
.fillMaxWidth()
) {
Icon(
Icons.Filled.Close,
contentDescription = stringResource(id = R.string.close),
modifier = Modifier.align(Alignment.CenterEnd)
)
}
}
}
},
content = { innerPadding ->//}
}
Should I add a theme to this compose function? How to apply it and make sure the app these will not change? thx
You can add background image to a Box just by placing it under all other views. And matchParentSize modifier will stretch it to the parent size.
Box(modifier = Modifier.fillMaxWidth()) {
Image(
painterResource(id = R.drawable.ic_launcher_background),
contentDescription = "",
contentScale = ContentScale.FillBounds, // or some other scale
modifier = Modifier.matchParentSize()
)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
IconButton(
onClick = { /*...*/ },
modifier = Modifier
.padding(horizontal = 20.dp, vertical = 20.dp)
.fillMaxWidth()
) {
Icon(
Icons.Filled.Close,
contentDescription = "stringResource(id = R.string.close)",
modifier = Modifier.align(Alignment.CenterEnd)
)
}
}
}
This is how it looks when I hardcode the text inside the cards:
This is how it looks when I use my data from the viewModel, but it only has this issue after changing the sorting method of the list and it only happens in the middle of the list:
Here is the Code of the two Composables:
#Composable
fun BookShelfList(
navController: NavController,
viewModel: BookShelfViewModel,
toolbarHeight: Dp
) {
val bookList by remember { viewModel.books }
LazyColumn(
contentPadding = PaddingValues(
start = 16.dp,
top = toolbarHeight + 16.dp,
end = 16.dp,
bottom = 16.dp
),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
var items = bookList
if (viewModel.sortingMethod.value == "A-Z")
items = bookList.sortedWith(compareBy { it.title })
items(items) { book ->
BookShelfListItem(navController, book)
}
}
}
#Composable
fun BookShelfListItem(navController: NavController, book: Book) {
Card(
elevation = 4.dp,
modifier = Modifier
.fillMaxWidth()
.clickable { navController.navigate("second_screen") }
) {
Row(
modifier = Modifier
.height(120.dp)
) {
Image(
modifier = Modifier.width(90.dp),
painter = painterResource(id = R.drawable.book_cover_placeholder),
contentDescription = "Book cover",
contentScale = ContentScale.Crop
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Text(
text = book.title,
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
How do I create a rounded checkbox in Jetpackcompose like this. I tried using a Shape composable on it but it doesn't work.
I was looking on how to do the same thing you were asking, your question helped me on my journey so it is only fair I share. Add some animations and you are set my friend.
Make a round looking icon by using a box and an icon
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
2.Place the newly made rounded icon and some text next to each other by using a Row
Row(
verticalAlignment = Alignment.CenterVertically,
){
Box(
modifier = Modifier
.clip(CircleShape)
.size(40.dp)
.background(Color.Black)
.padding(3.dp)
.clip(CircleShape)
.background(Color.White),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
3.Replace whatever you want with variables so you can customize
it
#Composable
fun RoundedCheckView(
) {
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
{
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
4.Finally add Modifier.toggleable to the row, basically making it a clickable item that toggles (between true and false) a variable in this case isChecked. Then just customize the variables according to what you need
#Composable
fun RoundedCheckView()
{
val isChecked = remember { mutableStateOf(false) }
val checkedText = remember { mutableStateOf("unChecked") }
val circleSize = remember { mutableStateOf(20.dp) }
val circleThickness = remember { mutableStateOf(2.dp) }
val color = remember { mutableStateOf(Color.Gray) }
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.toggleable(value = isChecked.value,role = Role.Checkbox) {
isChecked.value = it
if (isChecked.value) {
checkedText.value = "Checked"
circleSize.value = 40.dp
circleThickness.value = 3.dp
color.value = Color.Black
} else {
checkedText.value = "unChecked"
circleSize.value = 20.dp
circleThickness.value = 2.dp
color.value = Color.Gray
}
}) {
Box(
modifier = Modifier
.clip(CircleShape)
.size(circleSize.value)
.background(color.value)
.padding(circleThickness.value)
.clip(CircleShape)
.background(Color.White) ,
contentAlignment = Alignment.Center
) {
if(isChecked.value){
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
Text(
text = checkedText.value,
color = color.value,
fontSize = 20.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(start = 5.dp)
)
}
}
This is how we can make a custom check box in jetpack compose
val isCheck = remember { mutableStateOf(false) }
Row {
Card(
modifier = Modifier.background(Color.White),
elevation = 0.dp,
shape = RoundedCornerShape(6.dp),
border = BorderStroke(1.5.dp, color = titleColor)
) {
Box(
modifier = Modifier
.size(25.dp)
.background(if (isCheck.value) titleColor else Color.White)
.clickable {
isCheck.value = !isCheck.value
},
contentAlignment = Center
) {
if(isCheck.value)
Icon(Icons.Default.Check, contentDescription = "", tint = Color.White)
}
}
Text(
modifier = Modifier
.align(CenterVertically)
.padding(start = 10.dp),
text = "I agree with the terms & condition",
)
}
You can try to make it use Box with modifier content alignment center. and put an icon on there.
#Preview
#Composable
fun Check() {
Box(
modifier = Modifier
.clip(CircleShape)
.size(50.dp)
.background(Color.Red)
.padding(5.dp)
.clip(CircleShape)
.background(Color.Blue),
contentAlignment = Alignment.Center
) {
Icon(imageVector = Icons.Default.Check, contentDescription = "")
}
}
We can use animations to make it behave similar to the default Checkbox. Using Modifier.toggleable on the top-level Row, the entire thing is clickable, including the label. This also creates the proper semantics for screen reader users. You can change the shape of the card to get a circular checkbox.
#Composable
fun PrimaryCheckbox(
label: String,
modifier: Modifier = Modifier,
size: Float = 24f,
checkedColor: Color = DarkGray,
uncheckedColor: Color = White,
checkmarkColor: Color = White,
onValueChange: () -> Unit
) {
var isChecked by remember { mutableStateOf(false) }
val checkboxColor: Color by animateColorAsState(if (isChecked) checkedColor else uncheckedColor)
val density = LocalDensity.current
val duration = 200
Row(
modifier = modifier
.toggleable(
value = isChecked,
role = Role.Checkbox,
onValueChange = {
isChecked = !isChecked
onValueChange.invoke()
}
)
) {
Card(
elevation = 0.dp,
shape = RoundedCornerShape(4.dp),
border = BorderStroke(1.5.dp, color = checkedColor),
) {
Box(
modifier = Modifier
.size(size.dp)
.background(checkboxColor),
contentAlignment = Alignment.Center
) {
androidx.compose.animation.AnimatedVisibility(
visible = isChecked,
enter = slideInHorizontally(
animationSpec = tween(duration)
) {
with(density) { (size * -0.5).dp.roundToPx() }
} + expandHorizontally(
expandFrom = Alignment.Start,
animationSpec = tween(duration)
),
exit = fadeOut()
) {
Icon(
Icons.Default.Check,
contentDescription = null,
tint = checkmarkColor
)
}
}
}
Text(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(start = 8.dp),
text = label,
)
}
}
I want to make an Image with Text on top, just like Google CLassroom. But first I want to test Image and then Text. Instead, I got the image overlapping the text. Image Overlapping text
Then I move the Image code after the text. How to get simple G classroom format
Text then Image
#Composable
fun ClassImage(
// icon: VectorAsset,
// label: String,
// modifier: Modifier = Modifier
) {
val imageAlpha = 1f
Surface(
modifier = Modifier
.padding(start = 8.dp, top = 8.dp, end = 8.dp)
.fillMaxWidth(),
color = colors.primary.copy(alpha = 0.12f)
) {
TextButton(
onClick = {},
modifier = Modifier.fillMaxWidth()
) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth())
{
Image(
imageResource(id = R.drawable.class1),
alpha = imageAlpha
)
Column {
Text("Alfred Sisley", fontWeight = FontWeight.Bold)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}
}
}
}
To put Text on top of the Image you can use Box, which is similar to old FrameLayout.
I'm not sure want you wanted to achieve, but if smth like this:
Then you can do it this way:
Surface(
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.preferredHeight(128.dp)
.clickable(onClick = {})
) {
Box {
Image(
vectorResource(id = R.drawable.ic_launcher_background),
alpha = imageAlpha,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(modifier = Modifier.padding(16.dp)) {
Text(
"Alfred Sisley",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h6)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
Spacer(modifier = Modifier.weight(1f))
Text(text = "Footer", style = MaterialTheme.typography.body1)
}
}
}
With 1.0.0-beta02 you can use a Box as parent container.
Something like:
Box(modifier = Modifier.height(IntrinsicSize.Max))
{
Image(
painterResource(id = R.drawable.xx),
"contentDescription",
alpha = 0.8f,
modifier = Modifier.requiredHeight(100.dp)
)
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Bottom) {
Text("Alfred Sisley",
fontWeight = FontWeight.Bold)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}
I want to create Cardview using jetpack compose but i am not able to find any
example.
You can use something like:
Card(
shape = RoundedCornerShape(8.dp),
backgroundColor = MaterialTheme.colors.surface,
) {
Column(
modifier = Modifier.height(200.dp).padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
){
Text(text = "This is a card view",
style = MaterialTheme.typography.h4)
}
}
With Material3 you can use:
Card(
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = /* ... */)
)
in v0.1.0-dev09 version, can be done like this, where the padding of Card sets the margin of the card, the padding of Box sets the padding inside the card.
Card(
shape = RoundedCornerShape(8.dp),
modifier = Modifier.padding(16.dp).fillMaxWidth()
) {
Box(modifier = Modifier.height(200.dp).padding(16.dp)) {
Text("This is a card view")
}
}
As the friends recommended, Card is a way of creating CardView but you can do that by surface too :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Text(text = "Sample text")
}
Note that Surface and Card cannot be used for positioning items so that if you wanna position that Text(text = "Sample text") , you should use one layout inside that Surface like this :
val shape = RoundedCornerShape(10.dp)
Surface(modifier = Modifier.size(250.dp, 70.dp), elevation = 8.dp, shape = shape) {
Box(modifier = Modifier.fillMaxSize()) {
Text(modifier = Modifier.align(Alignment.Center), text = "Sample text")
}
}
The appropriate way for creating CardView is this but if you wanna just create shadow for a view, you can use Modifier.shadow (Note that Modifier.shadow and Surface/Card are not the same):
Box(modifier = Modifier.size(250.dp, 70.dp).shadow(8.dp, RoundedCornerShape(10.dp)), contentAlignment = Alignment.Center) {
Text(text = "Sample Text")
}
Code:
class ImageCardActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val painter = painterResource(id = R.drawable.grogu)
val contentDescription = "Grogu says hi"
val title = "Force is strong with Grogu"
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier.fillMaxWidth(0.5f)
) {
ImageCard(
title = title,
contentDescription = contentDescription,
painter = painter
)
}
}
}
}
}
#Composable
fun ImageCard(
title: String,
contentDescription:String,
painter: Painter,
modifier:Modifier=Modifier
){
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(18.dp),
elevation = 5.dp
) {
Box(
modifier = Modifier.height(200.dp)
) {
// Image
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
// Gradient
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent, Color.Black
),
startY = 300f
)
)
)
// Title
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
contentAlignment = Alignment.BottomStart
) {
Text(
text = title,
style = TextStyle(color = Color.White, fontSize = 12.sp)
)
}
}
}
}