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!
Related
I have a slightly different expectation for padding to work. How can this problem be solved? I see this only at TopAppBar.
CenterAlignedTopAppBar(
title = { Text(title) },
modifier = Modifier.statusBarsPadding(), // or padding(top = 24.dp)
actions = {
if (isSync) {
IconButton(
onClick = { },
enabled = false,
content = {
Icon(
painter = painterResource(R.drawable.ic_outline_cloud_download_24),
contentDescription = title,
tint = Color.DarkGray.copy(alpha = alpha)
)
}
)
}
},
scrollBehavior = scrollBehavior
)
UPD
For example, here it works as it should -> link
I made it myself based on the source code. Not quite universal, but just right for me. I will not give the full code, it is in the source code. I will give only the place where it is necessary to add.
#Composable
private fun TopAppBarLayout(...) {
Layout(
...,
modifier = modifier.statusBarsPadding()
) {
...
}
}
The rest of the code is identical to the original.
UPDATE 21
Now everything works out of the box.
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
)
}
}
}
How to create BottomNavigation with one of the item is larger than the parent, but without using floatingActionButton. For example like this:
I tried to do that by wrapping the icon with Box but it get cut like this:
Then i try to separate that one button and use constraintLayout to position it, but the constraintLayout cover the screen like this. Even when i color it using Color.Transparent, it always feels like Color.White (i dont know why Color.Transparent never work for me). In this picture i give it Red color for clarity reason.
So how to do this kind of bottomNavBar without having to create heavy-custom-composable?
Update: so i try to make the code based on MARSK and Dharman comment (thanks btw). This is what i
BoxWithConstraints(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(Color.Transparent)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.background(Color.White)
.align(Alignment.BottomCenter)
)
Row(
modifier = Modifier
.zIndex(56.dp.value)
.fillMaxWidth()
.selectableGroup(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
items.forEach { item ->
val selected = item == currentSection
BottomNavigationItem(
modifier = Modifier
.align(Alignment.Bottom)
.then(
Modifier.height(
if (item == HomeSection.SCAN) 84.dp else 56.dp
)
),
selected = selected,
icon = {
if (item == HomeSection.SCAN) {
ScanButton(navController = navController, visible = true)
} else {
ImageBottomBar(
icon = if (selected) item.iconOnSelected else item.icon,
description = stringResource(id = item.title)
)
}
},
label = {
Text(
text = stringResource(item.title),
color = if (selected) Color(0xFF361DC0) else LocalContentColor.current.copy(
alpha = LocalContentAlpha.current
),
style = TextStyle(
fontFamily = RavierFont,
fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 18.sp,
),
maxLines = 1,
)
},
onClick = {
if (item.route != currentRoute && item != HomeSection.SCAN) {
navController.navigate(item.route) {
launchSingleTop = true
restoreState = true
popUpTo(findStartDestination(navController.graph).id) {
saveState = true
}
}
}
}
)
}
}
}
It works in preview, but doesn't work when i try in app.
This one in the preview, the transparent working as expected:
And this is when i try to launch it, the transparent doesnt work:
Note: I assign that to bottomBar of Scaffold so i could access the navigation component. Is it the cause that Transparent Color doesnt work?
Update 2: so the inner paddingValues that makes the transparent doesnt work. I fixed it by set the padding bottom manually:
PaddingValues(
start = paddingValues.calculateStartPadding(
layoutDirection = LayoutDirection.Ltr
),
end = paddingValues.calculateEndPadding(
layoutDirection = LayoutDirection.Ltr
),
top = paddingValues.calculateTopPadding(),
bottom = SPACE_X7,
)
Custom Composable are not heavy, really.
Anyway, try this:-
Create a Container of MaxWidth (maybe a BoxWithConstraints or something), keep its background transparent, set the height to wrap content. Create the tabs as usual, but keeping the bigger tab's icon size bigger explicitly using Modifier.size(Bigger Size).
After you have this setup, add another container inside this container with white background, covering a specific height of the original container. Let's say 60%
Now set the z-index of all the icons and tabs to higher than the z-index of this lastly added container. Use Modifier.zIndex for this. And viola, you have your Composable ready.
In order to set a specific percentage height of the inner container, you will need access to the height of the original container. Use BoxWithConstraints for that, or just implement a simple custom Layout Composable
Image I have a normal Button in Android and a Star icon. I would like to compose them into a new Button icon, where the star is in one of the upper corners like here:
When I use Row both are seperated. As you can see, the star shall overlap the Button in one of its corner. How can I do that?
EDIT: Thanks to Gabriele Mariotti I used
Box {
Button(
id = "btnButton",
modifier = Modifier
.padding(end = 48)
onClick = {
//..
}
)
IconWithStar(
modifier = Modifier
.scale(0.65f)
)
}
Star icon is bound to upper left corner, how would I modify that?
You can wrap the composables with a Box and use the align/offset modifier to adjust the positions of them.
Box(Modifier.padding(top=40.dp)){
Button(
onClick = {})
{
Text("Hello World")
}
Icon(
Icons.Filled.Star, "",
modifier =Modifier
.align(TopEnd)
.offset(12.dp,-12.dp),
tint = Yellow600
)
}
To have more control you can build a custom Layout.
Something like:
Layout( content = {
Button(
modifier = Modifier.layoutId("button"),
onClick = { /* ... */ })
{
Text("Hello World")
}
Icon(Icons.Filled.Star, "",
Modifier.layoutId("icon"),
tint = Yellow600)
}){ measurables, incomingConstraints ->
val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
val buttonPlaceable =
measurables.find { it.layoutId == "button" }?.measure(constraints)
val iconPlaceable =
measurables.find { it.layoutId == "icon" }?.measure(constraints)
//align the icon on the top/end edge
layout(width = widthOrZero(buttonPlaceable) + widthOrZero(iconPlaceable)/2,
height = heightOrZero(buttonPlaceable)+ heightOrZero(iconPlaceable)/2){
buttonPlaceable?.placeRelative(0, heightOrZero(iconPlaceable)/2)
iconPlaceable?.placeRelative(widthOrZero(buttonPlaceable)- widthOrZero(iconPlaceable)/2,
0)
}
}
internal fun widthOrZero(placeable: Placeable?) = placeable?.width ?: 0
internal fun heightOrZero(placeable: Placeable?) = placeable?.height ?: 0
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.