How to set a drawable resource as a background to an Image in Jetpack compose?
Corresponding view XML code
<androidx.appcompat.widget.AppCompatImageView
android:background="#drawable/drawable_black_circle"
android:src="#drawable/app_logo"
...
/>
My current code,
Image(
painter = painterResource(id = R.drawable.app_logo),
contentDescription = null,
modifier = Modifier
.height(160.dp)
.width(160.dp)
.padding(32.dp),
)
Modifier.background() gives me the option to use color as a background.
Similarly looking for a way to use a drawable resource as a background.
Note:
Looking for a way to have the background in the image.
Not looking for enclosing the image inside another composable with the background.
With Jetpack compose, it is easy to draw a circle background or any shapes by using shape
Solution 1:
Image(painterResource(id = R.drawable.avatar1), contentDescription = null,
modifier = Modifier.size(160.dp).background(
color = Color.Black,
shape = CircleShape
)
)
Solution 2:
In the case, you want to use a background image, you can use :
Modifier.paint
For your case:
Image(
painter = painterResource(id = R.drawable.app_logo),
contentDescription = null,
modifier = Modifier
.height(160.dp)
.width(160.dp)
.paint(
painter = painterResource(R.drawable.drawable_black_circle),
contentScale = ContentScale.FillWidth
)
.padding(32.dp),
)
For other layouts, we can do the same. Here is an example for ConstraintLayout
ConstraintLayout(
modifier = Modifier
.fillMaxWidth()
.paint(painterResource(id = R.drawable.ic_background_detail), contentScale = ContentScale.FillWidth)
) {
}
You can place two images in a Box, so main one will be on top of the background.
If you are going to use such view extensively, you can create your own composable which can be used simply like this:
ImageWithBackground(
painter = painterResource(id = R.drawable.app_logo),
backgroundDrawableResId = R.drawable.background,
contentDescription = "",
modifier = Modifier
.height(160.dp)
.width(160.dp)
.padding(32.dp),
)
Composable:
#Composable
fun ImageWithBackground(
painter: Painter,
#DrawableRes backgroundDrawableResId: Int,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null
) {
Box(
modifier = modifier
) {
Image(
painter = painterResource(backgroundDrawableResId),
contentDescription = null,
modifier = Modifier
.matchParentSize()
)
Image(
painter = painter,
contentDescription = contentDescription,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
modifier = Modifier
.matchParentSize()
)
}
}
Sir if you absolutely do not wish to nest the image inside another, you could build it using Canvas. However, I don't see a problem here since under the hood, that's what it is — an image on top of another. A Box is just a positioning mechanism. If your decide to go for Canvas, this is how to go about using it:
val backgroundVector = ImageVector.vectorResource(id = R.drawable.ic_launcher_foreground)
val backgroundPainter = rememberVectorPainter(image = backgroundVector)
val foregroundVector = ... // Same as above
val foregroundPainter = rememberVectorPainter(...) // Same as above
Canvas(modifier = Modifier.fillMaxSize()) {
with(backgroundPainter) {
draw(painter.intrinsicSize) // You can scale and modify, all the good stuff
}
with(foregroundPainter) {
// Ditto as above,
}
}
There's also the transform scope available, or the individual translate, rotate, and scale, maybe more.
Set background image in jetpack compose
fun MainViews(){
Box(
modifier = with (Modifier){
fillMaxSize()
.paint(
// Replace with your image id
painterResource(id = R.drawable.image),
contentScale = ContentScale.fillBounds)
})
{
// Add more views here!
Text("Hello Stack!")
}}
Note: I wrote this code on my Android phone maybe you got some syntax errors
Related
I have the following composable that represents a list model -
#Composable
fun DashboardCard(
modifier: Modifier = Modifier,
model: DashboardCardModel,
onCardClicked: (model: DashboardCardModel) -> Unit
) {
Column(
modifier = modifier
.size(200.dp, 200.dp)
.background(Color.Transparent)
.padding(16.dp)
.clickable {
onCardClicked(model)
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround
) {
if (model.showDefaultThumbnail) {
AsyncImage(
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp)),
model = model.thumbnailUrl, contentDescription = ""
)
} else {
Image(
modifier = Modifier
.size(90.dp)
.clip(RoundedCornerShape(10.dp)),
painter = painterResource(id = com.tinytap.R.drawable.tinytap),
contentDescription = ""
)
}
Image(
modifier = Modifier
.size(25.dp)
.padding(top = 10.dp)
.alpha(if (model.isCurrentPostOfInterest) 1f else 0f),
painter = painterResource(id = com.tinytap.R.drawable.post_of_interest),
contentDescription = null
)
Text(
modifier = Modifier.padding(top = 10.dp),
fontSize = 16.sp,
color = Color.White,
text = model.title,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = model.author,
fontSize = 12.sp,
color = Color.LightGray,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
}
The issue is that I am using a fixed size for both my Image and AsyncImage but sometimes the API gives me images that are very wide, causing me to have the following inconsistency in the UI -
How can I make it that my images show up exactly the same? I tried using all kind of crops but the results ended up messing my image
Putting into consideration that image might be shorter, taller, wider or smaller.
To solve this issue, I recommend you to use Coil here is a sample of code that will solve your issue :
Card(
modifier = Modifier.size(119.dp, 92.dp),
shape = RoundedCornerShape(10.dp)
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageURL)
.build(),
placeholder = painterResource(R.drawable.complex_placeholder),
error = painterResource(R.drawable.complex_placeholder),
contentDescription = "complex image",
contentScale = ContentScale.Crop,//The line that will affect your image size and help you solve the problem.
)
}
The above code will always show fully covered box with image for any case, also don't forget to determine size of container (Card in my example '119,92' ).
To know more about different attributes and their effect on your code, select what suits you the best
Check more here (reference of image attached) : Content scaletype fully illustrated
I am trying to make something that uses the same concept as the image below;
An image like background with text overlaying it.
I tried to make a card and give it a backgroundColor of the image, but I got an error;
What I want to do is overlay some texts on an image, like the image above.
So please how do I arrange this code. I need everything to be in a single composable because I need to populate it.
Thanks for your understanding and assistance, In advance.
Please, I'd happily provide any more info needed.
Use a Box to overlay composables.
Something like:
#Composable
fun ImageAndText(
modifier: Modifier = Modifier,
painter: Painter,
contentDescription: String,
text: String
) {
val shape = RoundedCornerShape(8.dp)
val height = 100.dp
Box(
modifier = modifier
.height(height)
.fillMaxWidth()
.background(White, shape = shape),
contentAlignment = Alignment.Center
) {
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(shape)
)
Text(
text = text,
color = White
)
}
}
You can use Box as a direct Child of your Card and put the Image and Text in it and set its contentAlignment to Alignment.Center.
Use the Image composable to host your desired image instead of card's backgroundColor since it only accepts Color.
#Composable
fun ImageWithTextInMiddle() {
Card {
Box(
modifier = Modifier
.height(100.dp)
.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
Image(
// painterResource(successInfo.successInfoImageId)
painterResource(R.drawable.img),
contentDescription = "",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
// will display in the middle of the image
Text("Some Text In the middle")
}
}
}
I'm using Coil 1.3.2 in Jetpack Compose and I have an Image like this
Image(
painter = rememberImagePainter(
data = imageUrl,
onExecute = { _, _ -> true },
builder = {
placeholder(R.drawable.icon)
}
),
contentScale = ContentScale.FillWidth,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
)
How can I set a custom color and size for my placeholder icon ?
I didn't find any examples on the documentation
You can use painter.state to see if the image is still loading, and use Box to display the desired placeholder. Note that the Image to be loaded must be in the view hierarchy, just defining rememberImagePainter won't start loading.
You can use either Image or Icon for the placeholder: if you need to change tint color, the second option seems cleaner:
Box(contentAlignment = Alignment.Center) {
val painter = rememberImagePainter(data = imageUrl)
Image(
painter = painter,
contentScale = ContentScale.FillWidth,
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f)
)
if (painter.state !is ImagePainter.State.Success) {
Icon(
painter = painterResource(id = R.drawable.icon),
contentDescription = null,
tint = Color.Blue,
modifier = Modifier.size(100.dp)
)
}
}
I'm using contentAlignment = Alignment.Center to center static size placeholder inside the Box, also you can add Modifier.matchParentSize() to the placeholder so it'll be the same size as the image, use fillMaxSize(part) to take part of parent space, etc.
Also you can use AnimatedVisibility instead of if to add an animation.
AFAIK you cannot do that using the resource directly, but you could use different placeholder overload taking the Drawable object. You could try and do what you need directly in that object
Created ImageCard view for creating the list in android jetpack compose
but some images can't scratch to Box widget's width and height.
Using 640*427 image and output like image 1.
Using 6720*4480 image and it's looking good like image 2.
Use below code to create ImageCard.
Usage of ImageCard function: Call ImageCardData function in setContent{} function
#Composable
fun ImageCard(
painter: Painter,
title: String,
contentDescription: String,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(10.dp),
elevation = 5.dp
) {
Box(
modifier = Modifier.height(200.dp)
) {
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
Box(
modifier = Modifier
.fillMaxSize()
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black
),
startY = 50f
)
)
)
Box(
modifier = Modifier
.fillMaxSize()
.padding(12.dp),
contentAlignment = Alignment.BottomStart
) {
Text(
text = title,
style = TextStyle(color = Color.White, fontSize = 16.sp)
)
}
}
}
}
#Composable
fun ImageCardData() {
val painter = painterResource(id = R.drawable.engagement)
val title = "Sample Text Title"
val description = "This is sample Image Description"
Box(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
ImageCard(
painter = painter,
title = title,
contentDescription = description
)
}
}
Your image view size gets calculated by it content, because it doesn't have any size modifiers.
Add fillMaxSize to the image:
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
It depends by the Image implementation with a Painter
Creates a composable that lays out and draws a given Painter. This will attempt to size the composable according to the Painter's intrinsic size. However, an optional Modifier parameter can be provided to adjust sizing or draw additional content (ex. background)
It means that using an 640*427 image in a parent container with larger dimensions the Image composable size is the original size of the Painter.
The scale factor applied by the ContentScale is based on these dimensions and source = destination and doesn't change the intrinsic size of the original Painter.
Image(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
Using a 6720*4480 image the Image size is larger than the parent container and in this way the Image composable fills all the available space.
In your case you can solve using the modifier fillMaxWidth() in your Image
Image(
modifier =Modifier.fillMaxWidth(),
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop
)
In this way the Image composable fills the parent space.
Let's say I have a rectangular avatar image like the one below, how can I force it to be drawn as a circle in Jetpack Compose?
There's a clip modifier which can be applied to any composable as well as the Image, just pass a CircleShape into it:
Image(
painter = painterResource(R.drawable.sample_avatar),
contentDescription = "avatar",
contentScale = ContentScale.Crop, // crop the image if it's not a square
modifier = Modifier
.size(64.dp)
.clip(CircleShape) // clip to the circle shape
.border(2.dp, Color.Gray, CircleShape) // add a border (optional)
)
You can use any other shape to clip the image, for example CircleShape it's just RoundedCornerShape(percent = 50). Let's try RoundedCornerShape(percent = 10):
Also, you may try a
implementation "com.github.skydoves:landscapist-glide:1.3.6"
By using Modifier.clip(CircleShape)
GlideImage(
modifier = Modifier
.width(50.dp)
.height(50.dp)
.clip(CircleShape)
.clickable(enabled = true, onClick = onClick),
imageModel = "https://avatars.githubusercontent.com/u/27887884?v=4",
// Crop, Fit, Inside, FillHeight, FillWidth, None
contentScale = ContentScale.Crop,
// shows an image with a circular revealed animation.
circularReveal = CircularReveal(duration = 250),
// shows a placeholder ImageBitmap when loading.
placeHolder = ImageBitmap.imageResource(R.drawable.avater),
// shows an error ImageBitmap when the request failed.
error = ImageBitmap.imageResource(id = R.drawable.avater)
)
For more components visit LandScapist
For those who wonder how to make an image squared (or round) without explicitly setting its sizes, there is Modifier.aspectRatio(1f)
We can achieve this using the background field in a modifier.
Image(
painter = painterResource(id = R.drawable.ic_arrow_right),
contentDescription = "",
modifier = Modifier
.padding(4.dp, 4.dp).background(colorResource(id = R.color.greenColor), CircleShape)
.clip(CircleShape),
colorFilter = ColorFilter.tint(colorResource(id = R.color.white)),
contentScale = ContentScale.Crop,
)