I have tried below code but it reflects nothing in the UI, I'm missing anything here?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
Image(
(ResourcesCompat.getDrawable(
resources,
R.mipmap.ic_launcher,
null
) as BitmapDrawable).bitmap
)
}
}
}
}
You can use the painterResource function:
Image(painterResource(R.drawable.ic_xxxx),"content description")
The resources with the given id must point to either fully rasterized images (ex. PNG or JPG files) or VectorDrawable xml assets.
It means that this method can load either an instance of BitmapPainter or VectorPainter for ImageBitmap based assets or vector based assets respectively.
Example:
Card(
modifier = Modifier.size(48.dp).tag("circle"),
shape = CircleShape,
elevation = 2.dp
) {
Image(
painterResource(R.drawable.ic_xxxx),
contentDescription = "",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Starting at version 1.0.0-beta01:
Image(
painter = painterResource(R.drawable.your_drawable),
contentDescription = "Content description for visually impaired"
)
As imageResourceis not available anymore, the solutions with painterResource are indeed correct, e.g.
Image(painter = painterResource(R.drawable.ic_heart), contentDescription = "content description")
But you can actually still use Bitmap instead of drawable if you need so:
Image(bitmap = bitmap.asImageBitmap())
.asImageBitmap() is an extensions on Bitmap that compose provides and it creates an ImageBitmap from the given Bitmap.
Working in 0.1.0-dev14
Loading drawable in Image could be achieve from this:
Image(
imageResource(id = R.drawable.scene_01),
modifier = Modifier.preferredHeightIn(160.dp, 260.dp)
.fillMaxWidth(),
contentScale = ContentScale.Crop
)
Now, I'm trying to upload drawable in Circle Image that sounds tricky but too easy in JetPack Compose. You can do achieve in this way:
Image(
asset = imageResource(id = R.drawable.scene_01),
modifier = Modifier.drawBackground(
color = Color.Black,
style = Stroke(4f),
shape = CircleShape
).preferredSize(120.dp)
.gravity(Alignment.CenterHorizontally)
.clip(CircleShape),
contentScale = ContentScale.FillHeight
)
Output:
Try This one but if you copy the code and then paste it I don't know why but it won't work so just type it as it is and replace the image id
Image(
painter = painterResource(id = R.drawable.tanjim),
contentDescription = null,
)
version=1.0.0-beta01,use painterResource,imageResource has been deleted.
example
Image(
painterResource(R.drawable.ic_vector_or_png),
contentDescription = null,
modifier = Modifier.requiredSize(50.dp)
)
android developer doc
With version 1.0.0-beta01
It's like below
Image(
painter = painterResource(R.drawable.header),
contentDescription = null,
)
I found that there is a function imageFromResource() in AndroidImage.kt:
fun imageFromResource(res: Resources, resId: Int): Image {
return AndroidImage(BitmapFactory.decodeResource(res, resId))
}
so your code would be:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
val image = imageFromResource(resources, R.mipmap.ic_launcher)
SimpleImage(image)
}
}
}
}
#Composable
fun loadUi() {
val image = +imageResource(R.drawable.header)
CraneWrapper {
MaterialTheme {
Container(expanded = true,height = 180.dp) {
//Use the Clip() function to round the corners of the image
Clip(shape = RoundedCornerShape(8.dp)) {
//call DrawImage() to add the graphic to the app
DrawImage(image)
}
}
}
}
}
I found SimpleImage class from jetpack compose library to load the image but this is a temporary solution, and I didn't find any styling option with this yet.
// TODO(Andrey) Temporary. Should be replaced with our proper Image component when it available
#Composable
fun SimpleImage(
image: Image
) {
// TODO b132071873: WithDensity should be able to use the DSL syntax
WithDensity(block = {
Container(width = image.width.toDp(), height = image.height.toDp()) {
Draw { canvas, _ ->
canvas.drawImage(image, Offset.zero, Paint())
}
}
})
}
I have used it in this way
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
val bitmap = (ResourcesCompat.getDrawable(
resources,
R.mipmap.ic_launcher,
null
) as BitmapDrawable).bitmap
SimpleImage(Image(bitmap))
}
}
}
}
Still, I'm not sure this is the correct way of loading image from drawables.
Google updated their API. For 0.1.0-dev03 loading images is synchronous and is done this way
val icon = +imageResource(R.drawable.ic_xxx)
To draw the image
Container(modifier = Height(100.dp) wraps Expanded) {
DrawImage(icon)
}
Currently the above code relies on you specifying either the exact height or width. It seems that scaling the image is not supported if you want for example 100 dp height and wrap_content instead of Expanded which expands the full width.
Does any one know how to solve this issue? Also is possible to fit the image inside it's container like old way scaleType=fitCenter?
Related
I'm having issues understanding how exactly Offset works in Compose, specifically in the PointerScope onTap callback function to track the exact location where the user tapped on a UI element.
Use Case
User is presented an image. Whenever the user taps onto the image, a marker icon is placed on the tapped location.
Code
(You can ignore the button here, it does nothing at this point)
class MainActivity : ComponentActivity() {
//private var editMode = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val offset = remember {
mutableStateOf(Offset.Infinite)
}
MyLayout(offset.value) {
offset.value = it
}
}
}
#Composable
private fun MyLayout(
offset: Offset,
saveLastTap: (Offset) -> Unit
) {
val painter: Painter = painterResource(id = R.drawable.landscape)
Column(Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.weight(0.95f)
.fillMaxSize()
) {
Image(
contentScale = FillBounds,
painter = painter,
contentDescription = "",
modifier = Modifier
.pointerInput(Unit) {
detectTapGestures(
onTap = {
saveLastTap(it)
}
)
}.border(5.dp, Color.Red)
)
if (offset.isFinite) {
PlaceMarkerOnImage(offset = offset)
}
}
Button(
enabled = false,
onClick = { TODO("Button Impl") },
modifier = Modifier.weight(0.05f),
shape = MaterialTheme.shapes.small
) {
Text(text = "Edit Mode")
}
}
}
#Composable
private fun PlaceMarkerOnImage(offset: Offset) {
Image(
painter = painterResource(id = R.drawable.marker),
contentScale = ContentScale.Crop,
contentDescription = "",
modifier = Modifier.offset(offset.x.dp, offset.y.dp)
)
}
}
Outcome
I added a little dot to the screenshots to indicate where i tapped. You see the image is getting placed far off the expected locations
Assumptions
I read a bit about the Offset object and i suspect the difference has something to do with the conversion to .dp which i need to feed the offset to the marker image Modifier.
It might also be related to coordinates being related to it's parent views or something, but since in my example theres nothing else in the UI but the image, i can't grasp this as a possible candidate.
Any help appreciated. Thank you !
I just solved it by replacing
modifier = Modifier.offset( offset.x.dp, offset.y.dp )
with
modifier = Modifier.offset(
x= LocalDensity.current.run{offset.x.toInt().toDp()},
y= LocalDensity.current.run{offset.y.toInt().toDp()} )
Turned out my mistake was that I ignored the properties of dp by just casting the pixel value to dp. Instead, I should call the local device density to correctly determine the amount of pixels for the transformation.
I have a composable for example
Box(modifier){ ... }
I want to share this view as an image with other apps, that's why I have to convert this Box into an image that can be shared with other apps such as WhatsApp etc. Thank you
You can check out this link for how to build your own capturing system
How to screenshot selectively or scrollable Composable?
You basically get view using
val view: View = LocalView.current
then get position of your Composable using
var composableBounds by remember {
mutableStateOf<Rect?>(null)
}
using onGloballyPositionedModifier
Box(modifier = modifier
.onGloballyPositioned {
composableBounds = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
it.boundsInWindow()
} else {
it.boundsInRoot()
}
}
) {
content()
}
Then capturing image using
val bitmap = Bitmap.createBitmap(
width,
height,
Bitmap.Config.ARGB_8888
)
PixelCopy, this is important, if you don't use this on Oreo and above you are likely to get crash on some devices
PixelCopy.request(
(this.context as Activity).window,
bounds.toAndroidRect(),
bitmap)
or
val canvas = Canvas(bitmap)
.apply {
translate(-bounds.left, -bounds.top)
}
this.draw(canvas)
canvas.setBitmap(null)
bitmapCallback.invoke(ImageResult.Success(bitmap))
i also created a library that takes screenshot or screenshots in specified interval.
https://github.com/SmartToolFactory/Compose-Screenshot
Problem with mine and every other screen capture library out there is not being able to capture scrollable Composables by default.
Box(
modifier: BoxModifier(
shape: BoxShape.rectangle,
color: Colors.red,
),
child: Image.network(
'https://picsum.photos/250?image=9',
),
);
This code converts the Box into an image that can be shared with other apps. The BoxModifier class provides a way to modify the appearance of a box, and the Image class provides a way to display images.
You can get drawable using painter in jetpack compose, then use that Drawble to share..
#Composable
fun photoItem(
photoUrl: String,
modifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Fit
): Drawable? {
val painter = rememberAsyncImagePainter(
Builder(LocalContext.current)
.placeholder(drawable.placeholder)
.data(photoUrl)
.build()
)
Image(
contentScale = contentScale,
painter = painter,
contentDescription = null,
modifier = modifier,
)
val state = painter.state as? AsyncImagePainter.State.Success
return state?.result?.drawable
}
then cast the Drawable to BitmapDrawable
I have a Text and I want to draw a circle that should have a color that exists inside my resource file.
Text(
modifier = Modifier.align(Alignment.TopEnd).drawBehind {
drawCircle(
color = colorResource(R.color.primary),
radius = 96.00f
)
},
text = "X"
)
But I get:
#Composable invocations can only happen from the context of a #Composable function
If I use color = Color.Red, it works fine. So I don't want to use any of those colors, I want to use mine. So I thought to make a conversion, but I cannot find any solution. How can I convert that?
You need to call Composable functions from a scope that has #Composable annotation.
Box() {
val color = colorResource(R.color.black)
Text(
modifier = Modifier.align(Alignment.TopEnd).drawBehind {
drawCircle(
color = color,
radius = 96.00f
)
},
text = "X"
)
}
colorResource is a #Composable function
#Composable
#ReadOnlyComposable
fun colorResource(#ColorRes id: Int): Color {
val context = LocalContext.current
return if (Build.VERSION.SDK_INT >= 23) {
ColorResourceHelper.getColor(context, id)
} else {
#Suppress("DEPRECATION")
Color(context.resources.getColor(id))
}
}
but lambda of Modifier.drawBehind isn't
fun Modifier.drawBehind(
onDraw: DrawScope.() -> Unit
) = this.then(
DrawBackgroundModifier(
onDraw = onDraw,
inspectorInfo = debugInspectorInfo {
name = "drawBehind"
properties["onDraw"] = onDraw
}
)
)
You can check this answer for difference between function, lambdas or params with #Composable annotation or the ones without
When I'm trying to use LazyVerticalGrid to display a list of images, some grid items have a different size even the images itself have exactly same size 240x178.
I tied to use modifier.fillMaxWidth(), modifier.matchParentSize(), modifier.fillMaxHeight(), modifier.fillMaxWidth(), modifier.fillParentMaxHeight(), modifier.fillParentMaxWidth(), modifier.requiredHeight(imageHeight), modifier.requiredWidth(imageWidth) but nothing had helped me to make images fill all available space without leaving any empty spaces between and some images continue to be not the same size with the others.
Below is my current implementation and I'm using Coil for image loading if its important
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun TreasureGrid(treasures: List<MapTreasure>) {
val configuration = LocalConfiguration.current
val imageWidth = configuration.screenWidthDp.dp / 4
val imageHeight = (imageWidth.times(1.348f))
LazyVerticalGrid(
cells = GridCells.Adaptive(imageWidth)
) {
items(treasures.size) {
TreasureItem(
treasures[it],
Modifier.requiredHeight(imageHeight).requiredWidth(imageWidth)
)
}
}
}
#Composable
fun TreasureItem(mapTreasure: MapTreasure, modifier: Modifier) {
val resId = TMApplication.instance.resources.getIdentifier(
mapTreasure.image,
"drawable",
TMApplication.instance.packageName
)
val matrix = ColorMatrix()
val isOpened = getUser()?.openedTreasures?.contains(mapTreasure.name) == true
if (isOpened.not()) {
matrix.setToSaturation(0F)
}
Box {
AsyncImage(
model = resId,
contentDescription = mapTreasure.description,
modifier = modifier.clickable {
if (isOpened) {
activity?.let { DialogUtils.showTreasureInfoDialog(it, mapTreasure) }
} else {
Toast.makeText(activity, "Treasure not found", Toast.LENGTH_SHORT).show()
}
}.matchParentSize(),
colorFilter = ColorFilter.colorMatrix(matrix)
)
val alpha = if (isOpened) 0f else 1f
AsyncImage(
model = R.drawable.treasure_key,
contentDescription = mapTreasure.description,
modifier = modifier.rotate(90f).scale(0.3f).alpha(alpha)
)
}
}
Any suggestions on how to make every grid item same size and remove the padding around it are highly appreciated
The problem was not related to the code. I found out that the designer gave me images of the same size but with different paddings on each image, so the problem was in the image resource itself.
I have tried below code but it reflects nothing in the UI, I'm missing anything here?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
Image(
(ResourcesCompat.getDrawable(
resources,
R.mipmap.ic_launcher,
null
) as BitmapDrawable).bitmap
)
}
}
}
}
You can use the painterResource function:
Image(painterResource(R.drawable.ic_xxxx),"content description")
The resources with the given id must point to either fully rasterized images (ex. PNG or JPG files) or VectorDrawable xml assets.
It means that this method can load either an instance of BitmapPainter or VectorPainter for ImageBitmap based assets or vector based assets respectively.
Example:
Card(
modifier = Modifier.size(48.dp).tag("circle"),
shape = CircleShape,
elevation = 2.dp
) {
Image(
painterResource(R.drawable.ic_xxxx),
contentDescription = "",
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
}
Starting at version 1.0.0-beta01:
Image(
painter = painterResource(R.drawable.your_drawable),
contentDescription = "Content description for visually impaired"
)
As imageResourceis not available anymore, the solutions with painterResource are indeed correct, e.g.
Image(painter = painterResource(R.drawable.ic_heart), contentDescription = "content description")
But you can actually still use Bitmap instead of drawable if you need so:
Image(bitmap = bitmap.asImageBitmap())
.asImageBitmap() is an extensions on Bitmap that compose provides and it creates an ImageBitmap from the given Bitmap.
Working in 0.1.0-dev14
Loading drawable in Image could be achieve from this:
Image(
imageResource(id = R.drawable.scene_01),
modifier = Modifier.preferredHeightIn(160.dp, 260.dp)
.fillMaxWidth(),
contentScale = ContentScale.Crop
)
Now, I'm trying to upload drawable in Circle Image that sounds tricky but too easy in JetPack Compose. You can do achieve in this way:
Image(
asset = imageResource(id = R.drawable.scene_01),
modifier = Modifier.drawBackground(
color = Color.Black,
style = Stroke(4f),
shape = CircleShape
).preferredSize(120.dp)
.gravity(Alignment.CenterHorizontally)
.clip(CircleShape),
contentScale = ContentScale.FillHeight
)
Output:
Try This one but if you copy the code and then paste it I don't know why but it won't work so just type it as it is and replace the image id
Image(
painter = painterResource(id = R.drawable.tanjim),
contentDescription = null,
)
version=1.0.0-beta01,use painterResource,imageResource has been deleted.
example
Image(
painterResource(R.drawable.ic_vector_or_png),
contentDescription = null,
modifier = Modifier.requiredSize(50.dp)
)
android developer doc
With version 1.0.0-beta01
It's like below
Image(
painter = painterResource(R.drawable.header),
contentDescription = null,
)
I found that there is a function imageFromResource() in AndroidImage.kt:
fun imageFromResource(res: Resources, resId: Int): Image {
return AndroidImage(BitmapFactory.decodeResource(res, resId))
}
so your code would be:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
val image = imageFromResource(resources, R.mipmap.ic_launcher)
SimpleImage(image)
}
}
}
}
#Composable
fun loadUi() {
val image = +imageResource(R.drawable.header)
CraneWrapper {
MaterialTheme {
Container(expanded = true,height = 180.dp) {
//Use the Clip() function to round the corners of the image
Clip(shape = RoundedCornerShape(8.dp)) {
//call DrawImage() to add the graphic to the app
DrawImage(image)
}
}
}
}
}
I found SimpleImage class from jetpack compose library to load the image but this is a temporary solution, and I didn't find any styling option with this yet.
// TODO(Andrey) Temporary. Should be replaced with our proper Image component when it available
#Composable
fun SimpleImage(
image: Image
) {
// TODO b132071873: WithDensity should be able to use the DSL syntax
WithDensity(block = {
Container(width = image.width.toDp(), height = image.height.toDp()) {
Draw { canvas, _ ->
canvas.drawImage(image, Offset.zero, Paint())
}
}
})
}
I have used it in this way
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
loadUi()
}
}
#Composable
fun loadUi() {
CraneWrapper {
MaterialTheme {
val bitmap = (ResourcesCompat.getDrawable(
resources,
R.mipmap.ic_launcher,
null
) as BitmapDrawable).bitmap
SimpleImage(Image(bitmap))
}
}
}
}
Still, I'm not sure this is the correct way of loading image from drawables.
Google updated their API. For 0.1.0-dev03 loading images is synchronous and is done this way
val icon = +imageResource(R.drawable.ic_xxx)
To draw the image
Container(modifier = Height(100.dp) wraps Expanded) {
DrawImage(icon)
}
Currently the above code relies on you specifying either the exact height or width. It seems that scaling the image is not supported if you want for example 100 dp height and wrap_content instead of Expanded which expands the full width.
Does any one know how to solve this issue? Also is possible to fit the image inside it's container like old way scaleType=fitCenter?