I have an Image in compose like the following:
Image(
bitmap = ImageBitmap.imageResource(id = R.drawable.testimage),
contentDescription = null, // Only decorative image
contentScale = ContentScale.FillWidth,
modifier = Modifier
.requiredHeightIn(max = 250.dp)
.fillMaxWidth()
.semantics { testTag = "MyTestTag" },
)
During an Instrumentation test I want to make sure the correct drawable is set. I did not find anything to achieve this in classes like SemanticsProperties to write a custom matcher. Can anyone help?
You can add a semantic yourself.
val DrawableId = SemanticsPropertyKey<Int>("DrawableResId")
var SemanticsPropertyReceiver.drawableId by DrawableId
val resId = R.drawable.my_drawable
Image(
painter = painterResource(id = resId),
contentDescription = null,
Modifier.semantics { drawableId = resId }
)
And test it with
fun hasDrawable(#DrawableRes id: Int): SemanticsMatcher =
SemanticsMatcher.expectValue(DrawableId, id)
composeRule.onNode(hasDrawable(R.drawable.my_drawable))
.assertIsDisplayed()
If you prefer to keep things simple and avoid defining your own semantics, use the testTag:
val resId = R.drawable.some_image
Image(
painter = painterResource(id = resId),
contentDescription = null,
Modifier.testTag = resId.toString()
)
and in your test:
composeTestRule.onNodeWithTag(R.drawable.someImage.toString())
.assertIsDisplayed()
You can use espresso for that
Espresso.onView(withId(R.drawable.testimage)).check(matches(isDisplayed()))
https://developer.android.com/jetpack/compose/testing#espresso-interop
The semantics apparently are not aware of what image is displayed.
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
I'm implementing a simple gallery screen using Jetpack Compose which shows all video and image thumbnails on the screen
I have displayed image from file path successfully. However, I've got trouble in showing video thumbnail. How can I do that using Coil?
Here's my code to show image thumbnails:
#Composable
fun ImageLoaderFromLocal(
url: String,
placeHolderResId: Int,
modifier: Modifier,
transformation: Transformation
) {
val painter = rememberImagePainter(data = File(url),
builder = {
placeholder(placeHolderResId)
crossfade(true)
transformations(transformation)
})
Image(
painter = painter,
contentDescription = null,
modifier = modifier,
contentScale = ContentScale.Inside
)
}
According to Coil documentation, you need to add following dependency:
implementation("io.coil-kt:coil-video:$coil_version")
and specify fetcher in the builder:
val context = LocalContext.current
val painter = rememberImagePainter(
data = url,
builder = {
fetcher(VideoFrameUriFetcher(context))
// optionally set frame location
videoFrameMillis(1000)
placeholder(placeHolderResId)
crossfade(true)
transformations(transformation)
}
)
The other answer doesn't really work any more.
so i tried this one and it worked
val context = LocalContext.current
var visible by rememberSaveable { mutableStateOf(false) }
val imageLoader = ImageLoader.Builder(context)
.components {
add(VideoFrameDecoder.Factory())
}.crossfade(true)
.build()
val painter = rememberAsyncImagePainter(
model = "Your file here",
imageLoader = imageLoader,
The painter should be called in the image composable
like this
Image(
painter = painter,
contentDescription = "",
contentScale = ContentScale.Crop,
alignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
)
i hope this help someone
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 need to build a ScrollableTabRow that include text and image.
:
(this screen shot was taken on compose 1.0.0-alpha09)
but after I upgrade compose to 1.0.0, the image didn't show. the image tab item is empty:
the ScrollableTabRow demo code:
#Composable
fun ScrollableRowWithImage(){
ScrollableTabRow(
backgroundColor = Color.Transparent,
selectedTabIndex = 0,
edgePadding = 24.dp,
modifier = Modifier.wrapContentSize(align = Alignment.CenterStart)
) {
(1..4).forEach{ _ ->
Tab(
selected = false,
onClick = { },
) {
Image(
painter = rememberImagePainter(
data = "http://mstphoto.cmvideo.cn:8080/clt/20210607/09/1F7I2L5NT7HQ.png",
),
contentDescription = null,
)
}
}
}
}
The image can display normally in ohter place:
#Composable
fun ScrollableRowWithImage(){
Column(modifier = Modifier.fillMaxSize()) {
ScrollableTabRow(
backgroundColor = Color.Transparent,
selectedTabIndex = 0,
edgePadding = 24.dp,
modifier = Modifier.height(80.dp)){
(1..4).forEach{ _ ->
Tab(
selected = false,
onClick = { },
) {
SampleImage()
}
}
}
Divider()
SampleImage()
}
}
#Composable
fun SampleImage(){
Image(
painter = rememberImagePainter(
data = "http://mstphoto.cmvideo.cn:8080/clt/20210607/09/1F7I2L5NT7HQ.png",
),
contentDescription = null,
)
}
You can check out state of your request to see if there's an error using state value:
val painter = rememberImagePainter("http://mstphoto.cmvideo.cn:8080/clt/20210607/09/1F7I2L5NT7HQ.png")
println("${painter.state}")
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.size(128.dp)
)
In your case it's:
CLEARTEXT communication to mstphoto.cmvideo.cn not permitted by network security policy
Which is because you're using http instead of https, check out how to solve this in this answer
An other problem is with Image inside ScrollableTabRow. Looks like a coil bug, I've reported. Image doesn't start loading without size modifier. Adding .size(40.dp) or even .weight(40.dp) solves the problem
Check it with a drawable resource, see if the problem is with the fetching of the data from the web. Replace the
painter = rememberImagePainter( data = "http://mstphoto.cmvideo.cn:8080/clt/20210607/09/1F7I2L5NT7HQ.png", )
with
painter = painterResource(R.drawable.ic_launcher_foreground) // OR any other resource, if this is not available
Check if this resource renders and if it does not, I'll modify the answer.
I'm working on an Android app using Jetpack Compose 1.0.0 and I'm trying to make a composable that uses a nullable image URL string and, if it's null, it will show a placeholder with painterResource and, if it's not null, it will display the actual image using rememberImagePainter.
The way I was doing that was:
#Composable
fun VariableImagePainterExample (
imageURL: String?
) {
val painter = rememberCoilPainter(
null,
previewPlaceholder = R.drawable.ic_placeholder_user,
fadeIn = true,
)
LaunchedEffect(imageURL) {
painter.request = imageURL
}
Image(painter = painter, contentDescription = null)
}
Unfortunately, the rememberCoilPainter became deprecated from accompanist-coil and it's suggested now to use the rememberImagePainter. However, the ImagePainter.request can't be changed like above. I then tried the following code:
#Composable
fun VariableImagePainterExample (
imageURL: String?
) {
val painter = remember {
mutableStateOf<ImagePainter>(painterResource(id = R.drawable.ic_placeholder_user))
}
LaunchedEffect(imageURL) {
painter.value = rememberImagePainter(imageURL)
}
Image(painter = painter.value, contentDescription = null)
}
But this doesn't work because painterResource and rememberImagePainter must be used on the #Composable function. How can i achieve the same effect as before?
In Coil 2.0.0 both AsyncImage and rememberAsyncImagePainter have placeholder parameter that takes any other painter:
AsyncImage(
model = imageURL,
placeholder = painterResource(R.drawable.placeholder),
contentDescription = null,
)
In Coil 1.4.0 you can use builder like this:
Image(
rememberImagePainter(
imageURL,
builder = {
placeholder(R.drawable.placeholder)
}
),
contentDescription = null,
)
For Coil 2.2.+
It has remeberAsyncImagePainter instead off rememberImagePainter
Image(painter = rememberAsyncImagePainter(model = imageUrl,
imageLoader = ImageLoader.Builder(context).crossfade(true).build()),
contentDescription = "images")