Tugas Pertemuan 7 - Membuat Aplikasi Woof

 Tugas Pertemuan 7

Membuat Aplikasi Woof


Nama : Rycahaya Sri Hutomo
NRP : 5025201046
Kelas : Pemrograman Perangkat Bergerak B
Tahun : 2024

Deskripsi Singkat Aplikasi

Aplikasi bernama Woof ini merupakan aplikasi yang menampilkan daftar anjing. Aplikasi ini menggunakan Desain Material untuk membuat pengalaman aplikasi yang menarik.


Download Kode Awal

Kode awal dapat diunduh melalui link berikut.

Download Zip Kode Awal


Mempelajari Kode Awal

1. Buka kode awal di Android Studio.

2. File com.example.woof > data > Dog.kt berisi Dog data class yang mewakili foto, nama, usia, dan hobi anjing, serta berisi daftar anjing dan informasi yang digunakan sebagai data di aplikasi.

3. Forder res > drawable berisi semua aset gambar termasuk ikon aplikasi, gambar anjing, dan ikon.

4. File res > values > strings.xml berisi string yang digunakan dalam aplikasi, termasuk nama aplikasi, nama anjing, deskripsinya, dan lainnya.

5. File MainActivity.kt berisi kode untuk membuat daftar sederhana yang menampilkan foto dan informasi anjing tersebut.

6. WoofApp() berisi LazyColumn yang menampilkan DogItem.

7. DogItem() berisi Row yang menampilkan foto dan informasi anjing.

8. DogIcon() menampilkan foto anjing.

9. DogInformation() menampilkan informasi anjing berupa nama dan usia.

10. WoofPreview() menampilkan pratinjau aplikasi di panel Design.


Tampilan kode awal beserta pratinjau aplikasi.


Memastikan Emulator/Perangkat Bertema Terang

1. Buka aplikasi Setelan di perangkat.

2. Telusuri Tema gelap dan klik di dalamnya.

3. Nonaktifkan, jika Tema gelap aktif.

Setelan Tema gelap di perangkat.


Menambahkan Warna

1. Buka file Color.kt dan ganti konten dengan kode seperti berikut.

package com.example.woof.ui.theme

import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF006C4C)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFF89F8C7)
val md_theme_light_onPrimaryContainer = Color(0xFF002114)
val md_theme_light_secondary = Color(0xFF4D6357)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFCFE9D9)
val md_theme_light_onSecondaryContainer = Color(0xFF092016)
val md_theme_light_tertiary = Color(0xFF3D6373)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFC1E8FB)
val md_theme_light_onTertiaryContainer = Color(0xFF001F29)
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(0xFFFBFDF9)
val md_theme_light_onBackground = Color(0xFF191C1A)
val md_theme_light_surface = Color(0xFFFBFDF9)
val md_theme_light_onSurface = Color(0xFF191C1A)
val md_theme_light_surfaceVariant = Color(0xFFDBE5DD)
val md_theme_light_onSurfaceVariant = Color(0xFF404943)
val md_theme_light_outline = Color(0xFF707973)
val md_theme_light_inverseOnSurface = Color(0xFFEFF1ED)
val md_theme_light_inverseSurface = Color(0xFF2E312F)
val md_theme_light_inversePrimary = Color(0xFF6CDBAC)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF006C4C)
val md_theme_light_outlineVariant = Color(0xFFBFC9C2)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFF6CDBAC)
val md_theme_dark_onPrimary = Color(0xFF003826)
val md_theme_dark_primaryContainer = Color(0xFF005138)
val md_theme_dark_onPrimaryContainer = Color(0xFF89F8C7)
val md_theme_dark_secondary = Color(0xFFB3CCBE)
val md_theme_dark_onSecondary = Color(0xFF1F352A)
val md_theme_dark_secondaryContainer = Color(0xFF354B40)
val md_theme_dark_onSecondaryContainer = Color(0xFFCFE9D9)
val md_theme_dark_tertiary = Color(0xFFA5CCDF)
val md_theme_dark_onTertiary = Color(0xFF073543)
val md_theme_dark_tertiaryContainer = Color(0xFF244C5B)
val md_theme_dark_onTertiaryContainer = Color(0xFFC1E8FB)
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(0xFF191C1A)
val md_theme_dark_onBackground = Color(0xFFE1E3DF)
val md_theme_dark_surface = Color(0xFF191C1A)
val md_theme_dark_onSurface = Color(0xFFE1E3DF)
val md_theme_dark_surfaceVariant = Color(0xFF404943)
val md_theme_dark_onSurfaceVariant = Color(0xFFBFC9C2)
val md_theme_dark_outline = Color(0xFF8A938C)
val md_theme_dark_inverseOnSurface = Color(0xFF191C1A)
val md_theme_dark_inverseSurface = Color(0xFFE1E3DF)
val md_theme_dark_inversePrimary = Color(0xFF006C4C)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF6CDBAC)
val md_theme_dark_outlineVariant = Color(0xFF404943)
val md_theme_dark_scrim = Color(0xFF000000)

