Week 1: Android Basics¶

Kotlin Basics¶

  • Kotlin is a modern programming language that allows you to be more concise and write fewer lines of code for the same functionality compared to other programming languages. Apps that are built with Kotlin are also less likely to crash, resulting in a more stable and robust app for users.

  • To practice the basics of the Kotlin language, you’ll use an interactive cloud-based code editor called the Kotlin Playground. This is for learning Kotlin, but not for building Android apps. Later, you’ll use a tool called Android Studio for building Android apps.

  • Open the Kotlin Playground. Add the code below, then click ▶️ to run the program.

    fun main() {
        println("is delulu the solulu? may your yololu come trululu at smululu")
    }
    
  • Code in the Kotlin programming language is meant to be understood by humans. However, your computer doesn’t understand this language. You need something called the Kotlin compiler, which takes the Kotlin code you wrote, looks at it line by line, and translates it into something that the computer can understand. This process is called compiling your code. It is also called building your code. The terms compile and build are synonymous. They refer to the process of translating your human-readable source code into a computer-readable form.

  • If your code compiles successfully, your program will run (or execute). When the computer executes your program, it performs each of your instructions. Executing instructions are like following steps in a recipe.

  • A Kotlin program needs a main() function, which is the specific place in your code where the program starts running. The main() function is the entry point, or starting point, of the program.

Style guide¶

  • The Kotlin style guide decribes Google’s Android coding style standards. The purpose of following the style guide is to make your code easier to read and more consistent with how other Android developers write their code. This consistency is important when collaborating on large projects together, so that the style of code is the same throughout all the files in the project.

  • Some relevant style guide recommendations:

    • Function names should be in camel case and should be verbs or verb phrases.

    • Each statement should be on its own line.

    • Opening curly brace should appear at the end of the line where the function begins.

    • There should be a space before the opening curly brace.

      ../_images/unit1-pathway1-activity3-section7-83759a94e8b6622e_14401.png
    • The function body should be indented in by 4 spaces. Do not use tabs to indent your code. Unlike Python, there won’t be any errors if the indentation is missing.

      ../_images/unit1-pathway1-activity3-section7-3f24b3fe1eda0302_14401.png
    • The closing curly brace is on its own line after the last line of code in the function body. The closing brace should line up with the fun keyword at the beginning of the function.

      ../_images/unit1-pathway1-activity3-section7-22350528401a32f1_14401.png

Comments¶

  • Single-line comments:

    height = 1 // Assume the height is 1 to start with
    
  • Longer comments:

    /*
     * This is a very long comment that can
     * take up multiple lines.
     */
    
  • Example:

    /*
     * This program displays the number of messages
     * in the user's inbox.
     */
    fun main() {
        // Create a variable for the number of unread messages.
        var count = 10
        println("You have $count unread messages.")
    
        // Decrease the number of messages by 1.
        count--
        println("You have $count unread messages.")
    }
    

Variables¶

  • In Kotlin, it’s necessary to specify what type of data can be stored in variables. This is one difference between Kotlin and other languages like Python. The table below shows some common data types:

Kotlin Data Types¶

Data type

What kind of data it can contain

Example literal values

String

Text

"Huat Ah"
"Cooking"
"Cooked"

Int

Integer number

32
1293490
-59281

Double

Decimal number

2.0
123.567
-31723.99999

Float

Decimal number (that is less precise than a Double). Has an f or F at the end of the number.

5.0f
-1630.209f
1.2940278F

Boolean

true or false. Use this data type when there are only two possible values. Note that true and false are keywords in Kotlin.

true
false
  • You must define a variable first in your code before you can use the variable. This is also known as declaring a variable.

    ../_images/unit1-pathway1-activity4-section3-dfcab30373610cbb_14401.png
  • Example:

    val count: Int = 2
    val this_mod: String = "slaps"
    
  • The value and data type of the variable must match. For instance, you can’t do val i: Int = "slay"

  • An expression is a small unit of code that evaluates to a value. An expression can be made up of variables, function calls, and more. For example, an expression can be made up of a single variable:

    count
    
  • Evaluate means determining the value of an expression. If the variable count holds the value 2, then the above expression evaluates to 2.

  • Variable names should follow the camel case convention, where the first word of the function name is all lower case, and all other words begin with a capital letter. Examples:

    • numberOfEmails

    • cityName

    • bookPublicationDate

  • String template: contains a template expression, which is a $ followed by a variable name. A template expression is evaluated, and its value gets substituted into the string. Examples:

    fun main() {
        val count: Int = 2
        println("You have $count unread messages.")
    }
    
    fun main() {
        val unreadCount = 5
        val readCount = 100
        println("You have ${unreadCount + readCount} total messages in your inbox.")
    }
    
  • Type inference: the Kotlin compiler can infer what data type a variable should be. That means you can omit the data type, if you provide an initial value for the variable.

  • In this example, the Kotlin compiler knows that you want to store 2 (a whole number integer) into the variable count, so it can infer that the count variable is of type Int.

    val count = 2
    
  • A mutable variable means its value can change. Declare mutable variables with the Kotlin keyword var, instead of val. Think of val as a fixed value and var as variable.

    • val: for immutable values

    • var: for mutable variables

  • For example, if the variable cartTotal is mutable:

    fun main() {
        var cartTotal = 0
        println("Total: $cartTotal")
    
        cartTotal = 20
        println("Total: $cartTotal")
    }
    
  • Only use var to declare a variable if you expect the value to change. Otherwise, use val. This practice makes your code safer. Using val ensures that variables won’t get changed unintentionally.

Functions¶

  • In Kotlin, function definitions begin with the keyword fun, followed by the:

    • function name, usually a verb or verb phrase

    • inputs in parentheses

    • curly braces around the function body

../_images/unit1-pathway1-activity3-section5-e8b488369268e737_14401.png
  • Function names should follow the camel case convention. Examples:

    • lookLeftLookRight()

    • displayErrorMessage()

    • takePhoto()

  • Declaring (or defining) a function uses the fun keyword and includes code within the curly braces which contains the instructions needed to execute a task.

  • Calling a function causes all the code contained in that function to execute.

  • When defining a function, you can specify the data type of the return value:

    ../_images/unit1-pathway1-activity5-section3-dfbfe5ad5495870a_14401.png
  • Example:

    fun getQuote(): String {
        return "At the end of the day, the day ends"
    }
    
  • If you don’t specify a return type, the default return type is Unit. Unit means the function doesn’t return a value. Unit is equivalent to void return types in other languages (e.g. void in Java and C, None in Python). Any function that doesn’t return a value implicitly returns Unit. Example:

    fun main() {
        birthdayGreeting()
    }
    
    fun birthdayGreeting(): Unit {
        println("Happy Birthday, Rover!")
        println("You are now 5 years old!")
    }
    
  • For functions that don’t return anything, return is optional.

  • Function parameters are declared like this:

    ../_images/unit1-pathway1-activity5-section4-8c4435bda86c8a67_14401.png
  • Example:

    fun birthdayGreeting(name: String): String {
        val nameGreeting = "Happy Birthday, $name!"
        val ageGreeting = "You are now 5 years old!"
        return "$nameGreeting\n$ageGreeting"
    }
    
  • For functions with multiple parameters:

    fun birthdayGreeting(name: String, age: Int): String {
        val nameGreeting = "Happy Birthday, $name!"
        val ageGreeting = "You are now $age years old!"
        return "$nameGreeting\n$ageGreeting"
    }
    
  • The function name with its inputs (parameters) are collectively known as the function signature. The function signature consists of everything before the return type:

    fun birthdayGreeting(name: String, age: Int)
    
  • Parameters in Kotlin are immutable. You cannot reassign the value of a parameter from within the function body.

  • A recap of function syntax:

    ../_images/unit1-pathway1-activity5-section5-3e0381d36be5415c_14401.png

