Movies UI - Lists
Better Movie Icon
Jetpack Compose comes with some useful Icons, but it's a rather short list. You can see available icons at Google Fonts.
Some are in the
androidx.compose.material:material-icons-core
(pulled in when you include androidx.compose.material3:material3
as a dependency), and the rest are available in androidx.compose.material:material-icons-extended
, which you need to pull in yourself.
Warning
The extended icons dependency is rather large, and you'll likely only need a few icons from it. You can shrink your app size (see Shrink your app) or copy the source for the icons into your application. I recommend copying the source for the icons you need, as it reduces the build time.
Note that source appears to be missing or difficult to locate for some versions. At this point I'm using extended icons version 1.6.0-alpha06, which has source.
To include the movie icon:
- Set the version of
material-icons-extended
you want in the version catalog. - Define the library in the version catalog.
- Include the dependency in your
app
module's build script. (Note that it's commented-out here, as the end result of this step has already copied the icon) - Sync your build scripts (press the elephant icon on the Android Studio tool bar)
- Change the icon in
MovieListUi
. - Change the icon in the tab in
ListScaffold
.
At this point you can run the application you'll now see movie icons.
Now we need to either shrink the app to remove all of the extra icons (this slows down the build!) or copy the source for the icons we need. We'll copy the source.
- Open
MovieListUi
in the editor - Find
Icons.Default.Movie
- Control-click on
Movie
to go to its source code. (If you don't see the source you'll need to try a different version ofmaterial-icons-extended
) - Copy the package name from the source
- Right-click on
java
underapp/src/main
- Choose New > Package
- Paste the package name
- Copy the entire source (including the copyright header at the top) for the
Movie
icon - Right-click the package you just created
- Choose New > Kotlin Class/File
- Type "Movie" as the name and choose File
- Delete the package statement and paste the source code
We now have a copy of the icon.
Next, remove the huge dependency by deleting or commenting out the implementation line from app/build.gradle.kts
. Be sure to re-sync the Gradle info by clicking the elephant on the tool bar.
Note
You do not need to delete the dependency information from the version catalog. This will allow you to more easily copy in other icons if you need to at a later date. I recommend leaving it and commenting out the dependency.
Code Changes
CHANGED: /app/build.gradle.kts
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.kotlinAndroid) } kotlin { jvmToolchain(17) } android { namespace = "com.androidbyexample.movie" compileSdk = 34 defaultConfig { applicationId = "com.androidbyexample.movieui2" minSdk = 24 targetSdk = 34 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true } } buildTypes { release { isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } buildFeatures { compose = true } composeOptions { kotlinCompilerExtensionVersion = "1.5.3" } packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } } dependencies { implementation(project(":repository"))// implementation(libs.icons.extended)implementation(libs.lifecycle.compose) implementation(libs.core.ktx) implementation(libs.lifecycle.runtime.ktx) implementation(libs.activity.compose) implementation(platform(libs.compose.bom)) implementation(libs.ui) implementation(libs.ui.graphics) implementation(libs.ui.tooling.preview) implementation(libs.material3) testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) androidTestImplementation(libs.espresso.core) androidTestImplementation(platform(libs.compose.bom)) androidTestImplementation(libs.ui.test.junit4) debugImplementation(libs.ui.tooling) debugImplementation(libs.ui.test.manifest) }
ADDED: /app/src/main/java/androidx/compose/material/icons/filled/Movie.kt
/* * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package androidx.compose.material.icons.filledimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.materialIconimport androidx.compose.material.icons.materialPathimport androidx.compose.ui.graphics.vector.ImageVectorpublic val Icons.Filled.Movie: ImageVector get() { if (_movie != null) { return _movie!! } _movie = materialIcon(name = "Filled.Movie") { materialPath { moveTo(18.0f, 4.0f) lineToRelative(2.0f, 4.0f) horizontalLineToRelative(-3.0f) lineToRelative(-2.0f, -4.0f) horizontalLineToRelative(-2.0f) lineToRelative(2.0f, 4.0f) horizontalLineToRelative(-3.0f) lineToRelative(-2.0f, -4.0f) horizontalLineTo(8.0f) lineToRelative(2.0f, 4.0f) horizontalLineTo(7.0f) lineTo(5.0f, 4.0f) horizontalLineTo(4.0f) curveToRelative(-1.1f, 0.0f, -1.99f, 0.9f, -1.99f, 2.0f) lineTo(2.0f, 18.0f) curveToRelative(0.0f, 1.1f, 0.9f, 2.0f, 2.0f, 2.0f) horizontalLineToRelative(16.0f) curveToRelative(1.1f, 0.0f, 2.0f, -0.9f, 2.0f, -2.0f) verticalLineTo(4.0f) horizontalLineToRelative(-4.0f) close() } } return _movie!! }private var _movie: ImageVector? = null
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/ListScaffold.kt
package com.androidbyexample.movie.screens import androidx.annotation.StringRes import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Movieimport androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Star import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.NavigationBar import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.androidbyexample.movie.R import com.androidbyexample.movie.components.ScreenSelectButton import com.androidbyexample.movie.repository.HasId@OptIn(ExperimentalMaterial3Api::class) @Composable fun <T: HasId> ListScaffold( @StringRes titleId: Int, items: List<T>, onItemClicked: (T) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedItems: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, itemContent: @Composable ColumnScope.(T) -> Unit, ) { Scaffold( topBar = { if (selectedIds.isEmpty()) { TopAppBar( title = { Text(text = stringResource(titleId)) }, actions = { IconButton(onClick = onResetDatabase) { Icon( imageVector = Icons.Default.Refresh, contentDescription = stringResource(R.string.reset_database) ) } } ) } else { TopAppBar( navigationIcon = { Icon( imageVector = Icons.Default.ArrowBack, contentDescription = stringResource(R.string.clear_selections), modifier = Modifier.clickable(onClick = onClearSelections), ) }, title = { Text(text = selectedIds.size.toString(), modifier = Modifier.padding(8.dp)) }, actions = { IconButton(onClick = onDeleteSelectedItems) { Icon( imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.delete_selected_items) ) } }, ) } },modifier = Modifier.fillMaxSize() ) { paddingValues -> List( items = items, onItemClicked = onItemClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, modifier = Modifier .padding(paddingValues) .fillMaxSize(), itemContent = itemContent, ) } }
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/MovieListUi.kt
package com.androidbyexample.movie.screens import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons//import androidx.compose.material.icons.filled.Starimport androidx.compose.material.icons.filled.Movieimport androidx.compose.material3.Icon import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.androidbyexample.movie.R import com.androidbyexample.movie.components.Display import com.androidbyexample.movie.repository.MovieDto @Composable fun MovieListUi( movies: List<MovieDto>, onMovieClicked: (MovieDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedMovies: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, ) {ListScaffold( titleId = R.string.movies, items = movies, onItemClicked = onMovieClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedMovies, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase ) { movie -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon(}contentDescription = stringResource(id = R.string.movie), modifier = Modifier.clickable { onSelectionToggle(movie.id) } ) Display(text = movie.title) } }// imageVector = Icons.Default.Star,imageVector = Icons.Default.Movie,
CHANGED: /gradle/libs.versions.toml
[versions] agp = "8.2.0-beta05" kotlin = "1.9.10" core-ktx = "1.12.0" junit = "4.13.2" androidx-test-ext-junit = "1.1.5" espresso-core = "3.5.1" lifecycle-runtime-ktx = "2.6.2" activity-compose = "1.7.2" lifecycle-compose = "2.6.2" compose-bom = "2023.09.01" appcompat = "1.6.1" material = "1.9.0" room = "2.5.2" ksp = "1.9.10-1.0.13"icons-extended = "1.6.0-alpha06"[libraries] core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-ktx" } activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-compose" } compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } ui = { group = "androidx.compose.ui", name = "ui" } ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } material3 = { group = "androidx.compose.material3", name = "material3" } appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }icons-extended = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "icons-extended"}[plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } androidLibrary = { id = "com.android.library", version.ref = "agp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }