Issue
Compose card surface shows always overdrawn. However, it's not the case if we use old style CardView in xml.
From here it's always recommended to reduce overdrawing of any view to make it more towards green, blue.
Just this following code will produce some output like this, which means it's overdrawn 3 or 4 times.
Card(
elevation = CardDefaults.cardElevation(defaultElevation = 10.dp),
modifier = modifier.fillMaxWidth().padding(vertical = 10.dp)
) {
Column(
modifier = Modifier
.padding(all = 16.dp)
) {
Text("randomText")
}
}
I tried to look into NowInAndroid sample application but even that produces all red output when debugged to show overdrawn areas.
Question
Is the Debug GPU Overdraw option in developer settings giving right information?
Can we really avoid this overdraw? If so, how?
Related
Is Android Jetpack Compose shadow modifier supposed to look like this?
I'm so confused. I've written a few components and I'm testing the shadow modifier that I believe was introduced in compose-1.2.0-alpha. I'm now using 1.2.1. My component is written as this.
fun Labels() {
Label(
modifier = Modifier.shadow(
elevation = 1.dp,
shape = RoundedCornerShape(4.dp),
ambientColor = Color.Blue,
spotColor = Color.Blue
),
text = field.name,
style = typographies.tagDefault
)
}
Label is a mirror copy of Text for my own reasons, but that means it boils down to::
fun Label(...) {
BasicText(
text,
modifier,
mergedStyle,
onTextLayout,
overflow,
softWrap,
maxLines,
)
}
So in the grand scheme and to my understanding the shadow effect is ultimately being applied to the BasicText and nothing else. Which looks like the case in the image, but why is it so thick? Why isn't it more like a border the same as box-shadow in CSS? How can I make it look like it should with similarities to box-shadow in CSS? I've tried a few things like moving the modifier to higher component pieces and messing with the elevation. But all that has done is made the shadow a little hazy and maybe slightly thicker. Also if I change the shape to something like RoundedCornerShape(percent = 50) it nearly fills in the whole background with this blue color which is definitely not intended.
Consider that this composable, which acts as a "Dialog", is being drawn in front the root application:
I tried to simulate this dialog by making it fit the entire screen and making its root container have a basic background(Color.Gray.copy(alpha = 0.5f) modifier.
However, even it is in front still being possible to interact with that top buttons.
My question is if there's a "direct" way to "disable" interactions of a particular composable tree to avoid passing parameters (such as "clickable") to all affected composables?
I thought doing something like:
take a "screenshot" from the area behind the alpha color;
draw the screenshot as an Image;
then draw the in-front composable (in my example, the "dialog").
However, I don't know how worth this is to implement or even how to take that "screenshot".
Also, may be an way to handle this using something relatad to remember compostion state or so on.
You can have a Box that consumes click events without click feedback:
val interactionSource = remember { MutableInteractionSource() }
Box(
modifier = modifier
.background(
color = MaterialTheme.colors.surface.copy(alpha = .4f)
)
.clickable(
onClick = {
if (dismissOnTouchOutside) {
onDismiss()
}
},
interactionSource = interactionSource,
indication = null
),
contentAlignment = Alignment.Center,
) {
// content here
}
This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.
This code fills the full screen if i specify the size to be 100.dp.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
This code behave properly by filling the required size.
ComposeTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Column(modifier = Modifier.fillMaxSize()) {
Box(
modifier = Modifier
.width(100.dp)
.height(100.dp)
.clip(RoundedCornerShape(12.dp))
.background(color = Color.Red)
) {
}
}
}
}
Can somebody please explain why is it happening?
This is how Box works with the propagateMinConstraints parameter set to true. Surface is using it under the hood.
As an example, setting propagateMinConstraints to true can be useful when the Box has content on which modifiers cannot be specified directly and setting a min size on the content of the Box is needed. If propagateMinConstraints is set to true, the min size set on the Box will also be applied to the content, whereas otherwise the min size will only apply to the Box.
Therefore, the first-level Surface children will have min size constraints equal to the size of Surface.
Here is how one of the maintainers explains the reasons for this decision:
Surface is not really a layout. We had such issue with FloatingActionButton - We set min width and height on it according to the specification, but users can set larger size if they need. And now the content (icon) inside FloatingActionButton needs to be fill the whole size of Surface so we apply a ripple on it, and then ripple is clipped by the Surface shape. If we just set Modifier.fillMaxSize() it will fill the whole screen as FloatingActionButton has no max size specified. And there is no such which as Modifier.fillMinSize() as this information is not propagated by Box because of how the system works. So we come up with propagateMinConstraints=true idea, now the content inside Surface has to fill the min size applied on Surface. To be honest I am not sure the explanation is clear enough :). But yeah, if you need to have some real layout and multiple elements inside your Surface you need to add it manually, so add your own Box.
It can be overridden by Modifier.requiredSize, or, as you did in your second code example - by using an other container. The Column in your example still have size equal to the parent Surface.
I am trying to figure out how to figure out how to wrap my components in Jetpack Compose for narrow screens, e.g. Samsung fold.
For example:
Box(modifier = Modifier.fillMaxWidth()){
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
Image(...)
Spacer(...)
Image(...)
Spacer(...)
SomeBadgeWithText(...)
}
}
On the narrow Samsung device, the last Badge gets squashed and text is cut off. I want it to just wrap to the next line. Is that possible?
AFAIK Wrapping content like you want is not a native compose feature. However Accompanist, a set of compose libraries developed by Google, offers Flow Layout which should suit your use-case.