Note

A parameter and an argument are two different things. When you define a function, you define the parameters that are to be passed to it when the function is called. When you call a function, you pass arguments for the parameters. Parameters are the variables accessible to the function, such as a name variable, while arguments are the actual values that you pass, such as the "Rover" string.

  • When you include the parameter name when you call a function, it’s called a named argument. Example:

    println(birthdayGreeting(name = "Rex", age = 2))
    
  • Named arguments can be reordered:

    println(birthdayGreeting(age = 2, name = "Rex"))
    
  • Function parameters can also specify default arguments. When you call a function, you can choose to omit arguments for which there is a default.

  • Example: in the birthdayGreeting() function, set the name parameter to the default value "Rover".

    fun birthdayGreeting(name: String = "Rover", age: Int): String {
        return "Happy Birthday, $name! You are now $age years old!"
    }
    
  • Since age is defined after name, to use the default argument for name, you need to use the named argument age. Without named arguments, Kotlin assumes the order of arguments is the same order in which parameters are defined. The named argument is used to ensure Kotlin is expecting an Int for the age parameter.

    println(birthdayGreeting(age = 5))
    println(birthdayGreeting("Rex", 2))
    
  • This would not work, because Kotlin will think that the 5 is the argument to the name parameter, but name is a String, not an Int.

    println(birthdayGreeting(5))
    

Practice: Kotlin Basics¶

  • Need more practice? Try these exercises.

  • String templates. This program informs users about the upcoming promotional sale on a particular item. It has a string template, which relies on the discountPercentage variable for the percent discount and the item variable for the item on sale. However, there are compilation errors in the code.

    fun main() {
        val discountPercentage: Int = 0
        val offer: String = ""
        val item = "Google Chromecast"
        discountPercentage = 20
        offer = "Sale - Up to $discountPercentage% discount on $item! Hurry up!"
    
        println(offer)
    }
    

    Can you figure out the root cause of the errors and fix them?

    Can you determine the output of this program before you run the code in Kotlin Playground?

    After you fix the errors, the program should compile without errors and print this output:

    Sale - Up to 20% discount on Google Chromecast! Hurry up!
    
  • Default parameters. Gmail has a feature that sends a notification to the user whenever a login attempt is made on a new device. In this exercise, you write a program that displays a message to users with this message template:

    There's a new sign-in request on $operatingSystem for your Google Account $emailId.
    

    You need to implement a function that accepts an operatingSystem parameter and an emailId parameter, constructs a message in the given format, and returns the message.

    For example, if the function was called with "Chrome OS" for the operatingSystem and "sample@gmail.com" for the emailId, it should return this string:

    There's a new sign-in request on Chrome OS for your Google Account sample@gmail.com.
    

    Implement the displayAlertMessage() function in this program so that it prints the output displayed. If the operating system is unknown, the function should default to Unknown OS.

    fun main() {
        val firstUserEmailId = "user_one@gmail.com"
    
        // The following line of code assumes that you named your parameter as emailId.
        // If you named it differently, feel free to update the name.
        println(displayAlertMessage(emailId = firstUserEmailId))
        println()
    
        val secondUserOperatingSystem = "Windows"
        val secondUserEmailId = "user_two@gmail.com"
    
        println(displayAlertMessage(secondUserOperatingSystem, secondUserEmailId))
        println()
    
        val thirdUserOperatingSystem = "Mac OS"
        val thirdUserEmailId = "user_three@gmail.com"
    
        println(displayAlertMessage(thirdUserOperatingSystem, thirdUserEmailId))
        println()
    }
    
    // Define your displayAlertMessage() below this line.
    

Android Studio Setup¶

Download and install Android Studio¶

  • Go to: Android Studio download archives

  • Download: Android Studio Meerkat Feature Drop | 2024.3.2 RC 4 April 28, 2025

  • Extract the archive:

    $ cd ~/Downloads
    $ tar -xzvf android-studio-*-linux.tar.gz
    
  • Navigate to the android-studio/bin directory and run the studio.sh script:

    $ cd ~/Downloads/android-studio/bin
    $ ./studio.sh
    
  • Select Do not import settings, click OK.

  • Continue with the installer.

  • The Welcome to Android Studio dialog displays and you’re ready to start creating apps!

  • Go to: Android Studio download archives

  • Download: Android Studio Meerkat Feature Drop | 2024.3.2 RC 4 April 28, 2025

  • Look for the downloaded file in your Downloads folder, and run it.

  • Drag the Android Studio icon to the Applications folder.

  • In the Applications folder, double-click the Android Studio icon to launch the Android Studio Setup Wizard.

  • Follow the Android Studio Setup Wizard and accept the default settings for all steps. During the installation, the setup wizard downloads and installs additional components and tools needed for Android app development.

  • When the installation completes, Android Studio starts automatically.

  • The Welcome to Android Studio window opens and you’re ready to start creating apps!

  • Go to: Android Studio download archives

  • Download: Android Studio Meerkat Feature Drop | 2024.3.2 RC 4 April 28, 2025

  • After the download completes, look for the downloaded installer in your Downloads folder, and run it.

  • If you see User Account Control dialogs about allowing the installer and Windows Command Processor to make changes to your computer, click Yes.

  • You may also receive a Windows Security Alert about adb.exe. Click Allow access.

  • After the installation completes, the Welcome to Android Studio window displays and you’re ready to start creating apps!

Create your first Android app¶

Create a project using the template

  • Launch Android Studio.

  • In the Welcome to Android Studio dialog, click New Project.

  • The New Project window opens with a list of templates provided by Android Studio. A project template provides the blueprint for a certain type of app. Templates create the structure of the project and the files needed for Android Studio to build your project.

  • Select the Phone and Tablet tab.

  • Select the Empty Activity template. The Empty Activity template has a single screen and displays the text "Hello Android!".

  • Click Next. The New Project dialog opens. This has some fields to configure your project.

  • Configure the project:

    • Name: “Greeting Card”. This is the project name.

    • Package name: com.example.greetingcard. This is how your files will be organized in the file structure.

    • Save location: Leave as is. It contains the location where all the files related to your project are saved. Note this location so that you can find your files later.

    • Minimum SDK: indicates the minimum version of Android that your app can run on. Select API 24: Android 7.0 (Nougat).

    ../_images/unit1-pathway2-activity3-section2-7a0f8e0cb77e120c_14401.png
  • Click Finish. This may take a while. A progress bar and message indicates whether Android Studio is still setting up your project. It may look like this:

    ../_images/unit1-pathway2-activity3-section2-68405342ab13c821_14401.png
  • A message that looks similar to this informs you when the project set up is created.

    ../_images/unit1-pathway2-activity3-section2-3558f61a535db5d1_14401.png
  • You may see a What’s New pane which contains updates on new features in Android Studio. Close it for now.

  • Click Split on the top right. This allows you to view both code and design. You can also click Code to view code only or click Design to view design only.

    ../_images/unit1-pathway2-activity3-section2-b17a701425679ff1_14401.png
  • After clicking Split you should see three areas:

    ../_images/unit1-pathway2-activity3-section2-d90019bf5a60c11b_14401.png
    1. Project view: shows the files and folders of your project

    2. Code view: for editing code

    3. Design view: preview of your app

  • In the Design view, you see a blank pane with the text Build & Refresh. Click Build & Refresh. It may take a while to build. When it is done, the preview shows a text box that says “Hello Android!”.

  • To rebuild the app later: Design view âžś âś“ Up-to-date âžś Build & Refresh