2. Buka file Theme.kt dan ganti konten dengan kode berikut.

package com.example.woof.ui.theme

import android.app.Activity
import android.os.Build
import android.view.View
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.Color
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 WoofTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    // Dynamic color is available on Android 12+
    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 {
            setUpEdgeToEdge(view, darkTheme)
        }
    }

    MaterialTheme(
        colorScheme = colorScheme,
        shapes = Shapes,
        typography = Typography,
        content = content
    )
}

/**
 * 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
}

3. Jalankan aplikasi dan lihat warna panel aplikasi telah berubah.


Pemetaan Warna

1. Dalam fungsi composable DogItem(), gabungkan Row() dengan Card().

Card() {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(dimensionResource(id = R.dimen.padding_small))
   ) {
       DogIcon(dog.imageResourceId)
       DogInformation(dog.name, dog.age)
   }
}

2. Teruskan pengubah dari DogItem() ke Card, dan perbarui pengubah Row ke instance baru Modifier.

Card(modifier = modifier) {
   Row(
       modifier = Modifier
           .fillMaxWidth()
           .padding(dimensionResource(id = R.dimen.padding_small))
   ) {
       DogIcon(dog.imageResourceId)
       DogInformation(dog.name, dog.age)
   }
}

3. Lihat pratinjau aplikasi.

Pratinjau aplikasi

File Dimensi

1. Di WoofApp(), tambahkan modifier dengan padding_small dalam panggilan ke DogItem().

@Composable
fun WoofApp() {
    Scaffold { it ->
        LazyColumn(contentPadding = it) {
            items(dogs) {
                DogItem(
                    dog = it,
                    modifier = Modifier.padding(dimensionResource(R.dimen.padding_small))
                )
            }
        }
    }
}

2. Tampilkan pratinjau aplikasi.

Pratinjau aplikasi setelah diberi padding_small.

Tema Gelap

1. Di bagian WoofPreview(), ubah boolean darkTheme menjadi true.

@Preview
@Composable
fun WoofDarkThemePreview() {
   WoofTheme(darkTheme = true) {
       WoofApp()
   }
}

Tampilan pratinjau dalam Tema gelap

2. Ubah pengaturan tema gelap di perangkat/emulator.

Tampilan Tema gelap di perangkat atau emulator.

3. Karena fokus aplikasi saat ini pada Tema terang, kembalikan setelan aplikasi dengan menonaktifkan tema gelap.

Warna Dinamis

Warna Dinamis membuat tema untuk aplikasi berdasarkan wallpaper pengguna. 

1. Untuk mengaktifkan warna dinamis, buka Theme.kt dan buka composable WoofTheme(), lalu setel parameter dynamicColor ke true.

@Composable
fun WoofTheme(
   darkTheme: Boolean = isSystemInDarkTheme(),
   dynamicColor: Boolean = true,
   content: @Composable () -> Unit
)

Tampilan aplikasi dengan warna dinamis.

2. Karena fokus aplikasi saat ini, yaitu tema custom. kembalikan dynamicColor menjadi false.

@Composable
fun WoofTheme(
   darkTheme: Boolean = isSystemInDarkTheme(),
   dynamicColor: Boolean = false,
   content: @Composable () -> Unit
)

Menambahkan Bentuk

Membentuk gambar anjing menjadi lingkaran

1. Buka file Shape.kt dan perhatikan bahwa parameter kecil ditetapkan ke RoundedCornerShape(50.dp). Ini akan digunakan untuk membentuk gambar menjadi lingkaran.

val Shapes = Shapes(
   small = RoundedCornerShape(50.dp),
)

2. Buka MainActivity.kt. Di DogIcon(), tambahkan atribut clip ke modifier dari Image; tindakan ini akan memotong gambar menjadi sebuah bentuk. Teruskan MaterialTheme.shapes.small.

import androidx.compose.ui.draw.clip

@Composable
fun DogIcon(
   @DrawableRes dogIcon: Int,
   modifier: Modifier = Modifier
) {
   Image(
       modifier = modifier
           .size(dimensionResource(id = R.dimen.image_size))
           .padding(dimensionResource(id = R.dimen.padding_small))
           .clip(MaterialTheme.shapes.small),

Pratinjau aplikasi dengan bentuk gambar anjing lingkaran namun sebagian terpotong.


3. Untuk membuat semua foto menjadi lingkaran, tambahkan atribut ContentScale dan Crop; tindakan ini akan memangkas gambar agar sesuai. Perlu diketahui bahwa contentScale adalah atribut dari Image, dan bukan bagian dari modifier.

import androidx.compose.ui.layout.ContentScale

@Composable
fun DogIcon(
   @DrawableRes dogIcon: Int,
   modifier: Modifier = Modifier
) {
   Image(
       modifier = modifier
           .size(dimensionResource(id = R.dimen.image_size))
           .padding(dimensionResource(id = R.dimen.padding_small))
           .clip(MaterialTheme.shapes.small),
       contentScale = ContentScale.Crop,

Kode DogIcon() yang lengkap.

@Composable
fun DogIcon(
    @DrawableRes dogIcon: Int,
    modifier: Modifier = Modifier
) {
    Image(
        modifier = modifier
            .size(dimensionResource(R.dimen.image_size))
            .padding(dimensionResource(R.dimen.padding_small))
            .clip(MaterialTheme.shapes.small),
        contentScale = ContentScale.Crop,
        painter = painterResource(dogIcon),

        // Content Description is not needed here - image is decorative, and setting a null content
        // description allows accessibility services to skip this element during navigation.

        contentDescription = null
    )
}

Lihat pratinjau aplikasi.

Pratinjau aplikasi dengan foto anjing lingkaran menggunakan contentScale dan Crop.

Menambahkan bentuk ke item daftar

1. Buka file Shape.kt. Card adalah komponen media, sehingga Anda menambahkan parameter media objek Shapes. Untuk aplikasi ini, pojok kanan atas dan kiri bawah item daftar, tetapi tidak membentuk lingkaran penuh. Untuk mencapainya, teruskan 16.dp ke atribut medium.

medium = RoundedCornerShape(bottomStart = 16.dp, topEnd = 16.dp)

Pratinjau aplikasi dengan perubahan bentuk daftar item.

Menambahkan Tipografi

Membuat Direktori Resource Android font.

1. Di tampilan project Android Studio, klik kanan folder res.
2. Pilih New > Android Resource Directory.



3. Beri nama Direktori font, setel jenis Resource sebagai font, dan klik OK.



4. Buka direktori resource font baru yang terletak di res > font.

Mendownload font kustom

2. Telusuri Montserrat, lalu klik Download family.
3. Buka file zip.
4. Buka folder Montserrat yang didownload. Di folder static, temukan Montserrat-Bold.ttf dan Montserrat-Regular.ttf (ttf adalah singkatan dari TrueType Font dan merupakan format untuk file font). Pilih kedua font dan tarik ke direktori resource font dalam project Anda di Android Studio.
5. Di folder font, ganti nama Montserrat-Bold.ttf menjadi montserrat_bold.ttf dan ganti nama Montserrat-Regular.ttf menjadi montserrat_regular.ttf.
6. Telusuri Abril Fatface dan klik Download family.
7. Buka folder Abril_Fatface yang telah didownload. Pilih AbrilFatface-Regular.ttf, lalu tarik ke direktori resource font.
8. Di folder font, ganti nama Abril_Fatface_Regular.ttf menjadi abril_fatface_regular.ttf.




Melakukan Inisialisasi Font

1. Di jendela project, buka ui.theme > Type.kt. Lakukan inisialisasi font yang didownload di bawah pernyataan impor dan di atas Typography val.

import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight

val AbrilFatFace = FontFamily(
    Font(R.font.abril_fatface_regular)
)

val Montserrat = FontFamily(
    Font(R.font.montserrat_regular),
    Font(R.font.montserrat_bold, FontWeight.Bold)
)

2. Untuk atribut displayLarge, tetapkan sama dengan TextStyle, lalu isi fontFamily, fontWeight, dan fontSize dengan informasi dari tabel berikut. Artinya, semua teks yang ditetapkan ke displayLarge akan memiliki Abril Fatface sebagai font, dengan ketebalan font normal, dan fontSize 36.sp. Ulangi proses ini untuk displayMedium, labelSmall, dan bodyLarge.

Tabel font


import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.sp

val Typography = Typography(
   displayLarge = TextStyle(
       fontFamily = AbrilFatface,
       fontWeight = FontWeight.Normal,
       fontSize = 36.sp
   ),
   displayMedium = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 20.sp
   ),
   labelSmall = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Bold,
       fontSize = 14.sp
   ),
   bodyLarge = TextStyle(
       fontFamily = Montserrat,
       fontWeight = FontWeight.Normal,
       fontSize = 14.sp
   )
)

Menambahkan tipografi ke teks aplikasi

Tambahkan displayMedium sebagai gaya untuk dogName karena merupakan informasi singkat yang penting. Tambahkan bodyLarge sebagai gaya untuk dogAge karena berfungsi cukup baik dengan ukuran teks yang lebih kecil.

@Composable
fun DogInformation(
   @StringRes dogName: Int,
   dogAge: Int,
   modifier: Modifier = Modifier
) {
   Column(modifier = modifier) {
       Text(
           text = stringResource(dogName),
           style = MaterialTheme.typography.displayMedium,
           modifier = Modifier.padding(top = dimensionResource(id = R.dimen.padding_small))
       )
       Text(
           text = stringResource(R.string.years_old, dogAge),
           style = MaterialTheme.typography.bodyLarge
       )
   }
}


Menambahkan Panel Atas

1. Di MainActivity.kt, buat composable bernama WoofTopAppBar().

@Composable
fun WoofTopAppBar(modifier: Modifier = Modifier) {
   CenterAlignedTopAppBar(
       title = {
           Row(
               verticalAlignment = Alignment.CenterVertically
           ) {
               Image(
                   modifier = Modifier
                       .size(dimensionResource(id = R.dimen.image_size))
                       .padding(dimensionResource(id = R.dimen.padding_small)),
                   painter = painterResource(R.drawable.ic_woof_logo),

                   contentDescription = null
               )
               Text(
                   text = stringResource(R.string.app_name),
                   style = MaterialTheme.typography.displayLarge
               )
           }
       },
       modifier = modifier
   )
}

Jalankan Aplikasi


Aplikasi Woof dengan Tema terang.


Aplikasi Woof dengan Tema gelap.

Source Code

https://github.com/rycahayash/WoofApp

Referensi


Komentar

Postingan populer dari blog ini

Tugas 4: Interactive Button "Dice

Tugas Pertemuan 8 - Aplikasi Dessert Clicker