1. Sebelum memulai
Selamat! Di pembelajaran ini, Anda telah mempelajari dasar-dasar Desain Material dan cara menambahkan animasi sederhana ke aplikasi Anda. Sekarang saatnya menerapkan apa yang telah Anda pelajari.
Dalam kumpulan latihan ini, Anda akan menerapkan konsep yang telah dipelajari di pembelajaran ini dengan membuat aplikasi Superheroes. Aplikasi ini berfokus pada pembuatan komponen yang diperlukan untuk membangun daftar yang dapat di-scroll dan UI yang dipoles menggunakan prinsip Desain Material yang telah Anda pelajari di codelab Penerapan Tema Material dengan Jetpack Compose.
Kode solusi tersedia di bagian akhir, tetapi coba selesaikan latihan ini sebelum Anda memeriksanya. Pertimbangkan solusinya sebagai salah satu cara untuk menerapkan aplikasi. Ada banyak peningkatan yang dapat dilakukan, jadi jangan ragu untuk bereksperimen dan mencoba berbagai hal.
Selesaikan semua soal latihan tanpa terburu-buru. Sebaiknya manfaatkan waktu semaksimal mungkin untuk menyelesaikan setiap soal dengan cermat.
Prasyarat
- Selesaikan kursus Dasar-Dasar Android di Compose melalui Animasi Sederhana dengan Jetpack Compose.
Yang Anda butuhkan
- Komputer yang dilengkapi akses internet dan Android Studio.
Yang akan Anda bangun
Aplikasi Superheroes yang menampilkan daftar pahlawan super.
Aplikasi final akan terlihat seperti berikut dalam tema terang dan gelap:
2. Memulai
Dalam tugas ini, Anda akan menyiapkan project dan membuat data model untuk pahlawan super.
- Buat project baru dengan template Empty Activity dan SDK minimum 24.
- Download aset untuk aplikasi: gambar pahlawan super dan logo aplikasi dari sini. Baca codelab Mengubah ikon aplikasi untuk mengingat kembali cara menambahkan ikon aplikasi. Baca codelab Membuat aplikasi Dice Roller interaktif untuk mengingat kembali cara menambahkan gambar ke aplikasi Anda.
- Download file font Cabin biasa dan Cabin tebal dari https://fonts.google.com. Jelajahi berbagai file font yang tersedia. Baca codelab Penerapan Tema Material dengan Jetpack Compose untuk menyesuaikan tipografi di aplikasi Anda.
- Buat class data untuk menyimpan data setiap pahlawan super. Buat paket baru bernama
model
untuk class dataHero
guna mengatur kode Anda. Item daftar Anda mungkin terlihat seperti berikut:
Setiap item daftar pahlawan super menampilkan tiga informasi unik: nama, deskripsi, dan gambar.
- Dalam paket
model
yang sama, buat file lain untuk semua informasi pahlawan yang ingin Anda tampilkan. Misalnya, nama, deskripsi, dan resource gambar. Berikut ini contoh set data yang akan menginspirasi Anda.
object HeroesRepository {
val heroes = listOf(
Hero(
nameRes = R.string.hero1,
descriptionRes = R.string.description1,
imageRes = R.drawable.android_superhero1
),
Hero(
nameRes = R.string.hero2,
descriptionRes = R.string.description2,
imageRes = R.drawable.android_superhero2
),
Hero(
nameRes = R.string.hero3,
descriptionRes = R.string.description3,
imageRes = R.drawable.android_superhero3
),
Hero(
nameRes = R.string.hero4,
descriptionRes = R.string.description4,
imageRes = R.drawable.android_superhero4
),
Hero(
nameRes = R.string.hero5,
descriptionRes = R.string.description5,
imageRes = R.drawable.android_superhero5
),
Hero(
nameRes = R.string.hero6,
descriptionRes = R.string.description6,
imageRes = R.drawable.android_superhero6
)
)
}
- Tambahkan nama pahlawan serta string deskripsi dalam file strings.xml.
<resources>
<string name="app_name">Superheroes</string>
<string name="hero1">Nick the Night and Day</string>
<string name="description1">The Jetpack Hero</string>
<string name="hero2">Reality Protector</string>
<string name="description2">Understands the absolute truth</string>
<string name="hero3">Andre the Giant</string>
<string name="description3">Mimics the light and night to blend in</string>
<string name="hero4">Benjamin the Brave</string>
<string name="description4">Harnesses the power of canary to develop bravely</string>
<string name="hero5">Magnificent Maru</string>
<string name="description5">Effortlessly glides in to save the day</string>
<string name="hero6">Dynamic Yasmine</string>
<string name="description6">Ability to shift to any form and energize</string>
</resources>
3. Penerapan Tema Material
Di bagian ini, Anda akan menambahkan palet warna, tipografi, dan bentuk aplikasi untuk meningkatkan tampilan dan nuansa aplikasi.
Warna, Jenis, dan Bentuk berikut hanyalah rekomendasi untuk tema tersebut. Jelajahi dan ubah berbagai skema warna.
Gunakan Material Theme Builder untuk membuat tema baru aplikasi.
Warna
ui.theme/Color.kt
import androidx.compose.ui.graphics.Color
val md_theme_light_primary = Color(0xFF466800)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFC6F181)
val md_theme_light_onPrimaryContainer = Color(0xFF121F00)
val md_theme_light_secondary = Color(0xFF596248)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFDDE6C6)
val md_theme_light_onSecondaryContainer = Color(0xFF161E0A)
val md_theme_light_tertiary = Color(0xFF396661)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFBCECE6)
val md_theme_light_onTertiaryContainer = Color(0xFF00201D)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFEFCF5)
val md_theme_light_onBackground = Color(0xFF1B1C18)
val md_theme_light_surface = Color(0xFFFEFCF5)
val md_theme_light_onSurface = Color(0xFF1B1C18)
val md_theme_light_surfaceVariant = Color(0xFFE1E4D4)
val md_theme_light_onSurfaceVariant = Color(0xFF45483D)
val md_theme_light_outline = Color(0xFF75786C)
val md_theme_light_inverseOnSurface = Color(0xFFF2F1E9)
val md_theme_light_inverseSurface = Color(0xFF30312C)
val md_theme_light_inversePrimary = Color(0xFFABD468)
val md_theme_light_surfaceTint = Color(0xFF466800)
val md_theme_light_outlineVariant = Color(0xFFC5C8B9)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFFABD468)
val md_theme_dark_onPrimary = Color(0xFF223600)
val md_theme_dark_primaryContainer = Color(0xFF344E00)
val md_theme_dark_onPrimaryContainer = Color(0xFFC6F181)
val md_theme_dark_secondary = Color(0xFFC1CAAB)
val md_theme_dark_onSecondary = Color(0xFF2B331D)
val md_theme_dark_secondaryContainer = Color(0xFF414A32)
val md_theme_dark_onSecondaryContainer = Color(0xFFDDE6C6)
val md_theme_dark_tertiary = Color(0xFFA0D0CA)
val md_theme_dark_onTertiary = Color(0xFF013733)
val md_theme_dark_tertiaryContainer = Color(0xFF1F4E4A)
val md_theme_dark_onTertiaryContainer = Color(0xFFBCECE6)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1B1C18)
val md_theme_dark_onBackground = Color(0xFFE4E3DB)
val md_theme_dark_surface = Color(0xFF1B1C18)
val md_theme_dark_onSurface = Color(0xFFE4E3DB)
val md_theme_dark_surfaceVariant = Color(0xFF45483D)
val md_theme_dark_onSurfaceVariant = Color(0xFFC5C8B9)
val md_theme_dark_outline = Color(0xFF8F9285)
val md_theme_dark_inverseOnSurface = Color(0xFF1B1C18)
val md_theme_dark_inverseSurface = Color(0xFFE4E3DB)
val md_theme_dark_inversePrimary = Color(0xFF466800)
val md_theme_dark_surfaceTint = Color(0xFFABD468)
val md_theme_dark_outlineVariant = Color(0xFF45483D)
val md_theme_dark_scrim = Color(0xFF000000)
Bentuk
ui.theme/Shape.kt
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(16.dp),
large = RoundedCornerShape(16.dp)
)
Tipografi
ui.theme/Type.kt
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.example.superheroes.R
val Cabin = FontFamily(
Font(R.font.cabin_regular, FontWeight.Normal),
Font(R.font.cabin_bold, FontWeight.Bold)
)
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = Cabin,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
),
displayLarge = TextStyle(
fontFamily = Cabin,
fontWeight = FontWeight.Normal,
fontSize = 30.sp
),
displayMedium = TextStyle(
fontFamily = Cabin,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
),
displaySmall = TextStyle(
fontFamily = Cabin,
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
)
Tema
ui.theme/Theme.kt
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun SuperheroesTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
// Dynamic color in this app is turned off for learning purposes
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColors
else -> LightColors
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
window.statusBarColor = colorScheme.background.toArgb()
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}
4. Menampilkan daftar
Langkah pertama dalam membuat daftar adalah membuat item daftar.
- Buat file dengan nama
HeroesScreen.kt
, pada paketcom.example.superheroes
. Anda akan membuat item daftar dan composable daftar dalam file ini. - Buat composable untuk menampilkan item daftar pahlawan super, yang terlihat seperti screenshot dan spesifikasi UI berikut.
Ikuti spesifikasi UI ini atau berkreasilah dan desain item daftar Anda sendiri:
- Elevasi kartu adalah
2dp
- Tinggi item daftar adalah
72dp
dengan padding16dp
- Radius klip item daftar adalah
16dp
- Tata letak
Box
dengan gambar dengan ukuran72dp
- Radius klip gambar adalah
8dp
- Ruang antara gambar dan teks adalah
16dp
- Gaya untuk nama pahlawan super adalah
DisplaySmall
- Gaya untuk deskripsi pahlawan super adalah
BodyLarge
Pelajari berbagai opsi padding dan ukuran, sesuai dengan panduan Material 3, padding harus berupa kelipatan 4dp
.
Membuat kolom lambat
- Buat composable lain yang mengambil daftar pahlawan dan menampilkan daftar. Di sinilah Anda menggunakan
LazyColumn
. - Gunakan spesifikasi UI berikut untuk padding.
Setelah Anda menyelesaikan implementasi, aplikasi Anda akan terlihat seperti screenshot berikut:
5. Menambahkan panel aplikasi Atas
Tambahkan panel aplikasi atas untuk aplikasi Anda.
- Di
MainActivity.kt
, tambahkan composable untuk menampilkan panel aplikasi atas. Tambahkan teks ke panel aplikasi atas; dapat berupa nama aplikasi. Sejajarkan di tengah secara horizontal dan vertikal. - Anda dapat menetapkan panel aplikasi atas dengan gaya sebagai
DisplayLarge
.
- Gunakan
scaffold
untuk menampilkan panel aplikasi atas. Lihat dokumentasi Panel aplikasi atas – Desain Material 3 jika diperlukan.
Menyesuaikan warna status bar
Untuk membuat aplikasi ditampilkan dalam layar penuh, Anda dapat menyesuaikan warna status bar agar cocok dengan warna latar belakang.
- Di
Theme.kt
, tambahkan metode baru ini untuk mengubah warna status bar dan menu navigasi layar penuh.
/**
* Sets up edge-to-edge for the window of this [view]. The system icon colors are set to either
* light or dark depending on whether the [darkTheme] is enabled or not.
*/
private fun setUpEdgeToEdge(view: View, darkTheme: Boolean) {
val window = (view.context as Activity).window
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = Color.Transparent.toArgb()
val navigationBarColor = when {
Build.VERSION.SDK_INT >= 29 -> Color.Transparent.toArgb()
Build.VERSION.SDK_INT >= 26 -> Color(0xFF, 0xFF, 0xFF, 0x63).toArgb()
// Min sdk version for this app is 24, this block is for SDK versions 24 and 25
else -> Color(0x00, 0x00, 0x00, 0x50).toArgb()
}
window.navigationBarColor = navigationBarColor
val controller = WindowCompat.getInsetsController(window, view)
controller.isAppearanceLightStatusBars = !darkTheme
controller.isAppearanceLightNavigationBars = !darkTheme
}
- Di fungsi
SuperheroesTheme()
, panggil fungsisetUpEdgeToEdge()
dari dalam blokSideEffect
.
fun SuperheroesTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
// Dynamic color in this app is turned off for learning purposes
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
//...
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
setUpEdgeToEdge(view, darkTheme)
}
}
//...
}
6. Mendapatkan kode solusi
Guna mendownload kode untuk codelab yang sudah selesai, Anda dapat menggunakan perintah git ini:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-superheroes.git
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi, lihat di GitHub.