Find project files

  • The Project tab shows the files and folders of your project. When you were setting up your project the package name was com.example.greetingcard. You can see that package in the Project tab. A package is basically a folder where code is located. Android Studio organizes the project in a directory structure made up of a set of packages.

  • If necessary, select Android from the drop-down menu in the Project tab.

    ../_images/unit1-pathway2-activity3-section3-2051aa2a3038b89_14401.png
  • This is the standard view of files that you use. It’s useful because you can easily access the files you will be working on in your app. However, if you look at the files in a file browser, the file hierarchy is organized very differently.

  • Select Project Source Files from the drop-down menu. You can now browse the files in the same way as in a file browser.

    ../_images/unit1-pathway2-activity3-section3-84dc993206449d28_14401.png
  • Select Android again to switch back to the Android view. If your file structure ever looks strange, check to make sure you’re still in Android view.

Modify the app

  • Look at the Code view of the MainActivity.kt file.

    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            enableEdgeToEdge()
            setContent {
                GreetingCardTheme {
                    Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
                        Greeting(
                            name = "Android",
                            modifier = Modifier.padding(innerPadding)
                        )
                    }
                }
            }
        }
    }
    
  • The onCreate() function is the entry point to this Android app and calls other functions to build the user interface.

  • The setContent, GreedingCardTheme, and Scaffold look like function definitions, but they are not. They are function calls. More on the strange notation later.

  • The setContent() function is called to define the app layout. All functions marked with the @Composable annotation can be called from setContent() or from other Composable functions. The annotation tells the Kotlin compiler that this function is used by Jetpack Compose to generate the UI.

  • Next, look at the Greeting() function. The Greeting() function is a composable function. Notice the @Composable annotation above it. This composable function takes some input and generates what’s shown on the screen.

    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Text(
            text = "Hello $name!",
            modifier = modifier
        )
    }
    
  • Change the Greeting() function to:

    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Text(
            text = "Hi, my name is $name!",
            modifier = modifier
        )
    }
    
  • Android Studio should automatically update the preview.

  • The GreetingPreview() function lets you see what your composable looks like without having to build your entire app. To enable a preview of a composable, annotate with @Composable and @Preview. The @Preview annotation tells Android Studio that this composable should be shown in the design view.

  • The @Preview annotation takes in a parameter called showBackground. If showBackground is set to true, it will add a background to your composable preview. Since Android Studio by default uses a light theme for the editor, it can be hard to see the difference between showBackground = true and showBackground = false.

  • Update GreetingPreview() with your name. The preview should update automatically. If it doesn’t, rebuild the app manually: Design view âžś âś“ Up-to-date âžś Build & Refresh

    @Preview(showBackground = true)
    @Composable
    fun GreetingPreview() {
        GreetingCardTheme {
            Greeting("Yue Niang")
        }
    }
    
  • To set a different background color for your introduction, you’ll need to surround your text with a Surface. A Surface is a container that represents a section of UI where you can alter the appearance, such as the background color or border. Do this:

    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Surface(color = Color.Magenta) {
            Text(
                text = "Hi, my name is $name!",
                modifier = modifier
            )
        }
    }
    
  • At this point, the code for Surface and Color may be red, so we need to import these references. To solve this, scroll to the top of the file where it says import and press the three dots.

    ../_images/unit1-pathway2-activity3-section5-80b6219d24f04365_14401.png
  • Add these statements to the bottom of the list of imports.

    import androidx.compose.ui.graphics.Color
    import androidx.compose.material3.Surface
    

    The full list of imports will look similar to this.

    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.activity.enableEdgeToEdge
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.padding
    import androidx.compose.material3.Scaffold
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.tooling.preview.Preview
    import com.example.greetingcard.ui.theme.GreetingCardTheme
    import androidx.compose.ui.graphics.Color
    import androidx.compose.material3.Surface
    
  • To keep the imports listed alphabetically, and remove unused imports: click Help on the top toolbar, select Find Action…, type optimize imports, and click on Optimize Imports.

    ../_images/unit1-pathway2-activity3-section5-92241239038c774a_14401.png
  • Next, you will add some space (padding) around the text.

  • A Modifier is used to augment or decorate a composable. One modifier you can use is the padding modifier, which adds space around an element. This is accomplished by using the Modifier.padding() function.

  • Every composable should have an optional parameter of the type Modifier. This should be the first optional parameter.

  • Add a padding to the modifier with a size of 24.dp. More about density-independent pixels (dp) later.

    @Composable
    fun Greeting(name: String, modifier: Modifier = Modifier) {
        Surface(color = Color.Magenta) {
            Text(
                text = "Hi, my name is $name!",
                modifier = modifier.padding(24.dp)
            )
        }
    }
    

    imports

    import androidx.compose.ui.unit.dp
    import androidx.compose.foundation.layout.padding
    
  • The preview should update to show the new padding.

Run your first app on the Android Emulator¶

  • In this task, you’ll use the Device Manager to create an Android Virtual Device (AVD). An AVD is a software version of a mobile device that runs on your computer and mimics the configuration of a particular type of Android device. This could be any phone, tablet, TV, watch, or Android Auto device. You’ll use an AVD to run the Greeting Card app.

  • The Android Emulator is an independent app used to set up virtual devices. Depending on the context, sometimes emulator refers to the Android Emulator, sometimes it refers to an Android Virtual Device.

  • The Android Emulator has its own system requirements. Virtual devices can use a lot of disk space. If you run into any issues, see Run apps on the Android Emulator.

  • In Android Studio, select Tools âžś Device Manager.

    ../_images/unit1-pathway2-activity4-section3-cf2abfbf15ceaa38_14401.png
  • The Device Manager dialog opens. If you created a virtual device previously, it’s listed in this dialog.

    ../_images/unit1-pathway2-activity4-section3-75adb8b6801b6f7f_14401.png
  • Click + (Create Virtual Device). The Virtual Device Configuration dialog appears.

    ../_images/unit1-pathway2-activity4-section3-9a1c65cecebc1e5d_14401.png
  • Select Phone âžś Pixel 6 âžś Next. This step opens another screen where you can choose the version of Android to run on your virtual device. This lets you test your app on different versions of Android.

    ../_images/unit1-pathway2-activity4-section3-7972833c77d3354c_14401.png
  • If there’s a download link next to UpsideDownCake, click Download âžś Accept âžś Next âžś Finish. The download will take a while to complete.

    ../_images/unit1-pathway2-activity4-section3-dca196de91530326_14401.png
    ../_images/unit1-pathway2-activity4-section3-30286f0543d58317_14401.png
  • In the Recommended tab, choose UpsideDownCake as the version of Android to run on the virtual device, and then click Next.

    Important

    These Android system images use a lot of disk space, so only a few are part of your original installation. Many more versions of the Android system are available than are shown in the Recommended tab. To see them, look under the x86 Images and Other Images tabs in the Virtual Device Configuration dialog.

    ../_images/unit1-pathway2-activity4-section3-4a06ff8ced8c3009_14401.png
  • This action opens another screen, where you can choose additional configuration details for your device. Leave the fields unchanged.

    ../_images/unit1-pathway2-activity4-section3-8ce477310f6c5008_14401.png

    Note

    If you see a red warning about using a system image with Google APIs, you can disregard it for now.

  • Click Finish. This action returns to the Device Manager pane.

    ../_images/unit1-pathway2-activity4-section3-448300499c4c136e_14401.png
  • Hide the Device Manager dialog.

  • Select the virtual device that you created from the dropdown menu at the top of the Android Studio window.

    ../_images/unit1-pathway2-activity4-section3-f74add101e42afde_14401.png
  • Click ▶️

  • The virtual device turns on, just like a physical device. Expect this to take a while when it starts for the first time. The virtual device should open beside the code editor.

    ../_images/unit1-pathway2-activity4-section3-1d09cfc43d012bd_14401.png
  • When your app is ready, it opens on the virtual device.

    ../_images/unit1-pathway2-activity4-section3-e5f5b9afeb297948_14401.png

