Allows overriding Icon's intrisic size
Previously the default content scale for Icon was `Inside`, so making the `Icon` bigger would not make it draw to fit the size. Now changing the size of the icon will scale up the drawing to fill the dimensions. For advanced customization / use cases, Image / Modifier.paint() provide more flexibility.
Updates the ListSamples accordingly, and removes unused resources.
Fixes: b/178796190
Fixes: b/159689286
Test: IconTest
Test: Demos
Relnote: "Icon will now scale up to fit its size, respecting size modifiers applied to it. For example Icon(.., modifier = Modifier.size(50.dp) will now draw in a 50x50dp space."
Change-Id: Ib2ba987760dbd038d075e720cf705eef99456f15
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
index 472aa2e..ca38f424 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
@@ -17,8 +17,6 @@
package androidx.compose.material.demos
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Call
import androidx.compose.material.samples.ClickableListItems
import androidx.compose.material.samples.OneLineListItems
import androidx.compose.material.samples.OneLineRtlLtrListItems
@@ -27,44 +25,36 @@
import androidx.compose.material.samples.TwoLineListItems
import androidx.compose.material.samples.TwoLineRtlLtrListItems
import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
-import androidx.compose.ui.res.painterResource
@Composable
fun ListItemDemo() {
- val icon24 = painterResource(R.drawable.ic_bluetooth)
- val icon40 = painterResource(R.drawable.ic_account_box)
- val icon56 = painterResource(R.drawable.ic_android)
- val vectorIcon = rememberVectorPainter(Icons.Default.Call)
LazyColumn {
item {
ClickableListItems()
}
item {
- OneLineListItems(icon24, icon40, icon56, vectorIcon)
+ OneLineListItems()
}
item {
- TwoLineListItems(icon24, icon40)
+ TwoLineListItems()
}
item {
- ThreeLineListItems(icon24, vectorIcon)
+ ThreeLineListItems()
}
}
}
@Composable
fun MixedRtlLtrListItemDemo() {
- val icon24 = painterResource(R.drawable.ic_bluetooth)
- val icon40 = painterResource(R.drawable.ic_account_box)
LazyColumn {
item {
- OneLineRtlLtrListItems(icon24, icon40)
+ OneLineRtlLtrListItems()
}
item {
- TwoLineRtlLtrListItems(icon40)
+ TwoLineRtlLtrListItems()
}
item {
- ThreeLineRtlLtrListItems(icon40)
+ ThreeLineRtlLtrListItems()
}
}
}
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png
deleted file mode 100755
index cdd05cc..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png
deleted file mode 100755
index 4696f99..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png
deleted file mode 100755
index 7bc2402..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png
deleted file mode 100644
index 5a76a28..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png
deleted file mode 100755
index 12bfa83..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png
deleted file mode 100755
index abf6b5e..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png
deleted file mode 100755
index c505e9a..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png
deleted file mode 100644
index 3eb94c2..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png
deleted file mode 100755
index 32499a4..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png
deleted file mode 100755
index 2cdeec9..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png
deleted file mode 100755
index ae3dc2a..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png
deleted file mode 100644
index 5db606c..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png
deleted file mode 100755
index ad831c3..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png
deleted file mode 100755
index 86ebf7f..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png
deleted file mode 100755
index 62c427d..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png
deleted file mode 100644
index be0d542..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
index 04ed988..5fefa61 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
@@ -17,27 +17,27 @@
package androidx.compose.material.samples
import androidx.annotation.Sampled
-import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.selection.toggleable
import androidx.compose.material.Checkbox
import androidx.compose.material.Divider
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.Icon
import androidx.compose.material.ListItem
-import androidx.compose.material.LocalContentColor
import androidx.compose.material.Switch
import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.unit.dp
@Sampled
@OptIn(ExperimentalMaterialApi::class)
@@ -89,22 +89,16 @@
@Sampled
@Composable
-fun OneLineListItems(
- icon24x24: Painter,
- icon40x40: Painter,
- icon56x56: Painter,
- vectorIcon: Painter
-) {
+fun OneLineListItems() {
Column {
ListItem(text = { Text("One line list item with no icon") })
Divider()
ListItem(
text = { Text("One line list item with 24x24 icon") },
icon = {
- Image(
- icon24x24,
- contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = null
)
}
)
@@ -112,10 +106,10 @@
ListItem(
text = { Text("One line list item with 40x40 icon") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
@@ -123,10 +117,10 @@
ListItem(
text = { Text("One line list item with 56x56 icon") },
icon = {
- Image(
- icon56x56,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(56.dp)
)
}
)
@@ -134,10 +128,10 @@
ListItem(
text = { Text("One line clickable list item") },
icon = {
- Image(
- icon56x56,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(56.dp)
)
},
modifier = Modifier.clickable { }
@@ -145,19 +139,29 @@
Divider()
ListItem(
text = { Text("One line list item with trailing icon") },
- trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
+ trailing = {
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = "Localized Description"
+ )
+ }
)
Divider()
ListItem(
text = { Text("One line list item") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
},
- trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
+ trailing = {
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = "Localized description"
+ )
+ }
)
Divider()
}
@@ -165,8 +169,7 @@
@Sampled
@Composable
-// TODO(popam, b/159689286): material icons instead of Painter when they can have custom sizes
-fun TwoLineListItems(icon24x24: Painter, icon40x40: Painter) {
+fun TwoLineListItems() {
Column {
ListItem(
text = { Text("Two line list item") },
@@ -182,10 +185,9 @@
text = { Text("Two line list item with 24x24 icon") },
secondaryText = { Text("Secondary text") },
icon = {
- Image(
- icon24x24,
- contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = null
)
}
)
@@ -194,10 +196,10 @@
text = { Text("Two line list item with 40x40 icon") },
secondaryText = { Text("Secondary text") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
@@ -207,10 +209,10 @@
secondaryText = { Text("Secondary text") },
trailing = { Text("meta") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
@@ -220,7 +222,7 @@
@Sampled
@Composable
-fun ThreeLineListItems(icon24x24: Painter, vectorIcon: Painter) {
+fun ThreeLineListItems() {
Column {
ListItem(
text = { Text("Three line list item") },
@@ -250,10 +252,9 @@
},
singleLineSecondaryText = false,
icon = {
- Image(
- icon24x24,
- contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = null
)
}
)
@@ -267,7 +268,12 @@
)
},
singleLineSecondaryText = false,
- trailing = { Icon(vectorIcon, "Localized description") }
+ trailing = {
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = "Localized description"
+ )
+ }
)
Divider()
ListItem(
@@ -283,17 +289,17 @@
// Demos for mixing RTL and LTR ListItems:
@Composable
-fun OneLineRtlLtrListItems(icon24x24: Painter, icon40x40: Painter) {
+fun OneLineRtlLtrListItems() {
Column {
ListItem(text = { Text("One line list item with no icon") })
Divider()
ListItem(
text = { Text("פריט ברשימה אחת עם תמונה.") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
@@ -301,10 +307,9 @@
ListItem(
text = { Text("One line list item with 24x24 icon") },
icon = {
- Image(
- icon40x40,
- contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = null
)
}
)
@@ -312,10 +317,9 @@
ListItem(
text = { Text("عنصر قائمة واحد مع رمز زائدة") },
trailing = {
- Image(
- icon24x24,
- contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ Icon(
+ Icons.Filled.Favorite,
+ contentDescription = null
)
}
)
@@ -324,7 +328,7 @@
}
@Composable
-fun TwoLineRtlLtrListItems(icon40x40: Painter) {
+fun TwoLineRtlLtrListItems() {
Column {
ListItem(
text = { Text("Two line list item") },
@@ -346,10 +350,10 @@
text = { Text("عنصر قائمة مكون من سطرين مع رمز") },
overlineText = { Text("فوق الخط") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
@@ -358,10 +362,10 @@
text = { Text("بندان قابلان للنقر") },
secondaryText = { Text("نص ثانوي") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
},
modifier = Modifier.clickable { }
@@ -371,7 +375,7 @@
}
@Composable
-fun ThreeLineRtlLtrListItems(icon40x40: Painter) {
+fun ThreeLineRtlLtrListItems() {
Column {
ListItem(
text = { Text("Three line list item") },
@@ -396,10 +400,10 @@
overlineText = { Text("فوق الخط") },
secondaryText = { Text("نص ثانوي") },
icon = {
- Image(
- icon40x40,
+ Icon(
+ Icons.Filled.Favorite,
contentDescription = null,
- colorFilter = ColorFilter.tint(LocalContentColor.current)
+ modifier = Modifier.size(40.dp)
)
}
)
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
index f32da1f..6a9b98d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
@@ -16,6 +16,7 @@
package androidx.compose.material
import android.os.Build
+import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.testutils.assertPixels
@@ -40,6 +41,7 @@
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -150,11 +152,50 @@
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
@Test
+ fun iconScalesToFitSize() {
+ // Image with intrinsic size of 24dp
+ val width = 24.dp
+ val height = 24.dp
+ val testTag = "testTag"
+ var expectedIntSize: IntSize? = null
+ rule.setMaterialContent {
+ val image: ImageBitmap
+ with(LocalDensity.current) {
+ image = createBitmapWithColor(
+ this,
+ width.roundToPx(),
+ height.roundToPx(),
+ Color.Red
+ )
+ }
+ Icon(
+ image,
+ null,
+ // Force Icon to be 50dp
+ modifier = Modifier.size(50.dp).testTag(testTag),
+ tint = Color.Unspecified
+ )
+ with(LocalDensity.current) {
+ val dimension = 50.dp.roundToPx()
+ expectedIntSize = IntSize(dimension, dimension)
+ }
+ }
+
+ rule.onNodeWithTag(testTag)
+ .captureToImage()
+ // The icon should be 50x50 and fill the whole size with red pixels
+ .assertPixels(expectedSize = expectedIntSize!!) {
+ Color.Red
+ }
+ }
+
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+ @Test
fun iconUnspecifiedTintColorIgnored() {
val width = 35.dp
val height = 83.dp
val testTag = "testTag"
- rule.setMaterialContentForSizeAssertions {
+ rule.setMaterialContent {
val image: ImageBitmap
with(LocalDensity.current) {
image = createBitmapWithColor(
@@ -177,7 +218,7 @@
val width = 35.dp
val height = 83.dp
val testTag = "testTag"
- rule.setMaterialContentForSizeAssertions {
+ rule.setMaterialContent {
val image: ImageBitmap
with(LocalDensity.current) {
image = createBitmapWithColor(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
index 2eeb8a2..c00e0fd 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
@@ -31,6 +31,7 @@
import androidx.compose.ui.graphics.toolingGraphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.role
@@ -128,7 +129,11 @@
}
Box(
modifier.toolingGraphicsLayer().defaultSizeFor(painter)
- .paint(painter, colorFilter = colorFilter)
+ .paint(
+ painter,
+ colorFilter = colorFilter,
+ contentScale = ContentScale.Fit
+ )
.then(semantics)
)
}