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
Related
Loading an image from a web URL and displaying a shimmer during load. Are there any better ways to handle this?
val context = LocalContext.current
val imageLoader = ImageLoader(context)
val request = ImageRequest.Builder(context)
.data(thumbnailUrl)
.build()
val painter = rememberImagePainter(
request = request,
imageLoader = imageLoader
)
val state = painter.state
Image(
painter = painter,
contentDescription = "thumbnail image",
modifier = Modifier
.fillMaxSize()
.placeholder(
visible = state is
ImagePainter.State.Loading,
color = PlaceholderDefaults.color(
backgroundColor = MyTheme.colors.shimmer.copy(0.1f),
),
highlight = PlaceholderHighlight.shimmer(),
),
contentScale = ContentScale.Crop
)
How would you add a token to this request? I tried setting the header with a token but no response. Any suggestions?
I recently migrated from Accompanist's ImagePainter to Coil's, below is the pertinent code after my updates.
val painter = rememberImagePainter(DRAWABLE_RESOURCE_ID)
when (painter.state) {
is ImagePainter.State.Empty -> Timber.w("Empty")
is ImagePainter.State.Loading -> {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.wrapContentSize()
) {
CircularProgressIndicator()
}
}
is ImagePainter.State.Success -> {
Image(
painter = painter,
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.padding(8.dp)
.size(84.dp)
.clip(RoundedCornerShape(corner = CornerSize(16.dp)))
)
}
is ImagePainter.State.Error -> Timber.e("Error")
}
Now those images don't render and painter.state is always Empty. My legacy Accompanist implementation displayed images by this point in the code. It also works if I use the stock painterResource(resId) from Compose.
What am I missing to execute Coil's new painter through its states?
As suggested by #Philip Dukhov you don't need coil to load local resources.
If you want to use it, you can simply your code using:
val painter = rememberImagePainter(R.drawable.xxx)
val state = painter.state
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.wrapContentSize()
) {
AnimatedVisibility(visible = (state is ImagePainter.State.Loading)) {
CircularProgressIndicator()
}
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.size(128.dp)
)
}
You can use the drawable's resource ID as model for the coil's AsyncImage composable.
AsyncImage(
model = R.drawable.bg_gradient,
contentDescription = "Gradient background",
)
There's a significant performance difference between the composables - Image and AsyncImage. So Coil's AsyncImage loading is very handy at times.
You don't need coil to load local resources. You can use system painterResource:
Image(
painter = painterResource(id = R.drawable.test),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.padding(8.dp)
.size(84.dp)
.clip(RoundedCornerShape(corner = CornerSize(16.dp)))
)
If you would use it for remove image loading: since move from accompanist to coil, painter won't start loading unless Image is in the view tree hierarchy. So you can move Image into a Box with your while:
Box(contentAlignment = Alignment.Center) {
val painter = rememberImagePainter(R.drawable.test)
Image(
painter = painter,
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.padding(8.dp)
.size(84.dp)
.clip(RoundedCornerShape(corner = CornerSize(16.dp)))
)
when (painter.state) {
is ImagePainter.State.Empty -> Timber.w("Empty")
is ImagePainter.State.Loading -> {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.wrapContentSize()
) {
CircularProgressIndicator()
}
}
is ImagePainter.State.Success -> {
}
is ImagePainter.State.Error -> Timber.e("Error")
}
}
Also it may not start loading when you're not providing enough size modifiers(that's not your case, just for you to know). Check out this answer for more information.
val context = LocalContext.current
val imageLoader = ImageLoader(context)
val request = ImageRequest.Builder(context)
.data(thumbnailUrl)
.build()
val painter = rememberImagePainter(
request = request,
imageLoader = imageLoader
)
val state = painter.state
Image(
painter = painter,
contentDescription = "thumbnail image",
modifier = Modifier
.fillMaxSize()
.placeholder(
visible = state is ImagePainter.State.Loading,
color = PlaceholderDefaults.color(
backgroundColor = SMXTheme.colors.shimmer.copy(0.1f),
),
highlight = PlaceholderHighlight.shimmer(),
),
contentScale = ContentScale.Crop
)
I am trying to show an image from the gallery if anything chosen by the user, or an image from an image file in the drawable resource as the default image, which is not working. I am using Coil for Compose and added the dependency already. Here is the code:
class MainActivity : ComponentActivity() {
private var imageUriState = mutableStateOf<Uri?>(null)
private val selectImageLauncher = registerForActivityResult(GetContent()) { uri ->
imageUriState.value = uri
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ImageSourceActivityScreen()
}
}
#Composable
fun ImageSourceActivityScreen() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center
) {
Image(
painter = rememberImagePainter(
if (imageUriState != null) {
imageUriState.value
} else {
R.drawable.blank_profile_picture
}
),
contentDescription = "profile image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
...
}
No error is showing, but the default image is also not showing. Please help to make it work. Thanks!
Your code doesn't work because you're comparing imageUriState with null which is always true
You have two options:
Specify painter depending on your state value
Image(
painter = if (imageUriState.value != null) {
rememberImagePainter(
imageUriState.value
)
} else {
painterResource(id = R.drawable.blank_profile_picture)
},
contentDescription = "profile image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
By default, coil won't display you a placeholder if you pass null as data, which generally makes sense. If you wanna see your placeholder both when there's no image, and when you're waiting image to be loaded, you can create such function:
#Composable
inline fun rememberImagePainter(
data: Any?,
#DrawableRes emptyPlaceholder: Int,
builder: ImageRequest.Builder.() -> Unit = {},
): Painter {
val painter = rememberImagePainter(
data,
builder = {
placeholder(emptyPlaceholder)
builder()
}
)
if (data == null) {
return painterResource(emptyPlaceholder)
}
return painter
}
// usage
Image(
painter = rememberImagePainter(
"https://i.stack.imgur.com/rkyep.jpg",
emptyPlaceholder = R.drawable.test,
),
contentDescription = "profile image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
You can use the builder parameter to set a placeholder:
val painter = rememberImagePainter(
imageUriState.value,
builder = {
placeholder(R.drawable.blank_profile_picture)
}
)
Image(
painter,
contentDescription = "profile image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
Found hints of where to look for the bug from #Gabriele Mariotti's answer. I was calling imageUriState without .value. Fixed that, and it is working now. Thanks, everyone for your help!
Image(
painter = rememberImagePainter(
if (imageUriState.value != null) {
imageUriState.value
} else {
R.drawable.blank_profile_picture
}
),
contentDescription = "profile image",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxWidth()
)
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")