How to connect your Android device (optional)¶

Teacher Say

This is optional. Since some of us don’t have Android devices, we’ll just stick to the emulator so that people won’t feel left out.

Enable USB debugging

  • To let Android Studio communicate with your Android device, you must enable USB debugging in the Developer options settings of the device.

  • To show developer options and enable USB debugging:

  • On your Android device, tap Settings > About phone.

  • Tap Build number seven times.

  • If prompted, enter your device password or pin. You know you succeeded when you see the You are now a developer! message.

    ../_images/unit1-pathway2-activity5-section3-fc5ee039dab58109_14401.png
  • Return to Settings and then tap System > Developer options.

  • If you don’t see Developer options, tap Advanced options.

    ../_images/unit1-pathway2-activity5-section3-c4b7a41bbf7800c3_14401.png
  • Tap Developer options and then tap the USB debugging toggle to turn it on.

    ../_images/unit1-pathway2-activity5-section3-cfc535f28f645d95_14401.png

Install the Google USB Driver (Windows only)

  • If you installed Android Studio on Windows, you must install a USB device driver before you can run your app on a physical device.

    Note

    For Ubuntu Linux, follow the instructions in Run Apps on a Hardware Device.

  • In Android Studio, click Tools > SDK Manager. The Preferences > Appearance & Behavior > System Settings > Android SDK dialog opens.

  • Click the SDK Tools tab.

  • Select Google USB Driver and then click OK.

    ../_images/unit1-pathway2-activity5-section3-3a5910f5cf07382f_14401.png
  • When done, the driver files are downloaded into the android_sdk\extras\google\usb_driver directory. Now you can connect and run your app from Android Studio.

Run your app on the Android device with a cable¶

  • There are two ways to connect your device to Android Studio, through a cable or through Wi-Fi. You can choose whichever you like more.

  • Connect your Android device to your computer with a USB cable. A dialog should appear on your device, which asks you to allow USB debugging.

    ../_images/unit1-pathway2-activity5-section4-ade9c70a580ffbee_14401.png
  • Select Always allow from this computer and then tap OK.

  • In Android Studio on your computer, make sure your device is selected in the dropdown. Click ▶️.

    ../_images/unit1-pathway2-activity5-section4-d180c23e4e73e5d2_14401.png
  • Select your device and then click OK. Android Studio installs the app on your device and runs it.

    Note

    For Android Studio 3.6 and higher, the physical device is automatically selected when the device is connected with debugging turned on.

  • If your device runs an Android platform that isn’t installed in Android Studio and you see a message that asks whether you want to install the needed platform, click Install > Continue > Finish. Android Studio installs the app on your device and runs it.

Run your app on the Android device with Wi-Fi¶

  • If you don’t have a cable, you can also connect and run your app on your device with Wi-Fi.

  • Ensure that your computer and device are connected to the same wireless network.

  • Ensure that your device runs Android 11 or higher. For more information, see Check & update your Android version.

  • Ensure that your computer has the latest version of Android Studio.

  • Ensure that your computer has the latest version of the SDK Platform Tools.

  • In Android Studio, select Pair Devices Using Wi-Fi from the run configurations drop-down menu.

    ../_images/unit1-pathway2-activity5-section5-eaab2f4e864cb67a_14401.png
  • The Pair devices over Wi-Fi dialog opens.

    ../_images/unit1-pathway2-activity5-section5-4715f95a3d2be595_14401.png
  • Go to Developer options, scroll down to the Debugging section and turn on Wireless debugging.

    ../_images/unit1-pathway2-activity5-section5-1a7a69258cf2a132_14401.png
  • On the Allow wireless debugging on this network? popup, select Allow.

    ../_images/unit1-pathway2-activity5-section5-5da578d4c08b3c3b_14401.png
  • If you want to pair your device with a QR code, select Pair device with QR code and then scan the QR code on your computer. Alternatively, if you want to pair your device with a pairing code, select Pair device with pairing code and then enter the 6-digit code.

  • Click run and you can deploy your app to your device.

Note

If you want to pair a different device or forget this device on your computer, navigate to Wireless debugging on your device, tap on your workstation name under Paired devices, and select Forget.

Troubleshooting¶

  • If your computer runs Linux or Windows, and you can’t run your app on a physical Android device, see Run apps on a hardware device for additional steps.

  • If your computer runs Windows and the emulator installation doesn’t work, see Install OEM USB Drivers for the appropriate USB driver for your device.

  • If Android Studio doesn’t recognize your device: try unplugging the USB cable and plug it back in or restarting Android Studio.

  • If your computer still doesn’t find the device or declares it unauthorized: disconnect the USB cable. On the device, tap Settings > Developer options > Revoke USB debugging authorizations. Reconnect the device to your computer. When prompted, grant authorizations.

Build a basic layout¶

  • Jetpack Compose is a modern toolkit for building Android User Interfaces (UIs). With Compose, you can build your UI by defining composable functions, that take in data and describe UI elements.

What is a user interface (UI)?¶

  • The user interface (UI) of an app is what you see on the screen: text, images, buttons, and many other types of elements, and how it’s laid out on the screen. It’s how the app shows things to the user and how the user interacts with the app.

  • Almost everything you see on the screen of your app is a UI element (also known as a UI component). They can be interactive, like a clickable button or an editable input field, or they can be decorative images.

  • Each of these elements is called a UI component.

    • A clickable button:

      ../_images/unit1-pathway3-activity3-section2-9a2df39af4122803_14401.png
    • A text message inside a Card:

      ../_images/unit1-pathway3-activity3-section2-50a9b402fd9037c0_14401.png
    • A text input field:

      ../_images/unit1-pathway3-activity3-section2-17794ea52cfb5473_14401.png

Annotations¶

  • Annotations are means of attaching extra information to code. This information helps other developers, and tools like the Jetpack Compose compiler, understand the app’s code. Different code elements, including properties, and functions, can be annotated.

    ../_images/unit1-pathway3-activity3-section3-87fe1e19ff89ee9c_14401.png
  • Examples:

    @Json
    val imgSrcUrl: String
    
    @Volatile
    private var INSTANCE: AppDatabase? = null
    
  • Example: @Preview annotation without parameters.

    ../_images/unit1-pathway3-activity3-section3-c7165115f8a9e40b_14401.png
  • Example: @Preview annotation with a background.

    ../_images/unit1-pathway3-activity3-section3-e3845e0f058aede9_14401.png
  • Example: @Preview annotation with a preview name

    ../_images/unit1-pathway3-activity3-section3-28a8df85bf4e80e6_14401.png
  • Example: @Preview annotation with multiple arguments

    ../_images/unit1-pathway3-activity3-section3-895f8d3a229c287a_14401.png

