How to remove padding in an IconButton ? I want items in my column have the same start padding
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
IconButton(onClick = { }) {
Icon(asset = Icons.Filled.Search)
}
Text("Some text")
}
The space is due to accessibility touch targets and a default size of 48.dp.
Starting with 1.2.0 the best best way to change the default behaviour and remove the extra space is disabling the LocalMinimumTouchTargetEnforcement and applying a size modifier:
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
IconButton(
modifier = Modifier.size(24.dp),
onClick = { }
) {
Icon(
Icons.Filled.Search,
"contentDescription",
)
}
}
Pay attention because in this way it is possible that if the component is placed near the edge of a layout / near to another component without any padding, there will not be enough space for an accessible touch target.
With 1.0.0 the IconButton applies a default size with the internal modifier: IconButtonSizeModifier = Modifier.size(48.dp).
You can modify it using something like:
IconButton(modifier = Modifier.
then(Modifier.size(24.dp)),
onClick = { }) {
Icon(
Icons.Filled.Search,
"contentDescription",
tint = Color.White)
}
It is important the use of .then to apply the size in the right sequence.
Wrap the IconButton with CompositionLocalProvider to override the value of LocalMinimumTouchTargetEnforcement which enforces a minimum touch target of 48.dp.
CompositionLocalProvider(
LocalMinimumTouchTargetEnforcement provides false,
) {
IconButton(onClick = { }) {
Icon(
imageVector = Icons.Filled.Search,
contentDescription = "Search",
)
}
}
If you use IconButton just for handling click listener, instead of:
IconButton(onClick = { // Todo -> handle click }) {
Icon(asset = Icons.Filled.Search)
}
You can use:
Icon(
asset = Icons.Filled.Search,
modifier = Modifier.clickable { // Todo -> handle click },
)
With this way you don't need to remove extra paddings.
Related
In LazyColumn when we use LazyListScope.items with Surface. Inside multiple items there is extra padding on TOP and BOTTOM. I want to remove this padding. I am using Surface component of Material 3. BOM version is compose_bom = "2022.11.00".
Please don't suggest any alpha or beta version fix. If Material 3 stable api don't have solution, then please suggest normal Surface Material.
PreviewCreateListView
#Preview(showBackground = true)
#Composable
fun PreviewCreateListView() {
CreateListView()
}
CreateListView
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun CreateListView() {
val itemList = listOf(1, 2, 3)
LazyColumn(
contentPadding = PaddingValues(16.dp),
) {
items(itemList) { item ->
Surface(
onClick = { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
}
}
Output
The M3 Surface with the onClick parameter has a minimum touch target size (48.dp) for accessibility. It will include extra space outside the component to ensure that they are accessible.
You can override this behaviour applying false to the LocalMinimumInteractiveComponentEnforcement. If it is set to false there will be no extra space.
Something like:
CompositionLocalProvider(
LocalMinimumInteractiveComponentEnforcement provides false) {
Surface(
onClick = { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
Note: LocalMinimumInteractiveComponentEnforcement requires at least
M2 1.4.0-alpha04 and M3 1.1.0-alpha04. Before you can use LocalMinimumTouchTargetEnforcement in the same way.
The Surface variant that you use, with a onClick parameter, enforces a minimum height for accessibility purposes, see this at line 221
If you want to remove the space, use the variant without the onClick argument and use a Modifier.clickable instead
#Composable
fun CreateListView() {
val itemList = listOf(1, 2, 3)
LazyColumn(
contentPadding = PaddingValues(16.dp),
) {
items(itemList) { item ->
Surface(
modifier = Modifier.clickable { },
color = Color.Blue
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "$item",
)
}
}
}
}
I have a few Icons in a row
Row {
IconButton {
Icon(
painter = painterResource(R.drawable.im1)
)
},
IconButton {
Icon(
painter = painterResource(R.drawable.im2)
)
}
}
But when it's displayed the distance between 2 icons in the row is bigger then I expect. I feel like there is 32dp spacer between them. How can I decrease the distance between 2 icons inside a row?
The space between the 2 icons it is not a padding and depends by the default size of the IconButton.
It is due to accessibility touch targets and allows the correct minimum touch target size.
You can change it disabling the LocalMinimumTouchTargetEnforcement and applying a Modifier.size(24.dp) to the IconButton:
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false){
Row {
IconButton(modifier = Modifier.size(24.dp),
onClick = {}) {
Icon(
painter = painterResource(R.drawable.ic_add_24px),""
)
}
IconButton(modifier = Modifier.size(24.dp),
onClick = {}) {
Icon(
painter = painterResource(R.drawable.ic_add_24px),""
)
}
}
}
As alternative you can use an Icon with the Modifier.clickable:
Row {
Icon(
painter = painterResource(R.drawable.ic_add_24px),"",
modifier = Modifier.clickable (onClick = {} )
)
Icon(
painter = painterResource(R.drawable.ic_add_24px),"",
modifier = Modifier.clickable (onClick = {} )
)
}
If you wish to control the exact distance, it is viable to implement a Layout, giving you complete control over the offset (yes you could also just use the offset Modifier, but I find this more promising.
Layout(
content = {
IconButton {
Icon(
painter = painterResource(R.drawable.im1)
)
},
IconButton {
Icon(
painter = painterResource(R.drawable.im2)
)
}
}
){ measurables, constraints ->
val icon1 = measurables[0].measure(constraints)
val icon2 = measurables[1].measure(constraints)
layout(constraints.maxWidth, constraints.maxHeight){ //Change these to suit your requirements
//Use place(x, y) to specify exact co-ordinates within the container
icon1.place(0, 0)
icon2.place(icon1.width, 0) //Places Directly where icon1 ends
/*If padding still appears, you can subtract some function from the second icon's starting point to make it even closer than the edge of iicon1
*/
}
This should be it!
I'm dipping my toes into Jetpack Compose, but I'm stumped by the behaviour of Row. I have a text next to an icon button, and I want the icon button to be anchored to the side with a minimum width of 48dp, and have text wrap around it. Like this:
But the text does not wrap, it eats up all the space in the Row:
#Composable
fun SampleLayout(text: String) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(text)
IconButton(
onClick = { },
) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Default.StarBorder,
null
)
}
}
}
#Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
#Composable
fun SamplePreview1() {
Box(Modifier.padding(16.dp)) {
SampleLayout("helooooo")
}
}
#Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
#Composable
fun SamplePreview2() {
Box(Modifier.padding(16.dp)) {
SampleLayout("helooooooooooooooooooooooooooo")
}
}
#Preview(showBackground = true, backgroundColor = 0x006EAEA0, fontScale = 1.5F)
#Composable
fun SamplePreview3() {
Box(Modifier.padding(16.dp)) {
SampleLayout("heloooooooooooooooooooooooooooooooooooooooo")
}
}
I've tried setting the minimum width of the icon 48dp, but the text then still fills until the end of the row.
How can I make sure the the Text width does not go further than the icon button?
By default Text has a higher layout priority than Icon in order to fill the necessary space. You can change this with the weight modifier.
After using this modifier, the size of Icon will be calculated before Text:
The parent will divide the vertical space remaining after measuring unweighted child elements
Also weight has a fill parameter, which is set to true by default. This is equivalent to fillMaxWidth (when weight is used inside a Row), so you can skip the fillMaxWidth modifier in your parent. When you don't need this behaviour, pass false to this parameter.
Row{
Text(text, modifier = Modifier.weight(1f))
IconButton(
onClick = { }
) {
Icon(
imageVector = Icons.Default.StarBorder,
null
)
}
}
Try this
#Composable
fun SampleLayout(text: String) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(text = text)
IconButton(
modifier = Modifier.weight(1f, fill = false), //You must add the false fill here to keep it from occupying all the available space
onClick = { },
) {
Icon(
imageVector = androidx.compose.material.icons.Icons.Default.StarBorder,
null
)
}
}
}
Hi i'm trying to implement a lazycolumn of a list of posts, I tested it on the emulator api 21 and 29 and it looks kinda smooth on the api 29 it's a little bit laggy, when I tested it on a physical device it was lagging, It looks like it's skipping frames or something..
I tried to remove some views that uses imageVector to see if that was the problem and still the same problem.
This is my composable view:
#Composable
fun HomePostView(
category: String,
imagesUrl: List<String> = listOf(imageHolder),
doctorProfileImage: String = imageUrl,
title: String,
subTitle: String
) {
Card(
shape = PostCardShape.large, modifier = Modifier
.padding(horizontal = 3.dp)
.fillMaxWidth()
) {
Column {
PostTopView(
category = category,
onOptionsClicked = { /*TODO option click*/ },
onBookmarkClicked = {/*TODO bookmark click*/ })
CoilImage(
data = imagesUrl[0],
fadeIn = true,
contentDescription = "post_image",
modifier = Modifier
.fillMaxWidth()
.requiredHeight(190.dp)
.padding(horizontal = contentPadding),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.height(10.dp))
PostDoctorContent(
doctorProfileImage = doctorProfileImage,
title = title,
subTitle = subTitle
)
Spacer(modifier = Modifier.height(contentPadding))
PostBottomView(likesCount = 293, commentsCount = 22)
Spacer(modifier = Modifier.height(contentPadding))
}
}
Spacer(modifier = Modifier.height(10.dp))
}
#Composable
private fun PostDoctorContent(doctorProfileImage: String, title: String, subTitle: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = contentPadding)
) {
CoilImage(data = doctorProfileImage,
contentScale = ContentScale.Crop,
contentDescription = null,
fadeIn = true,
modifier = Modifier
.size(30.dp)
.clip(CircleShape)
.clickable {
/*Todo on doctor profile clicked*/
})
Column {
Text(
text = title, fontSize = 14.sp, maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
Text(
text = subTitle,
fontSize = 11.sp,
color = LightTextColor,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = contentPadding)
)
}
}
}
#Composable
private fun PostBottomView(likesCount: Long, commentsCount: Long) {
Row(
modifier = Modifier.padding(horizontal = contentPadding),
verticalAlignment = Alignment.CenterVertically
) {
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on like clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_heart),
contentDescription = "Like"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = likesCount.toString(), fontSize = 9.sp)
}
Spacer(Modifier.width(20.dp))
Row(
Modifier
.clip(RoundedCornerShape(50))
.clickable { /*Todo on comment clicked*/ }
.padding(5.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_comment),
contentDescription = "Comment"
)
Spacer(modifier = Modifier.width(5.dp))
Text(text = commentsCount.toString(), fontSize = 9.sp)
}
}
}
#Composable
private fun PostTopView(
category: String,
onOptionsClicked: () -> Unit,
onBookmarkClicked: () -> Unit
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onOptionsClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_threedots),
contentDescription = "Options",
tint = Color.Unspecified
)
}
Text(text = category, fontSize = 16.sp, color = LightTextColor)
}
IconButton(onClick = onBookmarkClicked) {
Icon(
imageVector = ImageVector.vectorResource(id = R.drawable.ic_bookmark),
contentDescription = "Bookmark"
)
}
}
}
and the lazyColumn:
LazyColumn(contentPadding = paddingValues , state = state ) {
item {
Spacer(modifier = Modifier.height(10.dp))
DoctorsList(
viewModel.doctorListData.value,
onCardClicked = {})
}
items(30) { post ->
HomePostView(
category = "Public Health ",
title = "Food Importance",
subTitle = "you should eat every day it's healthy and important for you, and drink water every 2 hours and what you should do is you should run every day for an hour"
)
}
}
Note: I'm still not using a viewmodel i'm just testing the view with fake data
This may not work for anyone else but on an earlier version (1.0.0-beta01) I saw a very large improvement in performance when I switched
lazy(items) { item ->
...
}
to
items.forEach { item ->
lazy {
...
}
}
I have no idea why and I'm not sure if this is still the case in later versions but its worth checking. So for the example given in the question, that would mean changing
items(30) {
...
}
to
repeat(30) {
item {
...
}
}
TLDR: Make sure your new LazyColumn compose element is not within a RelativeLayout or LinearLayout.
After some investigation the solution to this issue for us was the view in which the LazyColumn was constrained.
Our project uses a combination of Jetpack Compose and the older XML layout files. Our new compose elements were embedded within a RelativeLayout of an existing XML file. This was where the problem was. The compose element would be given the entire screen and then the onMeasure function of the compose element was called to re-configure the view and add our bottom nav bar...this onMeasure was called over and over again, which also in the case of a LazyColumn the re-measuring was throwing out the cache as the height had changed.
The solution for us was to change the RelativeLayout that contained both the new compose element and the bottom nav bar and replace it with a ConstraintLayout. This prevented the onMeasure from being called more than twice and gave a massive performance increase.
Try to build release build with turn off debug logs. should be works fine.
Ok, So far I know that there is an issue with the API when it comes to performance ...But what I found is this
Actually, In my case, I was just loading the image with 2980*3750 pixels image. I just crunched my resources to shorter pixels by some other tools
Now the lag is not present...
In my case, after I set the height of ComposeView to a specific value, it make LazyColumn scroll smooth.
Therefore, I create a XML file like
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.compose.ui.platform.ComposeView
android:id="#+id/compose"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
super.onViewCreated(view, savedInstanceState)
view.doOnLayout {
compose.layoutParams.height = view.height
compose.requestLayout()
}
}
I know ComposeView height already match_parent so set the height for it again on Fragment seem useless. However, without setting the height, LazyColumn will lagging when scroll.
I am not sure if it will work in all device but work well on my Pixel and Xiomi.
If you are using JPGs or PNGs in your project then check for their size, images having larger size causes a lots of lagging issues on low end devices.
I was having the same issue with my simple LazyColumn list and turned out I was using a JPG having size larger than 2MBs.
In https://developer.android.com/jetpack/compose/animation
If you are animating changes to content size:
Use Modifier.contentSize.
However, I cannot find how to access Modifier.contentSize and use it. Any guide?
I think you are talking about the animateContentSize function.
Here I have an example that might help
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
// This `Column` animates its size when its content changes.
.animateContentSize()
) {
Row {
Icon(
imageVector = Icons.Default.Info,
contentDescription = null
)
// ...
}
if (expanded) { // If the expanded value is changed then the animateContentSize will be triggered
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.lorem_ipsum),
textAlign = TextAlign.Justify
)
}
}
From the Jetpack Compose Animation codelab: