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,
)
Related
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
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
I am trying to show an image with fill width and auto height using Jetpack Compose Coil. I want the image to take the full width and auto height. But the image only showing when I specify a fixed height.
Image(
painter = rememberImagePainter(
data = post.image
),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
I tried with .fillMaxHeight() and .fillMaxSize() but it's not working. Is there any way I could achieve this?
This happens when one of view width/height is calculated as zero, which means it shouldn't be displayed and no need to download it. Check out more about the reasons in this issue on compose tracker.
You should make your size not being equal to zero. Depending you your layout it can be done with variations of width/height/aspect ratio modifiers.
If you'd like to get your image in original size, you can to add size(OriginalSize) to painter builder. This will force image to start loading. This parameter makes your view download and put into the memory full size of the image, without optimizing it for the current view. So use it carefully, only if you're sure the image won't be too big and you really can't add use size modifiers.
Image(
painter = rememberImagePainter(
data = post.image,
builder = {
size(OriginalSize)
},
),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
With latest Coil 2.1.0 you can do like this:
val model = ImageRequest.Builder(LocalContext.current)
.data(imageUrl)
.size(Size.ORIGINAL)
.crossfade(true)
.build()
val painter = rememberAsyncImagePainter(model)
Image(
modifier = Modifier.fillMaxWidth(),
painter = painter,
contentDescription = null,
contentScale = ContentScale.FillWidth
)
If your image OriginalSize is to small for your composable you can also use scale(Scale.FIT) in your builder.
Your image will max fit your composable while keeping it's aspect ratio.
Image(
rememberImagePainter(
data = user?.photoUrl?:"",
builder = {
size(OriginalSize)
scale(Scale.FIT)
transformations(CircleCropTransformation())
}
),
contentDescription = "Picture",
contentScale = ContentScale.FillWidth
)
Instead of using size(OriginalSize) that impacts image optimization, you can set min height/width for image (for example 1.dp). This will force image to start loading:
Image(
painter = rememberImagePainter(url),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.defaultMinSize(minHeight = 1.dp),
)
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.
I am trying to show a rectangle image with android compose in a circle shape but the edges don't stretch to fill the whole.
In other words I don't want the black background shown in the image below to appear and the flag to stretch to cover it even if I have to crop from the vertical edges
The code used to produce the image
Image(
imageVector = vectorResource(id = R.drawable.flag_cn),
modifier = Modifier.matchParentSize().clip(CircleShape)
.background(shape = CircleShape, color = Color.Black))
Update: I tried to set the content scale option in Image Compose to crop
Image(
imageVector = vectorResource(id = flagID),
modifier = Modifier
.fillMaxSize(0.4f)
.background(shape = CircleShape, color = Color.Black)
.align(Alignment.BottomEnd)
.clip(CircleShape),
contentScale = ContentScale.Crop
)
Maybe something like this?
val imageVector = vectorResource(id = R.drawable.flag_cn)
Surface(
modifier = Modifier.preferredSize(200.dp),
shape = CircleShape,
) {
Image(imageVector, modifier = Modifier.size(300.dp))
}
You can use scale type option available for scaling the bounds of an image to the bounds of its view. This option is available for ImageView.
You can choose
android:scaleType="centerCrop"
Reference:
ImageView ScaleType
Apply the clip Modifier using a CircleShape:
Image(
painter = painterResource(R.drawable.xxx),
contentDescription = "xxxx",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(64.dp)
.clip(CircleShape)
)