Composable functions¶

  • Composable functions are the basic building block of a UI in Compose. A composable function:

    • Describes some part of your UI.

    • Doesn’t return anything.

    • Takes some input, processes it, and displays something on the screen.

  • Composable functions differ from normal functions:

    ../_images/unit1-pathway2-activity3-section4-178c1b8d480aefe2_14401.png
    1. There’s a @Composable annotation before the function.

    2. @Composable function names start with a capital letter.

    3. @Composable functions can’t return anything.

  • A composable function is annotated with the @Composable annotation. These functions let you define your app’s UI programmatically by describing how it should look, rather than focusing on the process of the UI’s construction.

  • Example of a composable function that is passed some data (name), and uses it to render a text element on the screen.

    @Composable
    fun Greeting(name: String) {
        Text(text = "Hello $name!")
    }
    
  • A composable function MUST be named using Pascal case. Pascal case refers to a naming convention in which the first letter of each word in a compound word is capitalized.

  • A Composable function:

    • MUST be a noun: DoneButton()

    • MAY be prefixed by descriptive adjectives: RoundIcon()

    • is NOT a verb or verb phrase: DrawTextField()

    • is NOT a nouned preposition: TextFieldWithLink()

    • is NOT an adjective: Bright()

    • is NOT an adverb: Outside()

  • For more, see Naming Composable functions.

  • Examples:

    // Do: This function is a descriptive PascalCased noun. It is a visual UI element
    @Composable
    fun FancyButton(text: String) {}
    
    
    // Do: This function is a descriptive PascalCased noun. It is a non-visual element (it handles a button tap, but doesn't display anything)
    @Composable
    fun BackButtonHandler() {}
    
    
    // Don't: This function is a noun but is not PascalCased!
    @Composable
    fun fancyButton(text: String) {}
    
    
    // Don't: This function is PascalCased but is not a noun!
    @Composable
    fun RenderFancyButton(text: String) {}
    
    
    // Don't: This function is neither PascalCased nor a noun!
    @Composable
    fun drawProfileImage(image: ImageAsset) {}
    

