Jetpack Compose with Resizable Background Image - android

Currently, I'm a bit stuck on having a resizable drawable in the background of a card.
Card(
modifier,
shape = RoundedCornerShape(8.dp),
elevation = 6.dp
) {
Image(
painterResource(...),
contentScale = ContentScale.FillWidth,
contentDescription = null,
modifier = Modifier.fillMaxWidth()
)
Box() {
Surface(
color = Color.Transparent,
contentColor = Color.White
) {
Column(Modifier.padding(24.dp)) {
Spacer(Modifier.height(16.dp))
Text(...)
Spacer(Modifier.height(16.dp))
Text(...)
Spacer(Modifier.height(8.dp))
Text(...)
Spacer(Modifier.height(16.dp))
Button(...))
}
}
}
}
What I really am trying to get is the image to resize accordingly to the contents of the card...but not doing that. Basically, it doesn't matter if the image is cropped, but I'm trying to make it fill the card. Right now, since I have FillWidth, on bigger screens like tablets, there is a large unused space at the bottom of the card. Any help would be much appreciated!
Tried replacing the whole card with a box instead, didn't work, different permutations of contentscale also did not help.

Related

Jetpack Compose - Setting dimensions for Accompanist placeholder

The library I'm using: "com.google.accompanist:accompanist-placeholder-material:0.23.1"
I want to display a placeholder in the place of (or over) a component when it's in the loading state.
I do the following for a Text:
MaterialTheme() {
var placeholderVisible by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
while (true) {
delay(1000)
placeholderVisible = !placeholderVisible
}
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.border(1.dp, Color.Red)
.padding(16.dp)
) {
Text(
modifier = Modifier
.then(
if (placeholderVisible) {
Modifier.height(28.dp).width(62.dp)
} else {
Modifier
}
)
.placeholder(
visible = placeholderVisible,
highlight = PlaceholderHighlight.shimmer()
),
text = if (placeholderVisible) "" else "Hello"
)
}
}
}
And I get this:
I want instead that no matter how big I set the placeholder's height or width, it will not participate in any way in the measuring process and, if I want to, to be able to draw itself even over other components (in this case let's say the red border).
As an effect of what I want, the box with red border will always have the dimension as if that Modifier.height(28.dp).width(62.dp) is not there.
I know I can draw outside a component's borders using drawWithContent, specifying the size of a rectangle or a circle (or whatever) to be component's size + x.dp.toPx() (or something like that). But how do I do this with Modifier.placeholder?
Ideally, I would need something like Modifier.placeholder(height = 28.dp, width = 62.dp)
So, with or without this ideal Modifier, the UI should never change (except, of course, the shimmer box that may be present or not).
I think I can pull this off by modifying the source code of this Modifier, but I hope I won't need to turn to that.
Just replace your Text() with below code, maybe conditional Modifier is the issue in above code!
Text(
modifier = Modifier
.size(width = 62.dp, height = 28.dp)
.placeholder(
visible = placeholderVisible,
highlight = PlaceholderHighlight.shimmer()
),
text = if (placeholderVisible) "" else "Hello",
textAlign = TextAlign.Center
)

Jetpack compose how to blur text area and line it to the bottom of a card

I want to place text at the bottom of a card and blur the text area with a translucent color.
This is what I have so far.
The design I am trying to achieve is this. (Collection Area with cards specific part)
Here's my code:
#Composable
fun CollectionCardArea(
collection: Collection,
cardWidth: Dp
) {
Card(
modifier = Modifier
.width(cardWidth)
.padding(start = 2.dp, end = 25.dp, bottom = 5.dp, top = 0.dp)
.clickable { },
shape = RoundedCornerShape(6),
elevation = 4.dp
) {
Box(modifier = Modifier
.fillMaxSize(),
) {
Image(
painter = rememberImagePainter(
data = collection.image
),
contentDescription = collection.name,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
)
}
Box(modifier = Modifier
.fillMaxWidth()
.height(20.dp)
.background(Color.Transparent)
) {
Text(text = collection.name, color = Color.White)
}
}
}
The problem is I cannot find a way to align the text to the bottom of the screen.
Also, I cannot figure out how to blur the text area.
First thing, Card can't position its own child (when there are multiple children). So you need to use something like Column, Row, Box, or else inside the card itself. So instead having a tree like this
- Card
- Box
- Image
- Box
- Text
You can try it like this
- Card
- Box
- Box
- Image
- Box
- Text
Second, as for the blur in Jetpack-Compose you can refer to this answer. But the API itself is only available on Android 12 and up.

