Jetpack Compose - Image antialiasing not as smooth as native ImageView when scaled - android-jetpack-compose

I'm refactoring a screen to use jetpack compose. It had an ImageView that sits over the top of the background containing a large 1920x795dp PNG image (mdpi and hdpi buckets) which gets scaled down. After changing to use an Image it now has noticably jaggier edges.
ImageView:
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:scaleType="fitXY"
android:layout_marginBottom="-90dp"
android:src="#drawable/ic_swoosh"/>
Image:
Image(
modifier = Modifier
.fillMaxWidth()
.offset(y = 90.dp)
.align(Alignment.BottomCenter),
painter = painterResource(R.drawable.ic_swoosh),
contentDescription = null,
contentScale = ContentScale.Fit,
)
Drilling into the source of Image, it uses AndroidPaint which supposedly has it enabled:
internal fun makeNativePaint() =
android.graphics.Paint(android.graphics.Paint.ANTI_ALIAS_FLAG)
If I wrap ImageView in AndroidView the problem is not present. I can also verify that it is positioned and sized exactly the same as the Image version, but it is anti-aliased correctly.
AndroidView(
modifier = Modifier
.fillMaxWidth()
.offset(y = 90.dp)
.align(Alignment.BottomCenter),
factory = {
ImageView(it).apply {
scaleType = android.widget.ImageView.ScaleType.FIT_XY
setImageResource(R.drawable.ic_swoosh)
}
}
)
If I remove scaling with contentScale = ContentScale.None the image is also smooth, so it does appear to be due to being scaled down. Removing contentScale entirely shows the same problem because the image is automatically scaled down to fit the view.
Edit: If I remove the hdpi variant it also becomes smooth!

Related

Fill Image to max width of parent

I want the image to fill the max width of container, and have a specific height. I tried using ContentScale.FillWidth, but it still leaves some background (green).
#Preview(showBackground = true)
#Composable
fun testImg() {
Card(modifier = Modifier.fillMaxWidth()) {
Image(
painter = painterResource(id = R.drawable.jwimage1),
null,
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.background(Green),
contentScale = ContentScale.FillWidth
)
}
}
prefview fro testImg
I tried using different ContentScale, adding height specification, but they don't change the result.
Edit: Solved! It's really silly that I used a png image with two transparent strips on its sides :(

Jetpack compose: how to set intrinsic height as minimum, but expand to fill space?

I'm trying to set it so that a TextField refuses to shrink past its intrinsic height (defined by minLines), but also grow to fill max height:
// context: this is inside of a Column that has fillMaxHeight on it
OutlinedTextField(
// unrelated fields
minLines = 26,
modifier = Modifier
//.height(IntrinsicSize.Min)
.fillMaxWidth()
.fillMaxHeight()
.weight(1f)
.height(IntrinsicSize.Max)
)
However, everything I've tried (setting fillMaxHeight, weight(1f), using requiredHeight instead of height, etc) has issues. It seems like it's mutually exclusive: I can either set a minimum height based on intrinsic height or I can grow to fill the height (using weight seems to work for that, but not fillMaxHeight for some reason.
What is the proper way to have IntrinsicSize be the minimum, but grow to fill remaining space?
If i understand this question correctly you want your TextField height to be one line tall then increases as user enters text. You can selectively change modifier using Modifier.then()
Edit
As of jetpack compose 1.4.0-alpha02 minLines param is added to OutlineTextField and other text composables
#Composable
private fun Sample() {
var text by remember {
mutableStateOf("Hello World")
}
var focused by remember {
mutableStateOf(false)
}
Column(modifier = Modifier.fillMaxHeight()) {
OutlinedTextField(
value = text,
onValueChange = {
text = it
},
minLines = 26,
// unrelated fields
modifier = Modifier
.fillMaxWidth()
.then(
if (focused) {
Modifier.wrapContentHeight()
} else Modifier.fillMaxHeight()
)
.onFocusChanged {
focused = it.isFocused
}
)
}
}

how to set gravity in jetpack compose Text

I have a text component that height is 48dp and want to set the text center in vertical.
Like the TextView with 48dp height and gravity is center_vertical.
Or I have to put it(Text compose) in Surface component?
Yes, that is correct, you would need to put it in a box for instance, or a column, and center it that way.
Box(modifier = Modifier.fillMaxSize, contentAlignment = Alignment.Center){
Text(text = "My Text") }

Jetpack Compose - Scrollable Column and clip to padding

I have a simple screen with scrollable vertical column. It contains some text and images.
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.verticalScroll(rememberScrollState()),
) {
...
}
The content is scrollable but it clips to defined padding. Meaning when you scroll, you can see that overscroll shadow does not fill the entire screen, but it is bound to the padding. It looks really bad:
In XML world you would use android:clipToPadding="false" to "fill" the container. Is there equivalent of that in Compose?
Got it, apparently order of modifier constraints matters, didn't know that.
Just place padding as last one.
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState())
.padding(16.dp),
) {
...
}

How to trim image edge in Jetpack Compose?

Let's say I want to show a composable view with an image. Instead of an original image I would like to trim part of the image away, for example 20% from the right edge. How would I do this? Can I use a .clip-modifier with a custom shape?
You can create your own shape and clip using that shape as
private val cropShape = GenericShape { size: Size, layoutDirection: LayoutDirection ->
addRect(Rect(0f, 0f, size.width * .8f, size.height))
}
0.8 is arbitrary number, you can customize your Rectangle as you wish
Image(
modifier = Modifier.clip(cropShape),
painter = bitmap,
contentDescription = null
)
The one on top without clip modifier, the one at the bottom is clipped with Modifier.clip(cropShape)
You can crop it with a shape, as #Thracian suggests, but in that case the view size won't change. For example, if you put it in Row, you will have an unexpected padding to next item.
Instead, you can use Modifier.layout to actually reduce the size of the view, and clip it with RectangleShape, since by default Compose views are not clip to bounds.
Image(
painter = painterResource(id = R.drawable.my_image_1),
contentDescription = null,
modifier = Modifier
.clip(RectangleShape)
.layout { measurable, constraints ->
val placeable = measurable.measure(constraints)
layout(width = placeable.width * 8 / 10, height = placeable.height) {
placeable.place(0, 0)
}
}
)

Resources