Birthday Card app¶

  • The Birthday Card app displays an image with a message:

    ../_images/unit1-pathway3-activity4-section4-fa25ed7a4b6eb4d6_14401.png
  • #TODO: To get the code, …

  • https://github.com/

  • Branch: viewmodel

  • Clone:

    $ git clone https://github.com/
    $ cd basic-android-kotlin-compose-birthday-card-app
    $ git checkout main
    
  • The code for MainActivity.kt:

    /*
     * Copyright (C) 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
     *
     *     https://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 com.example.happybirthday
    
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.Image
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.padding
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Surface
    import androidx.compose.material3.Text
    import androidx.compose.runtime.Composable
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.layout.ContentScale
    import androidx.compose.ui.res.painterResource
    import androidx.compose.ui.res.stringResource
    import androidx.compose.ui.text.style.TextAlign
    import androidx.compose.ui.tooling.preview.Preview
    import androidx.compose.ui.unit.dp
    import androidx.compose.ui.unit.sp
    import com.example.happybirthday.ui.theme.HappyBirthdayTheme
    
    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                // Apply the app's theme
                HappyBirthdayTheme {
                    // Display a Surface composable
                    Surface(
                        modifier = Modifier.fillMaxSize(), // Fill the available space
                        color = MaterialTheme.colorScheme.background // Use the theme's background color
                    ) {
                        // Display a GreetingImage composable
                        GreetingImage(
                            stringResource(R.string.happy_birthday_text), // Load the string resource for the birthday message
                            stringResource(R.string.signature_text) // Load the string resource for the signature
                        )
                    }
                }
            }
        }
    }
    
    @Composable
    fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
        // Display a Column composable
        // It will contain 2 Text composables
        Column(
            verticalArrangement = Arrangement.Center, // Center the content vertically
            modifier = modifier // Since column is the first element, pass it the modifier argument
        ) {
            // Display a Text composable
            Text(
                text = message, // The text to display
                fontSize = 100.sp, // Set the font size
                lineHeight = 116.sp, // Set the height of each line. Must be large enough so that lines don't overlap
                textAlign = TextAlign.Center, // Alignment of the text inside the Text composable
                modifier = Modifier.padding(top = 16.dp) // Add padding around the Text composable
            )
            // Display another Text composable
            Text(
                text = from,
                fontSize = 36.sp,
                modifier = Modifier
                    .padding(top = 16.dp) // Add padding around the Text composable
                    .padding(end = 16.dp) // Add padding around the Text composable
                    .align(alignment = Alignment.End) // Alignment of the Text composable inside the parent Column composable
            )
        }
    }
    
    @Composable
    fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
        // Display a box that contains an Image composable, and a GreetingText composable
        // Since the Box is the first element, pass it the modifier argument
        Box(modifier) {
            // Display an Image composable
            Image(
                painter = painterResource(id = R.drawable.androidparty), // Load the image from resources
                contentDescription = null, // A description of the image for accessibility, none needed in this case since the image doesn't contain any useful information
                contentScale = ContentScale.Crop, // Scale the image to fill the bounds of the Image composable
                alpha = 0.5F
            )
            // Display a GreetingText composable
            GreetingText(
                message = message,
                from = from,
                modifier = Modifier
                    .fillMaxSize() // Fill the available space of the Box
                    .padding(8.dp) // Add padding around the GreetingText
            )
        }
    }
    
    
    @Preview(showBackground = false)
    @Composable
    private fun BirthdayCardPreview() {
        HappyBirthdayTheme {
            GreetingImage(
                stringResource(R.string.happy_birthday_text),
                stringResource(R.string.signature_text)
            )
        }
    }
    
  • Briefly read through the comments to get an idea of how the code works. Subsequently, we’ll focus on various parts of this code to learn more about Jetpack Compose.

Preview functions¶

  • A preview function calls another composable function, and provides arguments. In the below example, BirthdayCardPreview() is a preview function. It calls the Greeting() composable, and provides an argument.

    @Preview(showBackground = true)
    @Composable
    fun BirthdayCardPreview() {
        HappyBirthdayTheme {
            Greeting("Sabrina")
        }
    }
    
  • The Design pane shows previews within the IDE, without having to install the app to an Android device or emulator.

    ../_images/unit1-pathway3-activity3-section4-2bb27291fa8c8ecc_14401.png
  • Click Split to view both code and previews.

    ../_images/unit1-pathway3-activity3-section4-86a1dd28a40dea11_14401.png
  • The code added to preview functions with the @Preview annotation is only for previewing in the Design pane in Android Studio. These changes aren’t reflected in the actual app.

The Text composable¶

  • The Text composable is used to display text in the UI. It takes a string as an argument and displays it on the screen. It takes many parameters, including fontSize and lineHeight

  • UI elements use two different units of measurement: density-independent pixels (DP), and scalable pixels (SP). By default, the SP unit is the same size as the DP unit. If the user sets a preferred text size under their phone settings, then the SP unit resizes based on the user’s preferred text size.

  • Example of setting the font size of a text element to 100 SP:

    Text(
        text = message,
        fontSize = 100.sp,
        lineHeight = 116.sp,
    )
    

    imports

    import androidx.compose.ui.unit.sp
    
  • In 100.sp, 100 is an Int, and sp is an extension property for Int. More on extension properties later.

  • The AndroidX (Android Extension) library helps accelerate app development by providing core functionality. It is accessed using the androidx package.

  • The line height refers to the vertical space between consecutive lines of text. It can be set like this:

    Text(
        text = message,
        fontSize = 100.sp,
        lineHeight = 116.sp,
    )
    

User Interface Hierarchy¶

  • The UI hierarchy is based on containment, meaning a component, called the parent, can contain one or more components, called children.

    ../_images/unit1-pathway3-activity3-section8-9270b7e10f954dcb_14401.png
  • The three basic layout elements in Compose are the Column, Row, and Box composables.

  • Example: each child element inside a Row composable is placed horizontally next to each other in a row. The blue borders are only for demonstration purposes and don’t display.

    Row {
        Text("First Column")
        Text("Second Column")
    }
    
    ../_images/unit1-pathway3-activity3-section8-7117f9998760a828_14401.png

Trailing lambda syntax

  • Notice in the previous code snippet that curly braces are used instead of parentheses in the Row composable function. This is called Trailing Lambda Syntax. It is confusing because it looks like a function definition, but it’s not. It’s a function call. Kotlin uses this special syntax when the last parameter to a function is a function:

    ../_images/unit1-pathway3-activity3-section8-6373d65802273065_14401.png
  • When you pass a function as the last parameter, you can use trailing lambda syntax. Instead of putting the function inside the parentheses, you can place it outside the parentheses in curly braces. This is a recommended and common practice in Compose, so you need to be familiar with how the code looks.

  • For example, the last parameter in the Row() composable function is the content parameter, a function that describes the child UI elements. Suppose you wanted to create a row that contains three text elements. This code would work, but it’s cumbersome:

    Row(
        content = {
            Text("Some text")
            Text("Some more text")
            Text("Last text")
        }
    )
    
  • Because the content parameter is the last one in the function signature, you can remove the content parameter and the parentheses as follows:

    Row {
        Text("Some text")
        Text("Some more text")
        Text("Last text")
    }
    

The Row composable¶

  • The Row composable arranges multiple UI elements in a row. Example:

    @Composable
    fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
        Row {
            Text(
                text = message,
                fontSize = 30.sp,
                lineHeight = 116.sp,
            )
            Text(
                text = from,
                fontSize = 36.sp
            )
        }
    }
    

    imports

    import androidx.compose.foundation.layout.Row
    
  • What it looks like:

    ../_images/unit1-pathway3-activity3-section8-665aa2f1cc85c29_14401.png

The Column composable¶

  • The Column composable arranges multiple UI elements in a column. Example:

    @Composable
    fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
        Column {
            Text(
                text = message,
                fontSize = 100.sp,
                lineHeight = 116.sp,
            )
            Text(
                text = from,
                fontSize = 36.sp
            )
        }
    }
    

    imports

    import androidx.compose.foundation.layout.Column
    
  • What it looks like:

    ../_images/unit1-pathway3-activity3-section8-d80295e73578e75d_14401.png

The Box composable¶

  • Box is one of the standard layout elements in Compose. Use Box to stack elements on top of one another. Imagine your phone lying flat on a table. Box stacks elements “upwards”, from the “bottom” of your phone screen. Like a burger.

    ../_images/unit1-pathway3-activity4-section4-4d191637aaecf374_14401.png
  • For example, in this code, the Box contains a GreetingText, which is stacked on top of an Image

    @Composable
    fun GreetingImage(message: String, from: String, modifier: Modifier = Modifier) {
        // Display a box that contains an Image composable, and a GreetingText composable
        // Since the Box is the first element, pass it the modifier argument
        Box(modifier) {
            // Display an Image composable
            Image(
                painter = painterResource(id = R.drawable.androidparty), // Load the image from resources
                contentDescription = null, // A description of the image for accessibility, none needed in this case since the image doesn't contain any useful information
                contentScale = ContentScale.Crop, // Scale the image to fill the bounds of the Image composable
                alpha = 0.5F
            )
            // Display a GreetingText composable
            GreetingText(
                message = message,
                from = from,
                modifier = Modifier
                    .fillMaxSize() // Fill the available space of the Box
                    .padding(8.dp) // Add padding around the GreetingText
            )
        }
    }
    

    imports

    import androidx.compose.foundation.layout.Box
    
  • What it looks like:

    ../_images/unit1-pathway3-activity4-section4-fa25ed7a4b6eb4d6_14401.png

Layout Modifiers¶

  • Modifiers are used to decorate or add behavior to Jetpack Compose UI elements. For example, you can add backgrounds, padding or behavior to rows, text, or buttons. To set them, a composable or a layout needs to accept a modifier as a parameter.

  • The default modifier object is simply Modifier. To create a layout modifer, start with the Modifier object, and chain on additional modifier functions:

    modifier = Modifier
        .background(color = Color.Green)
        .padding(16.dp)
        .fillMaxWidth()
    
  • Most composables have a parameter called modifier that can be used to specify layout modifiers. For example, this Text composable has a modifier parameter.

    Text(
        text = "Hello, World!",
        modifier = Modifier.background(color = Color.Green)
    )
    

Padding

  • A UI element wraps itself around its content. To prevent it from wrapping too tightly, you can specify the amount of padding on each side.

    ../_images/unit1-pathway3-activity4-section6-2e96e127f9f8c7_14401.png
  • Example:

    Modifier.padding(
        start = 16.dp,
        top = 16.dp,
        end = 16.dp,
        bottom = 16.dp
    )
    

Row and Column layout parameters

  • The Row and Column composables have a modifier parameter. In addition, they each have 2 layout-related parameters used to set their children’s positions.

    • A Row has verticalAlignment and horizontalArrangement parameters

    • A Column has horizontalAlignment and verticalArrangement parameters.

  • The alignment property is used to align the child elements at the start, center, or end of the layout.

  • The arrangement property is used to arrange the child elements when the size of the layout is larger than the sum of its children.

    • When the size of a Column is larger than the sum of its children sizes, a verticalArrangement can define the positioning of the children inside the Column.

      ../_images/unit1-pathway3-activity4-section5-df69881d07b064d01.gif
    • When the size of a Row is larger than the sum of its children sizes, a horizontalArrangement can define the positioning of the children inside the Row.

      ../_images/unit1-pathway3-activity4-section5-c1e6c40e30136af21.gif
  • In GreetingText(), the highlighted lines show layout modifiers being used. If unclear, adjust these lines to see what they actually do.

    @Composable
    fun GreetingText(message: String, from: String, modifier: Modifier = Modifier) {
        // Display a Column composable
        // It will contain 2 Text composables
        Column(
            verticalArrangement = Arrangement.Center, // Center the content vertically
            modifier = modifier // Since column is the first element, pass it the modifier argument
        ) {
            // Display a Text composable
            Text(
                text = message, // The text to display
                fontSize = 100.sp, // Set the font size
                lineHeight = 116.sp, // Set the height of each line. Must be large enough so that lines don't overlap
                textAlign = TextAlign.Center, // Alignment of the text inside the Text composable
                modifier = Modifier.padding(top = 16.dp) // Add padding around the Text composable
            )
            // Display another Text composable
            Text(
                text = from,
                fontSize = 36.sp,
                modifier = Modifier
                    .padding(top = 16.dp) // Add padding around the Text composable
                    .padding(end = 16.dp) // Add padding around the Text composable
                    .align(alignment = Alignment.End) // Alignment of the Text composable inside the parent Column composable
            )
        }
    }
    
  • As a best practice, pass the modifier argument from the parent composable to the first child. In this case, GreetingText() is the parent, its first child is Column(), that’s why the line modifier = modifier is added.

Resources in Jetpack Compose¶

  • Resources are files and static content that your code uses, such as images, user-interface strings, animation instructions, and more. You should always separate resources from your code, so that you can maintain them independently. At runtime, Android uses the appropriate resource based on the current configuration. For example, you might want to provide a different UI layout based on the screen size, or different strings based on the language setting.

Grouping resources

  • You should always place each type of resource in a specific subfolder of your project’s res/ folder. For example, here’s the file hierarchy for a simple project:

    MyProject/
        src/
            MyActivity.kt
        res/
            drawable/
                graphic.png
            mipmap/
                icon.png
            values/
                strings.xml
    
  • As you can see in this example, the res/ folder contains all the resources in subdirectories, which includes

    • a drawable/ folder for image resources

    • a mipmap/ folder for launcher icons

    • a values/ folder for string resources

Accessing resources

  • Resources can be accessed with resource IDs that are generated in your project’s R class. The R class is an automatically generated class that contains the IDs of all resources in the project. In most cases, the resource ID is the same as the filename.

    ../_images/unit1-pathway3-activity4-section3-7f95dd836a249cdc_14401.png
  • For example, the image in the previous file hierarchy can be accessed with this code:

    R.drawable.graphic
    
  • Example code that uses an Image() composable and a resource called androidparty.png:

      Image(
          painter = painterResource(id = R.drawable.androidparty), // Load the image from resources
          contentDescription = null, // A description of the image for accessibility, none needed in this case since the image doesn't contain any useful information
          contentScale = ContentScale.Crop, // Scale the image to fill the bounds of the Image composable
          alpha = 0.5F
      )
    

Add an image resource¶

  • This section shows how to add an image to the project. The image being added here is already in the project, so you don’t have to actually follow these steps. If you’d like to practice, you can still repeat these steps, and overwrite the existing image. Otherwise, just refer to them later when you actually need to add an image.

  • Download androidparty.png

  • Resource Manager is a tool window that lets you import, create, manage, and use resources in your app. To access it, either click View âžś Tool Windows âžś Resource Manager, or click the Resource Manager tab next to the Project window.

    ../_images/unit1-pathway3-activity4-section2-318ae32952de3b49_14401.png
    ../_images/unit1-pathway3-activity4-section2-2703cd334049774f_14401.png
  • Click + (Add resources to the module) âžś Import Drawables.

    ../_images/unit1-pathway3-activity4-section2-41054199d5299d08_14401.png
  • Select the androidparty.png file, and click Open. This action opens the Import drawables dialog.

    ../_images/unit1-pathway3-activity4-section2-727d06e96adc8b19_14401.png
  • An image preview appers. From the QUALIFIER TYPE drop-down list, select Density.

    ../_images/unit1-pathway3-activity4-section2-c8e37d10f3afb21d_14401.png
  • Select No Density from the VALUE list.

    ../_images/unit1-pathway3-activity4-section2-a8d0554a56c5a6e7_14401.png
  • Android devices come in different screen sizes (phones, tablets, TVs, etc), and their screens also have different pixel sizes. That is, while one device has 160 pixels per square inch, another device fits 480 pixels in the same space. If you don’t consider these variations in pixel density, the system might resize your images, which could result in blurry images, or large images that consume too much memory, or images that are sized improperly. For more information about pixel densities, see Support different pixel densities.

  • For photographs and background images, like androidparty.png, doing the above stops the resizing behavior. Such images are placed in a folder called drawable-nodpi.

  • Click Next. Android Studio shows you the folder structure in which your image will be placed. Notice the drawable-nodpi folder.

    ../_images/unit1-pathway3-activity4-section2-6fbeec4f4d4fa984_14401.png
  • Click Import. Android Studio creates a drawable-nodpi folder and places your image in it. In the Android Studio project view, the resource name is displayed as androidparty.png (nodpi). In the computer file system, Android Studio would have created a folder called drawable-nodpi.

    ../_images/unit1-pathway3-activity4-section2-5d5eac7fd129c558_14401.png
  • If the image is imported successfully, Android Studio adds the image to the list under the Drawable tab. This list includes all your images and icons for the app. You can now use this image in your app.

    ../_images/unit1-pathway3-activity4-section2-305e34085badab89_14401.png
  • Switch back to the project view: click View âžś Tool Windows âžś Project or click the Project tab on the far left.

  • Click app âžś res âžś drawable to confirm that the image is in the drawable folder.

    ../_images/unit1-pathway3-activity4-section2-9ace033108aa748a_14401.png

Add a string resource¶

  • When you write apps, they may be translated into another language at some point. A hardcoded string is one that’s written directly in the code of your app. Hardcoded strings make it more difficult to translate your app into other languages and harder to reuse strings in different places in your app. You can extract strings into a resource file to resolve these issues. Instead of hardcoding strings in your code, you put the strings into a file, name the string resources, and use the names whenever you want to use the strings. The name stays the same, even if you change the string or translate it to a different language.

  • To extract a string resource, select any string, then click the bulb on the left side of the screen, and select Extract string resource. Example:

    ../_images/unit1-pathway3-activity4-section7-bd8451ea9a2aee25_14401.png
  • The Extract Resource dialog opens. In this dialog, you can customize what your string resource is called and some details on how to store it. The Resource name field is where you enter what the string is going to be called. The Resource value field is where you enter the actual string itself.

  • String resources should have lowercase names and multiple words should be separated with an underscore. Example:

    ../_images/unit1-pathway3-activity4-section7-c110d39102e88e4_14401.png
  • After extracting a string, the hardcoded string is now replaced with a call to the getString() function:

    GreetingImage(
        message = getString(R.string.happy_birthday_text),
        from = "From Emma",
        modifier = Modifier.padding(8.dp)
    )
    
  • String resources are stored in app âžś res âžś values âžś strings.xml. The strings.xml file has a list of strings that the user sees in your app. The name of your app is also a string resource. By putting the strings all in one place, you can more easily translate all the text in your app and more easily reuse a string in different parts of your app.

  • An example of strings.xml:

    <resources>
        <string name="app_name">Happy Birthday</string>
        <string name="happy_birthday_text">Happy Birthday Sam!</string>
        <string name="signature_text">From Emma</string>
    </resources>
    
  • An example of how they are used:

    @Preview(showBackground = false)
    @Composable
    private fun BirthdayCardPreview() {
        HappyBirthdayTheme {
            GreetingImage(
                stringResource(R.string.happy_birthday_text),
                stringResource(R.string.signature_text)
            )
        }
    }
    

    imports

    import androidx.compose.ui.res.stringResource

The Image composable¶

  • Use an Image composable to display an image. Example:

    Image(
        painter = painterResource(id = R.drawable.androidparty), // Load the image from resources
        contentDescription = null, // A description of the image for accessibility, none needed in this case since the image doesn't contain any useful information
        contentScale = ContentScale.Crop, // Scale the image to fill the bounds of the Image composable
        alpha = 0.5F
    )
    
  • To let all users, including those with disabilities, have an awesome experience, follow the Android accessibility guidelines. For example, a content description defines the purpose of a UI element, which makes your app more usable with tools such as TalkBack.

  • Since the image in this app is only included for decorative purposes, the addition of a content description for the image would make it harder to use with TalkBack in this particular case. That’s why the image’s contentDescription argument is set to null.

  • ContentScale.Crop scales the image uniformly to maintain the aspect ratio, so that the width and height of the image are equal to, or larger than, the corresponding dimension of the screen.

  • The alpha parameter affects the image’s opacity. Higher values mean the image is more opaque, while lower values make it more transparent.

Week 1 in-lesson exercises: Compose Basics¶

  • To be done during the lesson.

  • It’s ok if you can’t complete everything during the lesson. Continue working on them after the lesson.

  • These exercises focus on how to build apps with the UI composables that you learned. The exercises are inspired by real-world use cases, some of which you probably encountered before.

  • These exercises provide you with resources that you need for implementation, such as images and strings. The string resources contain the text that’s displayed in the UI. You add these strings to the strings.xml file and use them in your code.

  • Additionally, the exercises provide you with a set of specifications, such as font size, to use for the text content or padding around the UI components. These specifications help you build consistent UIs and often guide developers to visualize and build the screens. You might encounter similar specifications when you work with a team for an organization.

  • Some exercises might require you to use a Modifier. In such cases, see the References section available for each problem, where you can find links to documentation related to the modifiers or properties. You can read the documentation and determine how to incorporate the concepts in the app. The ability to comprehend documentation is one of the important skills that you should develop to grow your knowledge.

  • The solution code is available at the end, but try to solve the exercises before you check the answers. Consider the solutions as one way to implement the app. The solution code uses the basic composables and concepts that you learned so far. There’s a lot of room for improvement, so feel free to experiment and try different things.

  • Lastly, you should use Android Studio to create separate projects for these exercises.

Compose Article¶

  • The Learn Together app displays a list of articles about several Jetpack libraries. Users can choose the topic of their choice and learn about its latest developments.

  • In this exercise, you build a screen for the app, which displays a tutorial for Jetpack Compose. You use the image and string resources provided in the Resources section for this problem.

Compose Article: final screenshot¶

  • After you finish the implementation, your design should match this screenshot:

    ../_images/unit1-pathway3-activity5-section2-c8c16974d0aef074_14401.png

Compose Article: UI specification¶

  • Follow this UI specification:

    ../_images/unit1-pathway3-activity5-section2-905139e48ed11bee_14401.png
  1. Set the image to fill the entire screen’s width.

  2. Set the first Text composable to a 24sp font size and 16dp padding (start, end, bottom, and top).

  3. Set the second Text composable to a default font size, 16dp padding(start and end), and Justify text align.

  4. Set the third Text composable to a default font size, 16dp padding (start, end, bottom, and top), and Justify text align.

Compose Article: resources¶

  • Import bg_compose_background.png into your project

  • Use these strings:

    • Jetpack Compose tutorial

    • Jetpack Compose is a modern toolkit for building native Android UI. Compose simplifies and accelerates UI development on Android with less code, powerful tools, and intuitive Kotlin APIs.

    • In this tutorial, you build a simple UI component with declarative functions. You call Compose functions to say what elements you want and the Compose compiler does the rest. Compose is built around Composable functions. These functions let you define your app\'s UI programmatically because they let you describe how it should look and provide data dependencies, rather than focus on the process of the UI\'s construction, such as initializing an element and then attaching it to a parent. To create a Composable function, you add the @Composable annotation to the function name.

  • Might be useful:

Compose Article: solution code¶

Task manager¶

  • The Task Manager app lets users manage their day-to-day tasks and check the tasks that they need to complete.

  • In this exercise, you build a screen that users see when they complete all the tasks for a given day.

Task manager: final screenshot¶

  • After you finish the implementation, your design should match this screenshot:

    ../_images/unit1-pathway3-activity5-section3-b5a2de2b0064e729_14401.png

Task manager: UI specification¶

  • Follow these UI specifications:

    ../_images/unit1-pathway3-activity5-section3-7c2bfe139b3ffaa9_14401.png
  1. Center align all of the content vertically and horizontally on the screen.

  2. Set the first Text composable to a Bold font weight, 24dp padding top, and 8dp padding bottom.

  3. Set the second Text composable to a 16sp font size.

Task manager: resources¶

Task manager: solution code¶

Compose Quadrant¶

  • In this exercise, you need to apply most of the concepts that you learned so far and then go a step further to explore new Modifier and properties. You can check the References section, where you can find the links to these Modifier classes and properties, and use them for implementation.

  • You need to build an app that displays the information about the Composable functions that you learned.

  • The screen is divided into four quadrants. Each quadrant provides the name of a Composable function and describes it in one sentence.

Compose Quadrant: final screenshot¶

  • After you finish the implementation, your design should match this screenshot:

    ../_images/unit1-pathway3-activity5-section4-c0c70117bbd3b5b5_14401.png

Compose Quadrant: UI specifications¶

  • Divide the entire screen into four equal parts, each of which contains a Compose card and displays information about a Composable function.

    ../_images/unit1-pathway3-activity5-section4-5b11c91ad6a356eb_14401.png
  • Follow these specifications for each quadrant:

    ../_images/unit1-pathway3-activity5-section4-e6befaa575985819_14401.png
  1. Set the entire quadrant (start, end, top, and bottom) to a 16dp padding.

  2. Center align all of the content vertically and horizontally in each quadrant.

  3. Format the first Text composable in bold and set it to a 16dp padding bottom.

  4. Set the second Text composable to a Default font size.

Compose Quadrant: resources¶

  • Colors:

    • Color(0xFFEADDFF)

    • Color(0xFFD0BCFF)

    • Color(0xFFB69DF8)

    • Color(0xFFF6EDFF)

  • Strings:

    • Text composable

    • Displays text and follows the recommended Material Design guidelines.

    • Image composable

    • Creates a composable that lays out and draws a given Painter class object.

    • Row composable

    • A layout composable that places its children in a horizontal sequence.

    • Column composable

    • A layout composable that places its children in a vertical sequence.

  • Other:

Compose Quadrant: solution code¶

Week 1 Homework: Business Card app¶

  • To be done after the lesson.

  • You apply what you learned in this unit to create your own business card app. There are much less step-by-step instructions to follow here. You’re only provided with guidelines and suggestions about what you can build. You’re encouraged to use your creativity to build the app.

  • A sample of how your app might look like:

    ../_images/unit1-pathway3-activity6-section1-c941a07bca72427f_14401.png

Build UI with composables¶

Create a low-fidelity prototype¶

  • When you begin a project, it’s useful to visualize how UI elements need to fit together on the screen. In professional development work, oftentimes there are designers or design teams that provide developers with UI mockups, or designs, that contain exact specifications. However, if you don’t work with a designer, you can create a low-fidelity, or low-fi, prototype on your own. Low-fi prototype refers to a simple model, or drawing, that provides a basic idea of what the app looks like.

  • Surprisingly, it’s common to work without a designer, which makes the ability to sketch simple UI mockups a handy skill for a developer. Don’t worry, you don’t need to be a professional designer or even know how to use design tools. You can simply use a pen and paper, Slides, or Drawings to help you build it. Figma is an option too.

  • To create a low-fidelity prototype:

    • On your preferred medium, add elements that make up your app. Some elements to consider include the Android logo, your name, title, and contact information, icons that indicate contact information. For example, a telephone icon indicates a phone number.

    • Add these elements in different positions and then evaluate them visually. Don’t worry about getting it perfect the first time. You can always settle on one design now and iteratively improve it later.

    Note

    There are principles that help make design better for users, which is outside the scope of this project. To learn more, see Understanding layout.

  • You may come up with a low-fi design that looks like this image:

../_images/unit1-pathway3-activity6-section2-33433fd75a21776_14401.png

Convert design into code¶

  • To use your prototype to help translate your design into code, identify different logical sections of the apps and draw boundaries around them. This step helps you divide your screen into small composables and think about the hierarchy of the composables.

  • In this example, you can divide the screen into two sections:

    • Logo, name, and title

    • Contact information

  • Each section can be converted into one composable. This way you are able to build your UI with small composable building blocks. You can arrange each of these sections with layout composables, such as a Row or Column composable.

    ../_images/unit1-pathway3-activity6-section2-86ba449b7f9a5866_14401.png
  • For each section of the app that contains multiple UI elements, draw boundaries around them. These boundaries help you see how one element relates to another in the section.

    ../_images/unit1-pathway3-activity6-section2-699b66506190e912_14401.png
  • Now it’s easier to see how you can arrange Text, Image, Icon, and other composables with layout composables.

  • Some notes on various composables that you may use:

  • Row or Column composables

    • Experiment with various horizontalArrangement and verticalAlignment parameters in Row and Column composables to match the design that you have.

  • Image composables

    • Don’t forget to fill in the contentDescription parameter. As mentioned in the previous codelab, TalkBack uses the contentDescription parameter to help with the accessibility of the app. If the Image composable is only used for decorative purposes or there’s a Text element that describes the Image composable, you can set the contentDescription parameter to null. You can also customize the size of the image by specifying the height and width modifiers in the modifier parameter.

  • Icon composables

  • Text composables

    • You can experiment with various values of fontSize, textAlign, color, and fontWeight parameters to style your text.

  • Spacing and alignment

    • You can use Modifier arguments, such as padding and weight modifiers, to help with the arrangement of composables.

    • You can also use a Spacer composable to make spacing more explicit.

  • Color customization

    • You can use custom color with the Color class and the color hex code (a hexadecimal way to represent a color in RGB format). For example, the green color of Android has a hex code of #3DDC84. You can make your text the same green color with this code:

    Text("Example", color = Color(0xFF3ddc84))
    
  • Run the app in an emulator or on your Android device.