How to apply translucent gradient on an Image in Android Jetpack Compose?

In Android Jetpack Compose, does anyone knows how to make an Image's left side slowly fading to transparent towards right side? Thanks!
Edit:
Sorry, I mean making an image fading like this in Compose, probably with blend mode? But not sure how to do that..
Expected outcome:
Just found an answer from How to add fading edge effect to Android Jetpack Compose Column or Row?
"Color.Black" means area that reveal the image.
Thanks guys!
Image(
painterResource(R.drawable.cat),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
// Workaround to enable alpha compositing
.graphicsLayer { alpha = 0.99f }
.drawWithContent {
val colors = listOf(
Color.Black,
Color.Transparent
)
drawContent()
drawRect(
brush = Brush.horizontalGradient(colors),
blendMode = BlendMode.DstIn
)
}
)
You could place a Box over the image and add a background gradient to the Box. You will have to set the size of the Box though to be the same as the image. In this example I don't bother setting the Box size correctly. I'll leave that up to you:
Here's the image I used:
https://adelaidevet.com.au/sites/default/files/archive/files/images/cat-ginger-eyes-closed_0.jpg
val screenWidth = LocalConfiguration.current.screenWidthDp
Box(modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
Image(
painterResource(R.drawable.cat),
contentDescription = "",
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
Box(
modifier = Modifier
.requiredWidth(screenWidth.dp)
.requiredHeight(310.dp)
.background(
brush = Brush.horizontalGradient(
startX = 0f,
endX = 700f,
colors = listOf(
Color.Transparent,
Color(0xD7525252),
)
)
)
) {
}
}

Jetpack Compose shows scaled images pixelated

I'm migrating from XML layouts to Jetpack Compose and noticed that the Image composable does not scale an image as good as ImageView when the image gets cropped.
The image on the top is added via Jetpack Compose, the one at the bottom is an ImageView.
For comparison, I added them next to each other like this:
Column {
Image(
painter = painterResource(id = R.drawable.my_image),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.size(280.dp)
)
Spacer(
modifier = Modifier.height(4.dp)
)
AndroidView(
factory = { context ->
ImageView(context).apply {
scaleType = ImageView.ScaleType.CENTER_CROP
setImageResource(R.drawable.my_image)
}
},
modifier = Modifier.size(280.dp)
)
}
The effect is depending on the image which is used and I noticed it's mostly promiment on low-end devices, however I would expect Jetpack Compose to have the same or higher image quality, as it is the new framework for building UI on Android.
Is there something I'm missing which can give me the same quality as ImageView?

How to make a surface background half transparent in jetpack compose, but not the content?

I want to achieve this layout:
In XML I would add an image in a relative layout with match_parent attributes, then a view with a black half-transparent background set to match_parent as well, then the content.
In compose I made this composeable:
#Composable
fun ImageCover(resourceId: Int, alpha: Float = 0.5f, content: #Composable () -> Unit) {
Box(modifier = Modifier.fillMaxSize()) {
Image(
painter = painterResource(id = resourceId),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
Surface(
color = Color.Black, modifier = Modifier
.fillMaxSize()
.alpha(alpha)
) {
content()
}
}
}
But the problem is alpha is applied to the surface and its content. So no matter what I put in the content, even if it's another surface with a background, will also be half transparent. Here, for example, the two sentences and two components at the bottom will be half transparent as well.
The background color of the Surface is based on the color attribute.
Apply the alpha to the color property instead of the Modifier.
Something like:
Surface(
color = Color.Black.copy(alpha = 0.6f),
modifier = Modifier.fillMaxSize()
){
//....
}

Categories

Resources