Movatterモバイル変換


[0]ホーム

URL:


Packt
Search iconClose icon
Search icon CANCEL
Subscription
0
Cart icon
Your Cart(0 item)
Close icon
You have no products in your basket yet
Save more on your purchases!discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Profile icon
Account
Close icon

Change country

Modal Close icon
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timerSALE ENDS IN
0Days
:
00Hours
:
00Minutes
:
00Seconds
Home> Mobile> Android Development> How to Build Android Apps with Kotlin
How to Build Android Apps with Kotlin
How to Build Android Apps with Kotlin

How to Build Android Apps with Kotlin: A hands-on guide to developing, testing, and publishing your first apps with Android

Arrow left icon
Profile Icon Alex ForresterProfile Icon Eran BoudjnahProfile Icon Alexandru DumbravanProfile Icon Jomar TigcalProfile Icon Gaurav ChandorkarProfile Icon Kaushal Dhruw +2 more Show less
Arrow right icon
$69.99
Full star iconFull star iconFull star iconFull star iconHalf star icon4.6(32 Ratings)
PaperbackFeb 2021794 pages1st Edition
eBook
$9.99 $55.99
Paperback
$69.99
Subscription
Free Trial
Renews at $19.99p/m
Arrow left icon
Profile Icon Alex ForresterProfile Icon Eran BoudjnahProfile Icon Alexandru DumbravanProfile Icon Jomar TigcalProfile Icon Gaurav ChandorkarProfile Icon Kaushal Dhruw +2 more Show less
Arrow right icon
$69.99
Full star iconFull star iconFull star iconFull star iconHalf star icon4.6(32 Ratings)
PaperbackFeb 2021794 pages1st Edition
eBook
$9.99 $55.99
Paperback
$69.99
Subscription
Free Trial
Renews at $19.99p/m
eBook
$9.99 $55.99
Paperback
$69.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature iconInstant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
Product feature iconAI Assistant (beta) to help accelerate your learning

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Shipping Address

Billing Address

Shipping Methods
Table of content iconView table of contentsPreview book icon Preview Book

How to Build Android Apps with Kotlin

2. Building User Screen Flows

Overview

This chapter covers the Android activity lifecycle and explains how the Android system interacts with your app. By the end of this chapter, you'll have learned how to build user journeys through different screens. You'll also be able to use activity tasks and launch modes, save and restore the state of your activity, use logs to report on your application, and share data between screens.

Introduction

The previous chapter introduced you to the core elements of Android development, from configuring your app using theAndroidManifest.xml file, working with simple activities, and the Android resource structure, to building an app withbuild.gradle and running an app on a virtual device. In this chapter, you'll go further and learn how the Android system interacts with your app through the Android lifecycle, how you are notified of changes to your app's state, and how you can use the Android lifecycle to respond to these changes. You'll then progress to learning how to create user journeys through your app and how to share data between screens. You'll be introduced to different techniques to achieve these goals so that you'll be able to use them in your own apps and recognize them when you see them used in other apps.

The Activity Lifecycle

In the previous chapter, we used theonCreate(saveInstanceState: Bundle?) method to display a layout in the UI of our screen. Now, we'll explore in more detail how the Android system interacts with your application to make this happen. As soon as an Activity is launched, it goes through a series of steps to take it through initialization and preparing it to be displayed to being partially displayed, and then fully displayed. There are also steps that correspond with your application being hidden, backgrounded, and then destroyed. This process is called theActivity lifecycle. For every one of these steps, there is acallback that your Activity can use to perform actions such as creating and changing the display and saving data when your app has been put into the background and then restoring that data after your app comes back into the foreground. You can consider these callbacks as hooks into how the system interacts with your activity/screen.

Every Activity has a parent Activity class that it extends. These callbacks are made on your Activity's parent, and it's up to you to decide whether you need to implement them in your own Activity to take any corresponding action. Every one of these callback functions has theoverride keyword. Theoverride keyword in Kotlin means that either this function is providing an implementation of an interface or an abstract method, or, in the case of your Activity here, which is a subclass, it is providing the implementation that will override its parent.

Now that you know how theActivity lifecycle works in general, let's go into more detail about the principal callbacks you will work with in order, from creating an Activity to the Activity being destroyed:

  • override fun onCreate(savedInstanceState: Bundle?): This is the callback that you will use the most for activities that draw a full-sized screen. It's here where you prepare your Activity layout to be displayed. At this stage, after the method completes, it is still not displayed to the user, although it will appear that way if you don't implement any other callbacks. You usually set up the UI of your Activity here by calling thesetContentView methodsetContentView(R.layout.activity_main) and carry out any initialization that is required. This method is only called once in itslifecycle unless the Activity is created again. This happens by default for some actions (such as rotating the phone from portrait to landscape orientation, for example). ThesavedInstanceState parameter of theBundle? type (? means the type can be null) in its simplest form is a map of key-value pairs optimized to save and restore data. It will be null if this is the first time that the Activity has been run after the app has started or if the Activity is being created for the first time or recreated without any states being saved. It may contain a saved state if it has been saved in theonSaveInstanceState(outState: Bundle?) callback prior to the Activity being recreated.
  • override fun onRestart(): When the Activity restarts, this is called immediately beforeonStart(). It is important to be clear about the difference between restarting an Activity and recreating an activity. When the Activity is backgrounded by pressing the home button—for instance, when it comes back into the foreground again—onRestart() will be called. Recreating an Activity is what happens when a configuration change happens, such as the device being rotated. The Activity is finished and then created again.
  • override fun onStart(): This is the callback made when the Activity first comes into view. Also, after the app is backgrounded by pressing either the back, home, or therecents/overview hardware buttons, on selecting the app again from therecents/overview menu or the launcher, this function will be run. It is the first of the visible lifecycle methods.
  • override fun onRestoreInstanceState(savedInstanceState: Bundle?): If the state has been saved usingonSaveInstanceState(outState: Bundle?) this is the method which the system calls afteronStart() where you can retrieve theBundle state instead of restoring the state duringonCreate(savedInstanceState: Bundle?)
  • override fun onResume(): This callback is run as the final stage of creating an Activity for the first time, and also when the app has been backgrounded and then is brought into the foreground. Upon the completion of this callback, the screen/activity is ready to be used, receive user events, and be responsive.
  • override fun onSaveInstanceState(outState: Bundle?): If you want to save the state of the activity, this function can do so. You add key-value pairs using one of the convenience functions depending on the data type. The data will then be available if your Activity is recreated inonCreate(saveInstanceState: Bundle?) andonRestoreInstanceState(savedInstanceState: Bundle?).
  • override fun onPause(): This function is called when the Activity starts to be backgrounded or another dialog or Activity comes into the foreground.
  • override fun onStop(): This function is called when the Activity is hidden, either because it is being backgrounded or another Activity is being launched on top of it.
  • override fun onDestroy(): This is called by the system to kill the Activity when system resources are low, whenfinish() is called explicitly on the Activity, or, more commonly, when the Activity is killed by the user closing the app from the recents/overview button.

Now that you understand what these common lifecycle callbacks do, let's implement them to see when they are called.

Exercise 2.01: Logging the Activity Callbacks

Let's create an application calledActivity Callbacks with an empty Activity, as you did previously inChapter 1,Creating Your First App. The aim of this exercise is to log the Activity callbacks and the order that they occur for common operations:

  1. After the application has been created,MainActivity will appear as follows:
    package com.example.activitycallbacksimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleclass MainActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)    }}

    In order to verify the order of the callbacks, let's add a log statement at the end of each callback. To prepare the Activity for logging, import the Android log package by addingimport android.util.Log to theimport statements. Then, add a constant to the class to identify your Activity. Constants in Kotlin are identified by theconst keyword and can be declared at the top level (outside the class) or in an object within the class. Top level constants are generally used if they are required to be public. For private constants, Kotlin provides a convenient way to add static functionality to classes by declaring a companion object. Add the following at the bottom of the class belowonCreate(savedInstanceState: Bundle?):

    companion object {    private const val TAG = "MainActivity"}

    Then add a log statement at the end ofonCreate(savedInstanceState: Bundle?):

    Log.d(TAG, "onCreate")

    Our Activity should now have the following code:

    package com.example.activitycallbacksimport android.os.Bundleimport android.util.Logimport androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        Log.d(TAG, "onCreate")    }    companion object {        private const val TAG = "MainActivity"    }}

    d in the preceding log statement refers todebug. There are six different log levels that can be used to output message information from the least to most important -v forverbose,d fordebug,i forinfo,w forwarn,e forerror, andwtf forwhat a terrible failure. (This last log level highlights an exception that should never occur.)

            Log.v(TAG, "verbose message")        Log.d(TAG, "debug message")        Log.i(TAG, "info message")        Log.w(TAG, "warning message")        Log.e(TAG, "error message")        Log.wtf(TAG, "what a terrible failure message")
  2. Now, let's see how the logs are displayed in Android Studio. Open theLogcat window. It can be accessed by clicking on theLogcat tab at the bottom of the screen and also from the toolbar by going toView |Tool WindowsLogcat.
  3. Run the app on the virtual device and examine theLogcat window output. You should see the log statement you have added formatted like the following line inFigure 2.1:
    Figure 2.1: Log output in Logcat

    Figure 2.1: Log output in Logcat

  4. Log statements can be quite difficult to interpret at first glance, so let's break down the following statement into its separate parts:
    2020-03-03  20:36:12.308  21415-21415/com.example.activitycallbacks D/MainActivity: onCreate

    Let's examine the elements of the log statement in detail:

    Figure 2.2: Table explaining a log statement

    Figure 2.2: Table explaining a log statement

    You can examine the output of the different log levels by changing the log filter fromDebug to other options in the drop-down menu. If you selectVerbose, as the name implies, you will see a lot of output.

  5. What's great about theTAG option of the log statement is that it enables you to filter the log statements that are reported in theLogcat window of Android Studio by typing in the text of the tag, as shown inFigure 2.3:
    Figure 2.3: Filtering log statements by the TAG name

    Figure 2.3: Filtering log statements by the TAG name

    So, if you are debugging an issue in your Activity, you can type in theTAG name and add logs to your Activity to see the sequence of log statements. This is what you are going to do next by implementing the principal Activity callbacks and adding a log statement to each one to see when they are run.

  6. Place your cursor on a new line after the closing brace of theonCreate(savedInstanceState: Bundle?) function and then add theonRestart() callback with a log statement. Make sure you call through tosuper.onRestart() so that the existing functionality of the Activity callback works as expected:
    override fun onRestart() {    super.onRestart()    Log.d(TAG, "onRestart")}
  7. You will find that once you start typing the name of the function, Android Studio's autocomplete feature will suggest options for the name of the function you want to override.

    Note

    In Android Studio you can start typing the name of a function, and autocomplete options will pop up with suggestions for functions to override. Alternatively, if you go to the top menu and thenCode |Generate |Override methods, you can select the methods to override.

    Do this for all of the following callback functions:

    onCreate(savedInstanceState: Bundle?)onRestart()onStart()onRestoreInstanceState(savedInstanceState: Bundle?)onResume()onPause()onStop()onSaveInstanceStateoutState: Bundle?)onDestroy()
  8. Your Activity should now have the following code (truncated here). You can see the full code on GitHub athttp://packt.live/38W7jU5

    The completed activity will now override the callbacks with your implementation, which adds a log message:

    package com.example.activitycallbacksimport android.os.Bundleimport android.util.Logimport androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        Log.d(TAG, "onCreate")    }    override fun onRestart() {        super.onRestart()        Log.d(TAG, "onRestart")    }    //Remaining callbacks follow: see github link above    companion object {        private const val TAG = "MainActivity"    }}
  9. Run the app, and then once it has loaded, as inFigure 2.4, look at theLogcat output; you should see the following log statements (this is a shortened version):
    D/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onResume

    The Activity has been created, started, and then prepared for the user to interact with:

    Figure 2.4: The app loaded and displaying MainActivity

    Figure 2.4: The app loaded and displaying MainActivity

  10. Press the round home button in the center of the bottom navigation controls and background the app. You should now see the following Logcat output:
    D/MainActivity: onPauseD/MainActivity: onStopD/MainActivity: onSaveInstanceState

    For apps which target below Android Pie (API 28) thenonSaveInstanceState(outState: Bundle?) may also be called beforeonPause() oronStop().

  11. Now, bring the app back into the foreground by pressing the recents/overview button (usually a square or three vertical lines) on the right and selecting the app, or by going to the launcher and opening the app. You should now see the following:
    D/MainActivity: onRestartD/MainActivity: onStartD/MainActivity: onResume

    The Activity has been restarted. You might have noticed that theonRestoreInstanceState(savedInstanceState: Bundle) function was not called. This is because the Activity was not destroyed and recreated.

  12. Press the triangle back button on the left of the bottom navigation controls (it may also be on the right) and you will see the Activity being destroyed. You can also do this by pressing the recents/overview button and then swiping the app upward to kill the activity. This is the output:
    D/MainActivity: onPauseD/MainActivity: onStopD/MainActivity: onDestroy
  13. Launch your app again and then rotate the phone. You might find that the phone does not rotate and the display is sideways. If this happens drag down the status bar at the very top of the virtual device and select the auto-rotate button 2nd from the right in the settings.
    Figure 2.5: Quick settings bar with Wi-Fi and Auto-rotate button selected

    Figure 2.5: Quick settings bar with Wi-Fi and Auto-rotate button selected

    You should see the following callbacks:

    D/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onResumeD/MainActivity: onPauseD/MainActivity: onStopD/MainActivity: onSaveInstanceStateD/MainActivity: onDestroyD/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onRestoreInstanceStateD/MainActivity: onResume

    Please note that as stated in step 11, the order of theonSaveInstanceState(outState: Bundle?) callback may vary.

  14. Configuration changes, such as rotating the phone, by default recreate the activity. You can choose not to handle certain configuration changes in the app, which will then not recreate the activity. To do this for rotation, add android:configChanges="orientation|screenSize|screenLayout" toMainActivity in theAndroidManifest.xml file. Launch the app and then rotate the phone, and these are the only callbacks that you have added toMainActivity that you will see:
    D/MainActivity: onCreateD/MainActivity: onStartD/MainActivity: onResume

    Theorientation andscreenSize values have the same function for different Android API levels for detecting screen orientation changes. ThescreenLayout value detects other layout changes which might occur on foldable phones. These are some of the config changes you can choose to handle yourself (another common one iskeyboardHidden to react to changes in accessing the keyboard). The app will still be notified by the system of these changes through the following callback:

    override fun onConfigurationChanged(newConfig: Configuration) {    super.onConfigurationChanged(newConfig)    Log.d(TAG, "onConfigurationChanged")}

    If you add this callback function toMainActivity, and you have addedandroid:configChanges="orientation|screenSize|screenLayout" toMainActivity in the manifest, you will see it called on rotation.

In this exercise, you have learned about the principal Activity callbacks and how they run when a user carries out common operations with your app through the system's interaction withMainActivity. In the next section, you will cover saving the state and restoring it, as well as see more examples of how the Activity lifecycle works.

Saving and Restoring the Activity State

In this section, you'll explore how your Activity saves and restores the state. As you've learned in the previous section, configuration changes, such as rotating the phone, cause the Activity to be recreated. This can also happen if the system has to kill your app in order to free up memory. In these scenarios, it is important to preserve the state of the Activity and then restore it. In the next two exercises, you'll work through an example ensuring that the user's data is restored whenTextView is created and populated from a user's data after filling in a form.

Exercise 2.02: Saving and Restoring the State in Layouts

In this exercise, firstly create an application calledSave and Restore with an empty activity. The app you are going to create will have a simple form that offers a discount code for a user's favorite restaurant if they enter some personal details (no actual information will be sent anywhere, so your data is safe):

  1. Open up thestrings.xml file (located inapp |src |main |res |values |strings.xml) and create the following strings that you'll need for your app:
    <resources>    <string name="app_name">Save And Restore</string>    <string name="header_text">Enter your name and email       for a discount code at Your Favorite Restaurant!        </string>    <string name="first_name_label">First Name:</string>    <string name="email_label">Email:</string>    <string name="last_name_label">Last Name:</string>    <string name="discount_code_button">GET DISCOUNT</string>    <string name="discount_code_confirmation">Your       discount code is below %s. Enjoy!</string></resources>
  2. You are also going to specify some text sizes, layout margins, and padding directly, so create thedimens.xml file in theapp |src |main |res |values folder and add the dimensions you'll need for the app (you can do this by right-clicking on theres |values folder within Android Studio and selecting Newvalues):
    <?xml version="1.0" encoding="utf-8"?><resources>    <dimen name="grid_4">4dp</dimen>    <dimen name="grid_8">8dp</dimen>    <dimen name="grid_12">12dp</dimen>    <dimen name="grid_16">16dp</dimen>    <dimen name="grid_24">24dp</dimen>    <dimen name="grid_32">32dp</dimen>    <dimen name="default_text_size">20sp</dimen>    <dimen name="discount_code_text_size">20sp</dimen></resources>

    Here, you are specifying all the dimensions you need in the exercise. You will see here thatdefault_text_size anddiscount_code_text_size are specified insp. They represent the same values as density-independent pixels, which not only define the size measurement according to the density of the device that your app is being run on but also change the text size according to the user's preference, defined inSettings |Display |Font style (this might beFont size and style or something similar, depending on the exact device you are using).

  3. InR.layout.activity_main, add the following XML, creating a containing layout file and adding header aTextView with theEnter your name and email for a discount code at Your Favorite Restaurant! text. This is done by adding theandroid:text attribute with the@string/header_text value:
    <?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:padding="@dimen/grid_4"    android:layout_marginTop="@dimen/grid_4"    tools:context=".MainActivity">    <TextView        android:id="@+id/header_text"        android:gravity="center"        android:textSize="@dimen/default_text_size"        android:paddingStart="@dimen/grid_8"        android:paddingEnd="@dimen/grid_8"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/header_text"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>

    You are usingConstraintLayout for constraining Views against the parent View and sibling Views.

    Although you should normally specify the display of the View with styles, you can do this directly in the XML, as is done for some attributes here. The value of theandroid:textSize attribute is@dimen/default_text_size, defined in the previous code block, which you use to avoid repetition, and it enables you to change all the text size in one place. Using styles is the preferred option for setting text sizes as you will get sensible defaults and you can override the value in the style or, as you are doing here, on the individual Views.

    Other attributes that affect positioning are also specified directly here in the Views. The most common ones are padding and margin. Padding is applied on the inside of Views and is the space between the text and the border. Margin is specified on the outside of Views and is the space from the outer edges of Views. For example,android:padding inConstraintLayout sets the padding for the View with the specified value on all sides. Alternatively, you can specify the padding for one of the four sides of a View withandroid:paddingTop,android:paddingBottom,android:paddingStart, andandroid:paddingEnd. This pattern also exists to specify margins, soandroid:layout_margin specifies the margin value for all four sides of a View andandroid:layoutMarginTop,android:layoutMarginBottom,android:layoutMarginStart, andandroid:layoutMarginEnd allow setting the margin for individual sides.

    For API levels less than 17 (and your app supports down to 16) you also have to addandroid:layoutMarginLeft if you useandroid:layoutMarginStart andandroid:layoutMarginRight if you useandroid:layoutMarginEnd. In order to have consistency and uniformity throughout the app, you define the margin and padding values as dimensions contained within thedimens.xml file.

    To position the content within a View, you can specifyandroid:gravity. Thecenter value constrains the content both vertically and horizontally within the View.

  4. Next, add threeEditText views below theheader_text for the user to add their first name, last name, and email:
        <EditText        android:id="@+id/first_name"        android:textSize="@dimen/default_text_size"        android:layout_marginStart="@dimen/grid_24"        android:layout_marginLeft="@dimen/grid_24"        android:layout_marginEnd="@dimen/grid_16"        android:layout_marginRight="@dimen/grid_16"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:hint="@string/first_name_label"        android:inputType="text"        app:layout_constraintTop_toBottomOf="@id/header_text"        app:layout_constraintStart_toStartOf="parent" />    <EditText        android:textSize="@dimen/default_text_size"        android:layout_marginEnd="@dimen/grid_24"        android:layout_marginRight="@dimen/grid_24"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:hint="@string/last_name_label"        android:inputType="text"        app:layout_constraintTop_toBottomOf="@id/header_text"        app:layout_constraintStart_toEndOf="@id/first_name"        app:layout_constraintEnd_toEndOf="parent" />    <!-- android:inputType="textEmailAddress" is not enforced,       but is a hint to the IME (Input Method Editor) usually a       keyboard to configure the display for an email -       typically by showing the '@' symbol -->    <EditText        android:id="@+id/email"        android:textSize="@dimen/default_text_size"        android:layout_marginStart="@dimen/grid_24"        android:layout_marginLeft="@dimen/grid_24"        android:layout_marginEnd="@dimen/grid_32"        android:layout_marginRight="@dimen/grid_32"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:hint="@string/email_label"        android:inputType="textEmailAddress"        app:layout_constraintTop_toBottomOf="@id/first_name"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent" />

    TheEditText fields have aninputType attribute to specify the type of input that can be entered into the form field. Some values, such asnumber onEditText, restrict the input that can be entered into the field, and on selecting the field, suggest how the keyboard is displayed. Others, such asandroid:inputType="textEmailAddress", will not enforce an@ symbol being added to the form field, but will give a hint to the keyboard to display it.

  5. Finally, add a button for the user to press to generate a discount code, and display the discount code itself and a confirmation message:
        <Button        android:id="@+id/discount_button"        android:textSize="@dimen/default_text_size"        android:layout_marginTop="@dimen/grid_12"        android:gravity="center"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/discount_code_button"        app:layout_constraintTop_toBottomOf="@id/email"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/>    <TextView        android:id="@+id/discount_code_confirmation"        android:gravity="center"        android:textSize="@dimen/default_text_size"        android:paddingStart="@dimen/grid_16"        android:paddingEnd="@dimen/grid_16"        android:layout_marginTop="@dimen/grid_8"        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:layout_constraintTop_toBottomOf="@id/discount_button"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"        tools:text="Hey John Smith! Here is your discount code" />    <TextView        android:id="@+id/discount_code"        android:gravity="center"        android:textSize="@dimen/discount_code_text_size"        android:textStyle="bold"        android:layout_marginTop="@dimen/grid_8"        android:layout_width="match_parent"        android:layout_height="wrap_content"        app:layout_constraintTop_toBottomOf="@id/discount_code           _confirmation"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"        tools:text="XHFG6H9O" />

    There are also some attributes that you haven't seen before. The tools namespacexmlns:tools="http://schemas.android.com/tools" which was specified at the top of the xml layout file enables certain features that can be used when creating your app to assist with configuration and design. The attributes are removed when you build your app, so they don't contribute to the overall size of the app. You are using thetools:text attribute to show the text that will typically be displayed in the form fields. This helps when you switch to theDesign view from viewing the XML in theCode view in Android Studio as you can see an approximation of how your layout displays on a device.

  6. Run the app and you should see the output displayed inFigure 2.6:
    Figure 2.6: The Activity screen on the first launch

    Figure 2.6: The Activity screen on the first launch

  7. Enter some text into each of the form fields:
    Figure 2.7: The EditText fields filled in

    Figure 2.7: The EditText fields filled in

  8. Now, use the second rotate button in the virtual device controls (1) to rotate the phone 90 degrees to the right:
    Figure 2.8: The virtual device turned to landscape orientation

    Figure 2.8: The virtual device turned to landscape orientation

    Can you spot what has happened? TheLast Name field value is no longer set. It has been lost in the process of recreating the activity. Why is this? Well, in the case of theEditText fields, the Android framework will preserve the state of the fields if they have an ID set on them.

  9. Go back to theactivity_main.xml layout file and add an ID for theLast Name value in theEditText field:
    <EditText    android:id="@+id/last_name"    android:textSize="@dimen/default_text_size"    android:layout_marginEnd="@dimen/grid_24"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:hint="@string/last_name_label"    android:inputType="text"    app:layout_constraintTop_toBottomOf="@id/header_text"    app:layout_constraintStart_toEndOf="@id/first_name"    app:layout_constraintEnd_toEndOf="parent"    tools:text="Last Name:"/>

When you run up the app again and rotate the device, it will preserve the value you have entered. You've now seen that you need to set an ID on theEditText fields to preserve the state. For theEditText fields, it's common to retain the state on a configuration change when the user is entering details into a form so that it is the default behavior if the field has an ID. Obviously, you want to get the details of theEditText field once the user has entered some text, which is why you set an ID, but setting an ID for other field types, such asTextView, does not retain the state if you update them and you need to save the state yourself. Setting IDs for Views that enable scrolling, such asRecyclerView, is also important as it enables the scroll position to be maintained when the Activity is recreated.

Now, you have defined the layout for the screen, but you have not added any logic for creating and displaying the discount code. In the next exercise, we will work through this.

The layout created in this exercise is available athttp://packt.live/35RSdgz.

You can find the code for the entire exercise athttp://packt.live/3p1AZF3.

Exercise 2.03: Saving and Restoring the State with Callbacks

The aim of this exercise is to bring all the UI elements in the layout together to generate a discount code after the user has entered their data. In order to do this, you will have to add logic to the button to retrieve all theEditText fields and then display a confirmation to the user, as well as generate a discount code:

  1. Open upMainActivity.kt and replace the default empty Activity from the project creation. A snippet of the code is shown here, but you'll need to use the link given below to find the full code block you need to add:

    MainActivity.kt

    14  class MainActivity : AppCompatActivity() {1516    private val discountButton: Button17        get() = findViewById(R.id.discount_button)1819    private val firstName: EditText20        get() = findViewById(R.id.first_name)2122    private val lastName: EditText23        get() = findViewById(R.id.last_name)2425    private val email: EditText26        get() = findViewById(R.id.email)27  28    private val discountCodeConfirmation: TextView29        get() = findViewById(R.id             .discount_code_confirmation)3031    private val discountCode: TextView32        get() = findViewById(R.id.discount_code)    33  34    override fun onCreate(savedInstanceState: Bundle?) {35        super.onCreate(savedInstanceState)36        setContentView(R.layout.activity_main)37        Log.d(TAG, "onCreate")

    You can find the complete code herehttp://packt.live/38XcdQS.

    Theget() = … is a custom accessor for a property.

    Upon clicking the discount button, you retrieve the values from thefirst_name andlast_name fields, concatenate them with a space, and then use a string resource to format the discount code confirmation text. The string you reference in thestrings.xml file is as follows:

    <string name="discount_code_confirmation">Hey  %s! Here is   your discount code</string>

    The%s value specifies a string value to be replaced when the string resource is retrieved. This is done by passing in the full name when getting the string:

    getString(R.string.discount_code_confirmation, fullName)

    The code is generated by using the UUID (Universally Unique Identifier) library from thejava.util package. This creates a unique id, and then thetake() Kotlin function is used to get the first eight characters before setting these to uppercase. Finally, discount_code is set in the view, the keyboard is hidden, and all the form fields are set back to their initial values.

  2. Run the app and enter some text into the name and email fields, and then click onGET DISCOUNT:
    Figure 2.9: Screen displayed after the user has generated a discount code

    Figure 2.9: Screen displayed after the user has generated a discount code

    The app behaves as expected, showing the confirmation.

  3. Now, rotate the phone (pressing the fifth button down with the arrow on the right-hand side of the virtual device picture) and observe the result:
    Figure 2.10: Discount code no longer displaying on the screen

    Figure 2.10: Discount code no longer displaying on the screen

    Oh, no! The discount code has gone. TheTextView fields do not retain the state, so you will have to save the state yourself.

  4. Go back intoMainActivity.kt and add the following Activity callbacks:
    override fun onRestoreInstanceState(savedInstanceState:   Bundle) {    super.onRestoreInstanceState(savedInstanceState)    Log.d(TAG, "onRestoreInstanceState")}override fun onSaveInstanceState(outState: Bundle) {    super.onSaveInstanceState(outState)    Log.d(TAG, "onSaveInstanceState")}

    These callbacks, as the names declare, enable you to save and restore the instance state.onSaveInstanceState(outState: Bundle) allows you to add key-value pairs from your Activity when it is being backgrounded or destroyed, which you can retrieve in eitheronCreate(savedInstanceState: Bundle?) oronRestoreInstanceState(savedInstanceState: Bundle).

    So, you have two callbacks to retrieve the state once it has been set. If you are doing a lot of initialization inonCreate(savedInstanceState: Bundle), it might be better to useonRestoreInstanceState(savedInstanceState: Bundle) to retrieve this instance state when your Activity is being recreated. In this way, it's clear which state is being recreated. However, you might prefer to useonCreate(savedInstanceState: Bundle) if there is minimal setup required.

    Whichever of the two callbacks you decide to use, you will have to get the state you set in theonSaveInstanceState(outState: Bundle) call. For the next step in the exercise, you will useonRestoreInstanceState(savedInstanceState: Bundle).

  5. Add two constants to theMainActivity companion object:
    private const val DISCOUNT_CONFIRMATION_MESSAGE =   "DISCOUNT_CONFIRMATION_MESSAGE"private const val DISCOUNT_CODE = "DISCOUNT_CODE"
  6. Now, add these constants as keys for the values you want to save and retrieve by making the following additions to the Activity:
        override fun onRestoreInstanceState(        savedInstanceState: Bundle) {        super.onRestoreInstanceState(savedInstanceState)        Log.d(TAG, "onRestoreInstanceState")        //Get the discount code or an empty           string if it hasn't been set        discountCode.text = savedInstanceState           .getString(DISCOUNT_CODE,"")        //Get the discount confirmation message           or an empty string if it hasn't been set        discountCodeConfirmation.text =          savedInstanceState.getString(            DISCOUNT_CONFIRMATION_MESSAGE,"")    }    override fun onSaveInstanceState(outState: Bundle) {        super.onSaveInstanceState(outState)        Log.d(TAG, "onSaveInstanceState")        outState.putString(DISCOUNT_CODE,          discountCode.text.toString())        outState.putString(DISCOUNT_CONFIRMATION_MESSAGE,          discountCodeConfirmation.text.toString())    }
  7. Run the app, enter the values into theEditText fields, and then generate a discount code. Then, rotate the device and you will see that the discount code is restored inFigure 2.11:
    Figure 2.11: Discount code continues to be displayed on the screen

Figure 2.11: Discount code continues to be displayed on the screen

In this exercise, you first saw how the state of theEditText fields is maintained on configuration changes. You also saved and restored the instance state using the Activity lifecycleonSaveInstanceState(outState: Bundle) andonCreate(savedInstanceState: Bundle?)/onRestoreInstanceState(savedInstanceState: Bundle) functions. These functions provide a way to save and restore simple data. The Android framework also providesViewModel, an Android architecture component that is lifecycle-aware. The mechanisms of how to save and restore this state (withViewModel) are managed by the framework, so you don't have to explicitly manage it as you have done in the preceding example. You will learn how to use this component inChapter 10,Android Architecture Components.

So far, you have created a single-screen app. Although it is possible for simple apps to use one Activity, it is likely that you will want to organize your app into different activities that handle different functions. So, in the next section, you will add another Activity to an app and navigate between the activities.

Activity Interaction with Intents

An intent in Android is a communication mechanism between components. Within your own app, a lot of the time, you will want another specific Activity to start when some action happens in the current activity. Specifying exactly which Activity will start is called anexplicit intent. On other occasions, you will want to get access to a system component, such as the camera. As you can't access these components directly, you will have to send an intent, which the system resolves in order to open the camera. These are calledimplicit intents. An intent filter has to be set up in order to register to respond to these events. Go to theAndroidManifest.xml file and you will see an example of two intent filters set within the<intent-filter> XML element:

<activity android:name=".MainActivity">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.          LAUNCHER" />    </intent-filter></activity>

The one specified with<action android:name="android.intent.action.MAIN" /> means that this is the main entry point into the app. Depending on which category is set, it governs which Activity first starts when the app is started. The other intent filter that is specified is<category android:name="android.intent.category.LAUNCHER" />, which defines that the app should appear in the launcher. When combined, the two intent filters define that when the app is started from the launcher,MainActivity should be started. Removing any one of these intent filters results in the"Error running 'app': Default Activity not found" message. As the app has not got a main entry point, it can't be launched, which is what also happens when you remove<action android:name="android.intent.action.MAIN". />. If you remove<category android:name="android.intent.category.LAUNCHER" /> and don't specify a category, then there is nowhere that it can be launched from.

For the next exercise, you will see how intents work to navigate around your app.

Exercise 2.04: An Introduction to Intents

The goal of this exercise is to create a simple app that uses intents to display text to the user based on their input. Create a new project in Android Studio and select an empty Activity. Once you have set up the project, go to the toolbar and selectFile |New |Activity |EmptyActivity. Call itWelcomeActivity and leave all the other defaults as they are. It will be added to theAndroidManifest.xml file, ready to use. The issue you have now that you've addedWelcomeActivity is how do you do anything with it?MainActivity starts when you launch the app, but you need a way to launchWelcomeActivity and then, optionally, pass data to it, which is when you use intents:

  1. In order to work through this example, add the following code to thestrings.xml file. These are the strings you'll be using in the app:
    <resources>    <string name="app_name">Intents Introduction</string>    <string name="header_text">Please enter your name and       then we\'ll get started!</string>    <string name="welcome_text">Hello %s, we hope you enjoy       using the app!</string>    <string name="full_name_label">Enter your full       name:</string>    <string name="submit_button_text">SUBMIT</string></resources>
  2. Next, update the styles in thethemes.xml file adding the header style.
        <style name="header" parent=      "TextAppearance.AppCompat.Title">        <item name="android:gravity">center</item>        <item name="android:layout_marginStart">24dp</item>        <item name="android:layout_marginEnd">24dp</item>        <item name="android:layout_marginLeft">24dp</item>        <item name="android:layout_marginRight">24dp</item>        <item name="android:textSize">20sp</item>    </style>    <!--  continued below -->

    Next, add thefullname,button, andpage styles:

        <style name="full_name" parent=      "TextAppearance.AppCompat.Body1">        <item name="android:layout_marginTop">16dp</item>        <item name="android:layout_gravity">center</item>        <item name="android:textSize">20sp</item>        <item name="android:inputType">text</item>    </style>    <style name="button" parent=      "TextAppearance.AppCompat.Button">        <item name="android:layout_margin">16dp</item>        <item name="android:gravity">center</item>        <item name="android:textSize">20sp</item>    </style>    <style name="page">        <item name="android:layout_margin">8dp</item>        <item name="android:padding">8dp</item>    </style>

    Normally, you wouldn't specify dimensions directly in the styles themselves. They should be referenced asdimens values so that they can be updated in one place, are more uniform, and can be labeled to represent what the dimension actually is. This is not done here for simplicity.

  3. Next, change theMainActivity layout inactivity_main.xml and add aTextView header:
    <?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    style="@style/page"    tools:context=".MainActivity">    <TextView        android:id="@+id/header_text"        style="@style/header"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/header_text"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/></androidx.constraintlayout.widget.ConstraintLayout>

    This should be the first View displayed, and as it's constrained usingConstraintLayout to the top of its parent, it displays at the top of the screen. As it's also constrained to both the start and end of its parent, it will be displayed in the middle when you run the app, as shown inFigure 2.12:

    Figure 2.12: Initial app display after adding the TextView header

    Figure 2.12: Initial app display after adding the TextView header

  4. Now, add anEditText field for the full name and aButton field for the submit button in theactivity_main.xml file below theTextView header:
        <EditText        android:id="@+id/full_name"        style="@style/full_name"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:hint="@string/full_name_label"        app:layout_constraintTop_toBottomOf="@id/header_text"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintEnd_toEndOf="parent"/>    <Button        android:id="@+id/submit_button"        style="@style/button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/submit_button_text"        app:layout_constraintTop_toBottomOf="@id/full_name"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/>

    The app, when run, looks as inFigure 2.13:

    Figure 2.13: The app display after adding the EditText full name field and submit button

    Figure 2.13: The app display after adding the EditText full name field and submit button

    You now need to configure the button so that when it's clicked, it retrieves the user's full name from theEditText field and then sends it in an intent, which startsWelcomeActivity.

  5. Update theactivity_welcome.xml layout file to prepare to do this:
    <?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    style="@style/page"    tools:context=".WelcomeActivity">    <TextView        android:id="@+id/welcome_text"        style="@style/header"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintBottom_toBottomOf="parent"        tools:text="Welcome John Smith we hope you enjoy           using the app!"/></androidx.constraintlayout.widget.ConstraintLayout>

    You are adding aTextView field to display the full name of the user with a welcome message. The logic to create the full name and welcome message will be shown in the next step.

  6. Now, openMainActivity and add a constant value above the class header and also update the imports:
    package com.example.intentsintroductionimport android.content.Intentimport android.os.Bundleimport android.widget.Buttonimport android.widget.EditTextimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityconst val FULL_NAME_KEY = "FULL_NAME_KEY"class MainActivity : AppCompatActivity()…

    You will use the constant to set the key to hold the full name of the user by setting it in the intent.

  7. Then, add the following code to the bottom ofonCreate(savedInstanceState: Bundle?):
    findViewById<Button>(R.id.submit_button).setOnClickListener {    val fullName = findViewById<EditText>(R.id.full_name)      .text.toString().trim()    if (fullName.isNotEmpty()) {        //Set the name of the Activity to launch        Intent(this, WelcomeActivity::class.java)          .also { welcomeIntent ->            //Add the data            welcomeIntent.putExtra(FULL_NAME_KEY, fullName)            //Launch            startActivity(welcomeIntent)        }    } else {        Toast.makeText(this, getString(          R.string.full_name_label),           Toast.LENGTH_LONG).show()    }}

    There is logic to retrieve the value of the full name and verify that the user has filled this in; otherwise, a pop-up toast message will be shown if it is blank. The main logic, however, takes thefullName value of theEditText field and creates an explicit intent to startWelcomeActivity. Thealso scope function allows you to carry on using the intent you've just created,Intent(this, WelcomeActivity::class.java), and further operate on it by using something called alambda expression. The lambda argument here has a default name ofit but here for clarity we've called itwelcomeIntent. Then, you use the lambda argument in thewelcomeIntent.putExtra(FULL_NAME_KEY, fullName) line to add thefullName field, usingFULL_NAME_KEY as the key andfullName as the value to the extras that the intent holds.

    Then, you use the intent to startWelcomeActivity.

  8. Now, run the app, enter your name, and pressSUBMIT, as shown inFigure 2.14:
    Figure 2.14: The default screen displayed when the intent extras data is not processed

    Figure 2.14: The default screen displayed when the intent extras data is not processed

    Well, that's not very impressive. You've added the logic to send the user's name, but not to display it.

  9. To enable this, please openWelcomeActivity and add the following to the bottom of theonCreate(savedInstanceState: Bundle?) callback:
    //Get the intent which started this activityintent?.let {    //Set the welcome message    val fullName = it.getStringExtra(FULL_NAME_KEY)    findViewById<TextView>(R.id.welcome_text).text =      getString(R.string.welcome_text, fullName)}

    We reference the intent that started the Activity withintent?.let{} which specifies that thelet block will be run if the intent is not null, andlet is a scope function in which you can reference the context object with a default lambda argument ofit. This means you don't have to assign a variable before you can use it. You reference the intent withit and then retrieve the string value that was passed from theMainActivity intent by getting the stringFULL_NAME_KEY extra key. You then format the<string name="welcome_text">Hello %s, we hope you enjoy using the app!</string> resource string by getting the string from the resources and passing in thefullname value retrieved from the intent. Finally, this is set as the text ofTextView.

  10. Run the app again, and a simple greeting will be displayed, as inFigure 2.15:
    Figure 2.15: User welcome message displayed

Figure 2.15: User welcome message displayed

This exercise, although very simple in terms of layouts and user interaction, allows the demonstration of some core principles of intents. You will use them to add navigation and create user flows from one section of your app to another. In the next section, you will see how you can use intents to launch an Activity and receive a result back from it.

Exercise 2.05: Retrieving a Result from an Activity

For some user flows, you will only launch an Activity for the sole purpose of retrieving a result back from it. This pattern is often used to ask permission to use a particular feature, popping up a dialog with a question about whether the user gives their permission to access contacts, the calendar, and so on, and then reporting the result of yes or no back to the calling Activity. In this exercise, you will ask the user to pick their favorite color of the rainbow, and then once that is chosen, display the result in the calling activity:

  1. Create a new project namedActivity Results and add the following strings to thestrings.xml file:
        <string name="header_text_main">Please click the button       below to choose your favorite color of the rainbow!        </string>    <string name="header_text_picker">Rainbow Colors</string>    <string name="footer_text_picker">Click the button       above which is your favorite color of the rainbow.        </string>    <string name="color_chosen_message">%s is your favorite       color!</string>    <string name="submit_button_text">CHOOSE COLOR</string>    <string name="red">RED</string>    <string name="orange">ORANGE</string>    <string name="yellow">YELLOW</string>    <string name="green">GREEN</string>    <string name="blue">BLUE</string>    <string name="indigo">INDIGO</string>    <string name="violet">VIOLET</string>    <string name="unexpected_color">Unexpected color</string>
  2. Add the following colors to colors.xml
        <!--Colors of the Rainbow -->    <color name="red">#FF0000</color>    <color name="orange">#FF7F00</color>    <color name="yellow">#FFFF00</color>    <color name="green">#00FF00</color>    <color name="blue">#0000FF</color>    <color name="indigo">#4B0082</color>    <color name="violet">#9400D3</color>
  3. Add the relevant new styles to thethemes.xml file. A snippet is shown below, but you'll need to follow the link given to see all the code that you need to add:

    themes.xml

    11    <!-- Style for page header on launch screen -->12    <style name="header" parent=        "TextAppearance.AppCompat.Title">13        <item name="android:gravity">center</item>14        <item name="android:layout_marginStart">24dp</item>15        <item name="android:layout_marginEnd">24dp</item>16        <item name="android:layout_marginLeft">24dp</item>17        <item name="android:layout_marginRight">24dp</item>18        <item name="android:textSize">20sp</item>19    </style>2021    <!-- Style for page header on rainbow color         selection screen -->22    <style name="header.rainbows" parent="header">23        <item name="android:textSize">22sp</item>24        <item name="android:textAllCaps">true</item>25    </style>

    You can find the complete code herehttp://packt.live/39J0qES.

    Note

    Dimensions have not been added todimens.xml for simplicity.

  4. Now, you have to set up the Activity that will set the result you receive inMainActivity. Go toFile |New |Activity |EmptyActivity and create an Activity calledRainbowColorPickerActivity.
  5. Update theactivity_main.xml layout file to display a header, a button, and then a hiddenandroid:visibility="gone" View, which will be made visible and set with the user's favorite color of the rainbow when the result is reported:
    <?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    style="@style/page"    tools:context=".MainActivity">    <TextView        android:id="@+id/header_text"        style="@style/header"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/header_text_main"        app:layout_constraintTop_toTopOf="parent"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/>    <Button        android:id="@+id/submit_button"        style="@style/button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/submit_button_text"        app:layout_constraintTop_toBottomOf="@id/header_text"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"/>    <TextView        android:id="@+id/rainbow_color"        style="@style/color_block"        android:visibility="gone"        app:layout_constraintTop_toBottomOf="@id/          submit_button"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintEnd_toEndOf="parent"        tools:text="This is your favorite color of the           rainbow"/></androidx.constraintlayout.widget.ConstraintLayout>
  6. You'll be using thestartActivityForResult(Intent intent, int requestCode) function to get a result back from the Activity you launch. In order to ensure that the result you get back is from the operation you expected, you have to setrequestCode. Add this constant for the request code, and two others to set keys for the values we want to use in the intent, as well as a default color above the class header in MainActivity so it is displayed as follows with the package name and imports:
    package com.example.activityresultsimport android.content.Intentimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.widget.Buttonimport android.widget.TextViewconst val PICK_RAINBOW_COLOR_INTENT = 1  // The request code// Key to return rainbow color name in intentconst val RAINBOW_COLOR_NAME = "RAINBOW_COLOR_NAME" // Key to return rainbow color in intentconst val RAINBOW_COLOR = "RAINBOW_COLOR" const val DEFAULT_COLOR = "#FFFFFF" // Whiteclass MainActivity : AppCompatActivity()…
  7. Then, at the bottom ofonCreate(savedInstanceState: Bundle?) inMainActivity add the following:
            findViewById<Button>(R.id.submit_button).setOnClickListener {        //Set the name of the Activity to launch passing         //in request code            Intent(this, RainbowColorPickerActivity::class.java)            .also { rainbowColorPickerIntent ->                startActivityForResult(                    rainbowColorPickerIntent,                    PICK_RAINBOW_COLOR_INTENT                )            }        }

    This uses the syntax you used previously withalso to create an intent and use it with a named lambda parameter of the context object. In this case, you are usingrainbowColorPickerIntent to refer to the intent you just created withIntent(this, RainbowColorPickerActivity::class.java).

    The key call isstartActivityForResult(rainbowColorPickerIntent, PICK_RAINBOW_COLOR_INTENT), which launchesRainbowColorPickerActivity with a request code. So, when do we get this result back? You receive the result when it is set by overridingonActivityResult(requestCode: Int, resultCode: Int, data: Intent?).

    This call specifies the request code, which you can check to confirm that it is the same as the request code you sent.resultCode reports the status of the operation. You can set your own code, but it is usually set toActivity.RESULT_OK orActivity.RESULT_CANCELED, and the last parameter,data, is the intent that has been set by the activity started for the result, RainbowColorPickerActivity.

  8. Add the following toonActivityResult(requestCode: Int, resultCode: Int, data: Intent?) callback inMainActivity:
    override fun onActivityResult(requestCode: Int, resultCode:   Int, data: Intent?) {    super.onActivityResult(requestCode, resultCode, data)    if (requestCode == PICK_RAINBOW_COLOR_INTENT &&       resultCode == Activity.RESULT_OK) {        val backgroundColor = data?.getIntExtra(RAINBOW_COLOR,           Color.parseColor(DEFAULT_COLOR)) ?:             Color.parseColor(DEFAULT_COLOR)        val colorName = data?.getStringExtra           (RAINBOW_COLOR_NAME) ?: ""        val colorMessage = getString           (R.string.color_chosen_message, colorName)        val rainbowColor = findViewById           <TextView>(R.id.rainbow_color)rainbowColor.setBackgroundColor(ContextCompat.getColor(this,   backgroundColor))        rainbowColor.text = colorMessage        rainbowColor.isVisible = true    }}
  9. So, you check that the request code and response code values are what is expected, and then proceed to query the intent data for the values you are expecting. For this exercise, you want to get the background color name (colorName) and the hexadecimal value of the color (backgroundColor) so that we can display it. The? operator checks whether the value is null (that is, not set in the intent), and if so, the Elvis operator (?:) sets the default value. The color message uses String formatting to set a message replacing the placeholder in the resource value with the color name. Now that you've got the colors, you can make therainbow_colorTextView field visible and set the background color of the View tobackgroundColor and add text displaying the name of the user's favorite color of the rainbow.
  10. For the layout of theRainbowColorPickerActivity activity, you are going to display a button with a background color and color name for each of the seven colors of the rainbow:RED,ORANGE,YELLOW,GREEN,BLUE,INDIGO, andVIOLET. These will be displayed in aLinearLayout vertical list. For most of the layout files in the course, you will be usingConstrainLayout, as it provides fine-grained positioning of individual Views. For situations where you need to display a vertical or horizontal list of a small number of items,LinearLayout is also a good choice. If you need to display a large number of items, thenRecyclerView is a better option as it can cache layouts for individual rows and recycle views that are no longer displayed on the screen. You will learn aboutRecyclerView inChapter 5,RecyclerView.
  11. The first thing you need to do inRainbowColorPickerActivity is create the layout. This will be where you present the user with the option to choose their favorite color of the rainbow.
  12. Openactivity_rainbow_color_picker.xml and replace the layout, inserting the following:
    <?xml version="1.0" encoding="utf-8"?><ScrollView     xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="wrap_content"></ScrollView>

    We are addingScrollView to allow the contents to scroll if the screen height cannot display all of the items.ScrollView can only take one child View, which is the layout to scroll.

  13. Next, addLinearLayout withinScrollView to display the contained views in the order that they are added with a header and a footer. The first child View is a header with the title of the page and the last View that is added is a footer with instructions to the user to pick their favorite color:
        <LinearLayout        style="@style/page"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:gravity="center_horizontal"        android:orientation="vertical"        tools:context=".RainbowColorPickerActivity">    <TextView        android:id="@+id/header_text"        style="@style/header.rainbows"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/header_text_picker"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toTopOf="parent"/>    <TextView        style="@style/body"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/footer_text_picker"        app:layout_constraintEnd_toEndOf="parent"        app:layout_constraintStart_toStartOf="parent"        app:layout_constraintTop_toTopOf="parent"/>    </LinearLayout>

    The layout should now look as inFigure 2.16 in the app:

    Figure 2.16: Rainbow colors screen with a header and footer

    Figure 2.16: Rainbow colors screen with a header and footer

  14. Now, finally, add the button views between the header and the footer to select a color of the rainbow, and then run the app:
        <Button        android:id="@+id/red_button"        style="@style/button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@color/red"        android:text="@string/red"/>    <Button        .......        android:text="@string/orange"/>    <Button        .......        android:text="@string/yellow"/>    <Button        .......        android:text="@string/green"/>    <Button        .......        android:text="@string/blue"/>    <Button        .......        android:text="@string/indigo"/>    <Button        .......        android:text="@string/violet"/>

    The preceding layout created is available at the following link:http://packt.live/2M7okBX

    These Views are buttons that are displayed in the order of the colors of the rainbow. Although there is a button label for the color and the background color, which is filled in with the appropriate color, the most important XML attribute isid. This is what you will use in the Activity to prepare the result of what is returned to the calling activity.

  15. Now, openRainbowColorPickerActivity and replace the content with the following:
    package com.example.activityresultsimport android.app.Activityimport android.content.Intentimport androidx.appcompat.app.AppCompatActivityimport android.os.Bundleimport android.view.Viewimport android.widget.Toastclass RainbowColorPickerActivity : AppCompatActivity() {    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_rainbow_color_picker)    }    private fun setRainbowColor(colorName: String, color: Int) {        Intent().let { pickedColorIntent ->            pickedColorIntent.putExtra(RAINBOW_COLOR_NAME,               colorName)            pickedColorIntent.putExtra(RAINBOW_COLOR, color)            setResult(Activity.RESULT_OK, pickedColorIntent)            finish()        }    }}

    This is the function that creates an intent and puts the relevant String extras holding the rainbow color name and the rainbow colorhex value. The result is then returned to the calling Activity, and as you have no further use of this Activity, you callfinish() so that the calling Activity is displayed. The way that you retrieve the rainbow color that the user has chosen is done by adding a listener for all the buttons in the layout.

  16. Now, add the following to the bottom ofonCreate(savedInstanceState: Bundle?):
    val colorPickerClickListener = View.OnClickListener { view ->    when (view.id) {        R.id.red_button -> setRainbowColor(          getString(R.string.red), R.color.red)        R.id.orange_button -> setRainbowColor(          getString(R.string.orange), R.color.orange)        R.id.yellow_button -> setRainbowColor(          getString(R.string.yellow), R.color.yellow)        R.id.green_button -> setRainbowColor(          getString(R.string.green), R.color.green)        R.id.blue_button -> setRainbowColor(          getString(R.string.blue), R.color.blue)        R.id.indigo_button -> setRainbowColor(          getString(R.string.indigo), R.color.indigo)        R.id.violet_button -> setRainbowColor(          getString(R.string.violet), R.color.violet)        else -> {            Toast.makeText(this, getString(              R.string.unexpected_color), Toast.LENGTH_LONG)                .show()        }    }}

    ThecolorPickerClickListener click listener added in the preceding code determines which colors to set for thesetRainbowColor(colorName: String, color: Int) function by using awhen statement. Thewhen statement is the equivalent of theswitch statement in Java and languages based on C. It allows multiple conditions to be satisfied with one branch and is more concise. In the preceding example,view.id is matched against the IDs of the rainbow layout buttons and when found, executes the branch, setting the color name and hex value from the string resources to pass intosetRainbowColor(colorName: String, color: Int).

  17. Now, add this click listener to the buttons from the layout:
    findViewById<View>(R.id.red_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.orange_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.yellow_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.green_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.blue_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.indigo_button).setOnClickListener(  colorPickerClickListener)findViewById<View>(R.id.violet_button).setOnClickListener(  colorPickerClickListener)

    Every button has aClickListener interface attached, and as the operation is the same, they have the sameClickListener interface attached. Then, when the button is pressed, it sets the result of the color that the user has chosen and returns it to the calling activity.

  18. Now, run the app and press theCHOOSE COLOR button, as shown inFigure 2.17:
    Figure 2.17: The rainbow colors app start screen

    Figure 2.17: The rainbow colors app start screen

  19. Now, select your favorite color of the rainbow:
    Figure 2.18: The rainbow colors selection screen

    Figure 2.18: The rainbow colors selection screen

  20. Once you've chosen your favorite color, a screen with your favorite color will be displayed, as shown inFigure 2.19:
    Figure 2.19: The app displaying the selected color

Figure 2.19: The app displaying the selected color

As you can see, the app displays the color that you've selected as your favorite color inFigure 2.19.

This exercise introduced you to another way of creating user flows usingstartActivityForResult. This can be very useful for carrying out a dedicated Task where you need a result before proceeding with the user's flow through the app. Next, you will explore launch modes and how they impact the flow of user journeys when building apps.

Intents, Tasks, and Launch Modes

Up until now, you have been using the standard behavior for creating Activities and moving from one Activity to the next. The flow you have been using is the default, and in most cases, this will be the one you choose to use. When you open the app from the launcher with the default behavior, it creates its own Task, and each Activity you create is added to a back stack, so when you open three Activities one after the other as part of your user's journey, pressing the back button three times will move the user back through the previous screens/Activities and then go back to the device's home screen, while still keeping the app open.

The launch mode for this type of Activity is calledStandard; it is the default and doesn't need specifying in the Activity element ofAndroidManifest.xml. Even if you launch the same Activity three times, one after the other, there will be three instances of the same activity that exhibit the behavior described previously.

For some apps, you may want to change this behavior. The scenario most commonly used that doesn't conform to this pattern is when you want to relaunch an Activity without creating a new separate instance. A common use case for this is when you have a home screen with a main menu and different news stories that the user can read. Once the user has gone through to an individual news story and then presses another news story title from the menu, when the user presses the back button, they will expect to return to the home screen and not the previous news story. The launch mode that can help here is calledsingleTop. If asingleTop Activity is at the top of the Task (top, in this context, means most recently added), when the samesingleTop Activity is launched, then instead of creating a new Activity, it uses the same Activity and runs theonNewIntent callback. In the preceding scenario, this could then use the same activity to display a different news story. In this callback, you receive an intent, and you can then process this intent as you have done previously in onCreate.

There are two other launch modes to be aware of, calledSingleTask andSingleInstance. These are not for general use and are only used for special scenarios. For both of these launch modes, only one Activity of this type can exist in the application and it is always at the root of its Task. If you launch an Activity with this launch mode, it will create a new Task. If it already exists, then it will route the intent through theonNewIntent call and not create another instance. The only difference betweenSingleTask andSingleInstance is thatSingleInstance is the one and only Activity of its Task. No new Activities can be launched into its Task. In contrast,SingleTask does allow other Activities to be launched into its Task, but theSingleTask Activity is always at the root.

These launch modes can be added to the XML ofAndroidManifest.xml or created programmatically by adding intent flags. The most common ones used are the following:

  • FLAG_ACTIVITY_NEW_TASK: Launches the Activity into a new Task.
  • FLAG_ACTIVITY_CLEAR_TASK: Clears the current Task, so finishes all Activities and launches the Activity at the root of the current Task.
  • FLAG_ACTIVITY_SINGLE_TOP: Replicates the launch mode of thelaunchMode="singleTop" XML.
  • FLAG_ACTIVITY_CLEAR_TOP: Removes all Activities that are above any other instances of the same activity. If this is launched on a standard launch mode Activity, then it will clear the Task down to the first existing instance of the same Activity, and then launch another instance of the same Activity. This will probably not be what you want, and you can launch this flag with theFLAG_ACTIVITY_SINGLE_TOP flag to clear all the activities down to the same instance of the Activity you are launching and not create a new instance, but instead route a new intent to the existing Activity. To create an Activity using these twointent flags, you would do the following:
    val intent = Intent(this, MainActivity::class.java).apply {    flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or    Intent.FLAG_ACTIVITY_SINGLE_TOP}startActivity(intent)

If an intent launches an Activity with one or more of the intent flags specified in the preceding code block, then the launch mode specified overrides the one that is set in theAndroidManifest.xml file.

Intent flags can be combined in multiple ways. For more information, see the official documentation athttps://developer.android.com/reference/android/content/Intent.

You'll explore the differences in the behavior of these two launch modes in the next exercise.

Exercise 2.06: Setting the Launch Mode of an Activity

This exercise has many different layout files and Activities to illustrate the two most commonly used launch modes. Please download the code fromhttp://packt.live/2LFWo8t and then we will go through the exercise athttp://packt.live/2XUo3Vk:

  1. Open up theactivity_main.xml file and examine it.

    This illustrates a new concept when using layout files. If you have a layout file and you would like to include it in another layout, you can use the<include> XML element (have a look at the following snippet of the layout file):

    <include layout="@layout/letters"    android:id="@+id/letters_layout"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:layout_constraintLeft_toLeftOf="parent"    app:layout_constraintRight_toRightOf="parent"    app:layout_constraintTop_toBottomOf="@id/      launch_mode_standard"/><include layout="@layout/numbers"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    app:layout_constraintLeft_toLeftOf="parent"    app:layout_constraintRight_toRightOf="parent"    app:layout_constraintTop_toBottomOf="@id/      launch_mode_single_top"/>

    The preceding layout uses theinclude XML element to include the two layout files:letters.xml andnumbers.xml.

  2. Open up and inspect theletters.xml andnumbers.xml files found in theres |layout folder. These are very similar and are only differentiated from the buttons they contain by the ID of the buttons themselves and the text label they display.
  3. Run the app and you will see the following screen:
    Figure 2.20: App displaying both the standard and single top modes

    Figure 2.20: App displaying both the standard and single top modes

    In order to demonstrate/illustrate the difference betweenstandard andsingleTop activity launch modes, you have to launch two or three activities one after the other.

  4. Open upMainActivity and examine the contents of the code block inonCreate(savedInstanceState: Bundle?) after the signature:
        val buttonClickListener = View.OnClickListener { view ->        when (view.id) {            R.id.letterA -> startActivity(Intent(this,               ActivityA::class.java))            //Other letters and numbers follow the same pattern/flow            else -> {                Toast.makeText(                    this,                    getString(R.string.unexpected_button_pressed),                    Toast.LENGTH_LONG                )                .show()            }        }    }    findViewById<View>(R.id.letterA).setOnClickListener(buttonClickListener)    //The buttonClickListener is set on all the number and letter views}

    The logic contained in the main Activity and the other activities is basically the same. It displays an Activity and allows the user to press a button to launch another Activity using the same logic of creating a ClickListener and setting it on the button you saw in Exercise 2.05,Retrieving a Result from an Activity.

  5. Open theAndroidManifest.xml file and you will see the following:
    <?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.launchmodes">    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/Theme.LaunchModes">        <activity android:name=".ActivityA"           android:launchMode="standard"/>        <activity android:name=".ActivityB"           android:launchMode="standard"/>        <activity android:name=".ActivityC"           android:launchMode="standard"/>        <activity android:name=".ActivityOne"           android:launchMode="singleTop"/>        <activity android:name=".ActivityTwo"           android:launchMode="singleTop"/>        <activity android:name=".ActivityThree"           android:launchMode="singleTop"/>        <activity android:name=".MainActivity">            <intent-filter>                <action android:name=                  "android.intent.action.MAIN" />                <category android:name=                  "android.intent.category.LAUNCHER" />            </intent-filter>        </activity>    </application></manifest>

    You launch an Activity based on a button pressed on the main screen, but the letter and number activities have a different launch mode, which you can see specified in theAndroidManifest.xml file.

    Thestandard launch mode is specified here to illustrate the difference betweenstandard andsingleTop, butstandard is the default and would be how the Activity is launched if theandroid:launchMode XML attribute was not present.

  6. Press one of the letters under theStandard heading and you will see the following screen (withA or lettersC orB):
    Figure 2.21: The app displaying standard activity

    Figure 2.21: The app displaying standard activity

  7. Keep on pressing any of the letter buttons, which will launch another Activity. Logs have been added to show this sequence of launching activities. Here is the log after pressing 10 letter Activities randomly:
    2019-10-23 20:50:51.097 15281-15281/com.example.launchmodes D/MainActivity: onCreate2019-10-23 20:51:16.182 15281-15281/com.example.launchmodes D/Activity B: onCreate2019-10-23 20:51:18.821 15281-15281/com.example.launchmodes D/Activity B: onCreate2019-10-23 20:51:19.353 15281-15281/com.example.launchmodes D/Activity C: onCreate2019-10-23 20:51:20.334 15281-15281/com.example.launchmodes D/Activity A: onCreate2019-10-23 20:51:20.980 15281-15281/com.example.launchmodes D/Activity B: onCreate2019-10-23 20:51:21.853 15281-15281/com.example.launchmodes D/Activity B: onCreate2019-10-23 20:51:23.007 15281-15281/com.example.launchmodes D/Activity C: onCreate2019-10-23 20:51:23.887 15281-15281/com.example.launchmodes D/Activity B: onCreate2019-10-23 20:51:24.349 15281-15281/com.example.launchmodes D/Activity C: onCreate

    If you observe the preceding log, every time the user presses a character button in launch mode, a new instance of the character Activity is launched and added to the back stack.

  8. Close the app, making sure it is not backgrounded (or in the recents/overview menu) but is actually closed, and then open the app again and press one of the number buttons under theSingle Top heading:
    Figure 2.22: The app displaying the Single Top activity

    Figure 2.22: The app displaying the Single Top activity

  9. Press the number buttons 10 times, but make sure you press the same number button at least twice sequentially before pressing another number button.

    The logs you should see in theLogcat window (View |Tool Windows |Logcat) should be similar to the following:

    2019-10-23 21:04:50.201 15549-15549/com.example.launchmodes D/MainActivity: onCreate2019-10-23 21:05:04.503 15549-15549/com.example.launchmodes D/Activity 2: onCreate2019-10-23 21:05:08.262 15549-15549/com.example.launchmodes D/Activity 3: onCreate2019-10-23 21:05:09.133 15549-15549/com.example.launchmodes D/Activity 3: onNewIntent2019-10-23 21:05:10.684 15549-15549/com.example.launchmodes D/Activity 1: onCreate2019-10-23 21:05:12.069 15549-15549/com.example.launchmodes D/Activity 2: onNewIntent2019-10-23 21:05:13.604 15549-15549/com.example.launchmodes D/Activity 3: onCreate2019-10-23 21:05:14.671 15549-15549/com.example.launchmodes D/Activity 1: onCreate2019-10-23 21:05:27.542 15549-15549/com.example.launchmodes D/Activity 3: onNewIntent2019-10-23 21:05:31.593 15549-15549/com.example.launchmodes D/Activity 3: onNewIntent2019-10-23 21:05:38.124 15549-15549/com.example.launchmodes D/Activity 1: onCreate

You'll notice that instead of callingonCreate when you pressed the same button again, the Activity is not created, but a call is made toonNewIntent. If you press the back button, you'll notice that it will take you less than 10 clicks to back out of the app and return to the home screen, reflecting the fact that 10 activities have not been created.

Activity 2.01: Creating a Login Form

The aim of this activity is to create a login form with username and password fields. Once the values in these fields are submitted, check these entered values against hardcoded values and display a welcome message if they match, or an error message if they don't, and return the user to the login form. The steps needed to achieve this are the following:

  1. Create a form with username and passwordEditText Views and a LOGIN button.
  2. Add aClickListener interface to the button to react to a button press event.
  3. Validate that the form fields are filled in.
  4. Check the submitted username and password fields against the hardcoded values.
  5. Display a welcome message with the username if successful and hide the form.
  6. Display an error message if not successful and redirect the user back to the form.

There are a few possible ways that you could go about trying to complete this activity. Here are three ideas for approaches you could adopt:

  • Use asingleTop Activity and send an intent to route to the same Activity to validate the credentials.
  • Use astandard Activity to pass a username and password to another Activity and validate the credentials.
  • UsestartActivityForResult to carry out the validation in another Activity and then return the result.

The completed app, upon its first loading, should look as inFigure 2.23:

Figure 2.23: The app display when first loaded

Figure 2.23: The app display when first loaded

Note

The solution to this activity can be found at:http://packt.live/3sKj1cp.

The source code for all the exercises and the activity in this chapter is located athttp://packt.live/3o12sp4.

Summary

In this chapter, you have covered a lot of the groundwork of how your application interacts with the Android framework, from the Activity lifecycle callbacks to retaining the state in your activities, navigating from one screen to another, and how intents and launch modes make this happen. These are core concepts that you need to understand in order to move on to more advanced topics.

In the next chapter, you will be introduced to fragments and how they fit into the architecture of your application, as well as exploring more of the Android resources framework.

Left arrow icon

Page1 of 7

Right arrow icon
Download code iconDownload Code

Key benefits

  • Build apps with Kotlin, Google’s preferred programming language for Android development
  • Unlock solutions to development challenges with guidance from experienced Android professionals
  • Improve your apps by adding valuable features that make use of advanced functionality

Description

Are you keen to get started building Android 11 apps, but don’t know where to start? How to Build Android Apps with Kotlin is a comprehensive guide that will help kick-start your Android development practice.This book starts with the fundamentals of app development, enabling you to utilize Android Studio and Kotlin to get started building Android projects. You'll learn how to create apps and run them on virtual devices through guided exercises. Progressing through the chapters, you'll delve into Android’s RecyclerView to make the most of lists, images, and maps, and see how to fetch data from a web service.Moving ahead, you'll get to grips with testing, learn how to keep your architecture clean, understand how to persist data, and gain basic knowledge of the dependency injection pattern. Finally, you'll see how to publish your apps on the Google Play store.You'll work on realistic projects that are split up into bitesize exercises and activities, allowing you to challenge yourself in an enjoyable and attainable way. You'll build apps to create quizzes, read news articles, check weather reports, store recipes, retrieve movie information, and remind you where you parked your car.By the end of this book, you'll have the skills and confidence to build your own creative Android applications using Kotlin.

Who is this book for?

If you want to build your own Android applications using Kotlin but are unsure of how to begin, then this book is for you. To easily grasp the concepts in this book, it is recommended that you already have a basic understanding of Kotlin, or experience in a similar programming language and a willingness to brush up on Kotlin before you start.

What you will learn

  • Create maintainable and scalable apps using Kotlin
  • Understand the Android development lifecycle
  • Simplify app development with Google architecture components
  • Use standard libraries for dependency injection and data parsing
  • Apply the repository pattern to retrieve data from outside sources
  • Publish your app on the Google Play store
Estimated delivery feeDeliver to United States

Economy delivery10 - 13 business days

Free $6.95

Premium delivery6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date :Feb 26, 2021
Length:794 pages
Edition :1st
Language :English
ISBN-13 :9781838984113
Category :
Languages :
Tools :

What do you get with Print?

Product feature iconInstant access to your digital copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Redeem a companion digital copy on all Print orders
Product feature icon Access this title in our online reader with advanced features
Product feature iconDRM FREE - Read whenever, wherever and however you want
Product feature iconAI Assistant (beta) to help accelerate your learning

Contact Details

Modal Close icon
Payment Processing...
tickCompleted

Shipping Address

Billing Address

Shipping Methods
Estimated delivery feeDeliver to United States

Economy delivery10 - 13 business days

Free $6.95

Premium delivery6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date :Feb 26, 2021
Length:794 pages
Edition :1st
Language :English
ISBN-13 :9781838984113
Category :
Languages :
Concepts :
Tools :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99billed monthly
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconSimple pricing, no contract
$199.99billed annually
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts
$279.99billed in 18 months
Feature tick iconUnlimited access to Packt's library of 7,000+ practical books and videos
Feature tick iconConstantly refreshed with 50+ new titles a month
Feature tick iconExclusive Early access to books as they're written
Feature tick iconSolve problems while you work with advanced search and reference features
Feature tick iconOffline reading on the mobile app
Feature tick iconChoose a DRM-free eBook or Video every month to keep
Feature tick iconPLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick iconExclusive print discounts

Frequently bought together


Android Programming for Beginners
Android Programming for Beginners
Read more
Apr 2021742 pages
Full star icon4.2 (12)
eBook
eBook
$32.39$35.99
$43.99
Flutter Cookbook
Flutter Cookbook
Read more
Jun 2021646 pages
Full star icon4.2 (16)
eBook
eBook
$9.99$39.99
$48.99
How to Build Android Apps with Kotlin
How to Build Android Apps with Kotlin
Read more
Feb 2021794 pages
Full star icon4.6 (32)
eBook
eBook
$9.99$55.99
$69.99
Stars icon
Total$162.97
Android Programming for Beginners
$43.99
Flutter Cookbook
$48.99
How to Build Android Apps with Kotlin
$69.99
Total$162.97Stars icon
Buy 2+ to unlock$7.99 prices - master what's next.
SHOP NOW

Table of Contents

16 Chapters
1. Creating Your First AppChevron down iconChevron up icon
1. Creating Your First App
Introduction
Creating an Android Project with Android Studio
Setting Up a Virtual Device and Running Your App
The Android Manifest
Using Gradle to Build, Configure, and Manage App Dependencies
Android Application Structure
Summary
2. Building User Screen FlowsChevron down iconChevron up icon
2. Building User Screen Flows
Introduction
The Activity Lifecycle
Saving and Restoring the Activity State
Activity Interaction with Intents
Intents, Tasks, and Launch Modes
Summary
3. Developing the UI with FragmentsChevron down iconChevron up icon
3. Developing the UI with Fragments
Introduction
The Fragment Lifecycle
Static Fragments and Dual-Pane Layouts
Dynamic Fragments
Jetpack Navigation
Summary
4. Building App NavigationChevron down iconChevron up icon
4. Building App Navigation
Introduction
Navigation Overview
Navigation Drawer
Bottom Navigation
Tabbed Navigation
Summary
5. Essential Libraries: Retrofit, Moshi, and GlideChevron down iconChevron up icon
5. Essential Libraries: Retrofit, Moshi, and Glide
Introduction
Fetching Data from a Network Endpoint
Parsing a JSON Response
Loading Images from a Remote URL
Summary
6. RecyclerViewChevron down iconChevron up icon
6. RecyclerView
Introduction
Adding RecyclerView to Our Layout
Populating the RecyclerView
Responding to Clicks in RecyclerView
Supporting Different Item Types
Swiping to Remove Items
Adding Items Interactively
Summary
7. Android Permissions and Google MapsChevron down iconChevron up icon
7. Android Permissions and Google Maps
Introduction
Requesting Permissions from the User
Showing a Map of the User's Location
Map Clicks and Custom Markers
Summary
8. Services, WorkManager, and NotificationsChevron down iconChevron up icon
8. Services, WorkManager, and Notifications
Introduction
Starting a Background Task Using WorkManager
Background Operations Noticeable to the User – Using a Foreground Service
Summary
9. Unit Tests and Integration Tests with JUnit, Mockito, and EspressoChevron down iconChevron up icon
9. Unit Tests and Integration Tests with JUnit, Mockito, and Espresso
Introduction
JUnit
Android Studio Testing Tips
Mockito
Integration Tests
UI Tests
Test-Driven Development
Summary
10. Android Architecture ComponentsChevron down iconChevron up icon
10. Android Architecture Components
Introduction
ViewModel and LiveData
Room
Customizing Life Cycles
Summary
11. Persisting DataChevron down iconChevron up icon
11. Persisting Data
Introduction
Repository
Preferences
Files
Scoped Storage
Summary
12. Dependency Injection with Dagger and KoinChevron down iconChevron up icon
12. Dependency Injection with Dagger and Koin
Introduction
Manual DI
Dagger
Koin
Summary
13. RxJava and CoroutinesChevron down iconChevron up icon
13. RxJava and Coroutines
Introduction
RxJava
Coroutines
Transforming LiveData
Coroutines Channels and Flows
RxJava versus Coroutines
Summary
14. Architecture PatternsChevron down iconChevron up icon
14. Architecture Patterns
Introduction
MVVM
Data Binding
Retrofit and Moshi
The Repository Pattern
WorkManager
Summary
15. Animations and Transitions with CoordinatorLayout and MotionLayoutChevron down iconChevron up icon
15. Animations and Transitions with CoordinatorLayout and MotionLayout
Introduction
Activity Transitions
Animations with CoordinatorLayout
Animations with MotionLayout
Summary
16. Launching Your App on Google PlayChevron down iconChevron up icon
16. Launching Your App on Google Play
Introduction
Preparing Your Apps for Release
Creating a Developer Account
Uploading an App to Google Play
Managing App Releases
Summary

Recommendations for you

Left arrow icon
.NET MAUI Cookbook
.NET MAUI Cookbook
Read more
Dec 2024384 pages
Full star icon4.3 (3)
eBook
eBook
$9.99$35.99
$44.99
iOS 18 Programming for Beginners
iOS 18 Programming for Beginners
Read more
Dec 2024584 pages
Full star icon4.4 (7)
eBook
eBook
$9.99$35.99
$44.99
Apps and Services with .NET 8
Apps and Services with .NET 8
Read more
Dec 2023798 pages
Full star icon4.7 (16)
eBook
eBook
$9.99$39.99
$49.99
Flutter Design Patterns and Best Practices
Flutter Design Patterns and Best Practices
Read more
Sep 2024362 pages
Full star icon5 (4)
eBook
eBook
$9.99$35.99
$44.99
Mastering Kotlin for Android 14
Mastering Kotlin for Android 14
Read more
Apr 2024370 pages
Full star icon4.8 (10)
eBook
eBook
$9.99$31.99
$39.99
Thriving in Android Development Using Kotlin
Thriving in Android Development Using Kotlin
Read more
Jul 2024410 pages
Full star icon4.5 (6)
eBook
eBook
$9.99$35.99
$44.99
Swift Cookbook
Swift Cookbook
Read more
Jun 2024422 pages
Full star icon5 (3)
eBook
eBook
$9.99$31.99
$39.99
Android Programming for Beginners
Android Programming for Beginners
Read more
Apr 2021742 pages
Full star icon4.2 (12)
eBook
eBook
$32.39$35.99
$43.99
How to Build Android Apps with Kotlin
How to Build Android Apps with Kotlin
Read more
May 2023704 pages
Full star icon4.4 (14)
eBook
eBook
$9.99$83.99
$103.99
Right arrow icon

Customer reviews

Top Reviews
Rating distribution
Full star iconFull star iconFull star iconFull star iconHalf star icon4.6
(32 Ratings)
5 star75%
4 star12.5%
3 star6.3%
2 star6.3%
1 star0%
Filter icon Filter
Top Reviews

Filter reviews by




Kyle JablonskiMay 29, 2021
Full star iconFull star iconFull star iconFull star iconFull star icon5
This book will take you from interest to expert. There is theory to support the exercise in each chapter solidifying a baseline for your android development. As you build up your knowledge/experience moving further in the book advanced topics will help you go from capable to pragmatic. You will learn to put more thought into the design/architecture of your app to promote maintainability, scalability and testing. Coupled with theory & fundamentals this is a go to book for anyone learning to break into Android development or improve their existing understanding of developing better mobile apps for Android.
Amazon Verified reviewAmazon
J. PetersNov 14, 2022
Full star iconFull star iconFull star iconFull star iconFull star icon5
Already answered a lot of questions about app testing. Well written
Amazon Verified reviewAmazon
Amazon CustomerMar 17, 2021
Full star iconFull star iconFull star iconFull star iconFull star icon5
This is a good book. I like the fact that they decided to include a chapter on testing your application. I am a developer who really do not do a lot of testing, but by taking this book to read, I can see how testing can make your application more robust at the time of build.I recommend this book to people. You will lend a lot if you really get into it, you might get some advance topics if you are too new, but you can still follow along.
Amazon Verified reviewAmazon
Jim JupiterJul 13, 2021
Full star iconFull star iconFull star iconFull star iconFull star icon5
Kotlin and Android - i like this book
Amazon Verified reviewAmazon
gaetanoMar 07, 2021
Full star iconFull star iconFull star iconFull star iconFull star icon5
Great book for Android beginners and experts! I love the hand-on approach, with easy to follow step-by-step examples, and explanatory images.Although the book is practical and beginners focused, there are several advanced topics: Dependency Injection (with Dagger and Koin), RxJava and Coroutines, even the brand new MotionLayout!
Amazon Verified reviewAmazon
  • Arrow left icon Previous
  • 1
  • 2
  • 3
  • 4
  • 5
  • ...
  • Arrow right icon Next

People who bought this also bought

Left arrow icon
Apps and Services with .NET 7
Apps and Services with .NET 7
Read more
Nov 2022814 pages
Full star icon4.8 (27)
eBook
eBook
$9.99$39.99
$49.99
Apps and Services with .NET 8
Apps and Services with .NET 8
Read more
Dec 2023798 pages
Full star icon4.7 (16)
eBook
eBook
$9.99$39.99
$49.99
Machine Learning Techniques for Text
Machine Learning Techniques for Text
Read more
Oct 2022448 pages
Full star icon4.8 (6)
eBook
eBook
$9.99$37.99
$46.99
Test-Driven iOS Development with Swift
Test-Driven iOS Development with Swift
Read more
Apr 2022280 pages
Full star icon5 (2)
eBook
eBook
$9.99$26.99
$33.99
Right arrow icon

About the 6 authors

Left arrow icon
Profile icon Alex Forrester
Alex Forrester
LinkedIn iconGithub icon
Alex Forrester is an experienced software developer with more than 20 years of experience in mobile and web development and content management systems. He has worked with Android since 2010, creating flagship apps for blue-chip companies across a broad range of industries at Sky, The Automobile Association, HSBC, Discovery Channel, NatWest, and O2. Alex lives in Hertfordshire with his wife and daughter. When he's not developing, he likes playing touch rugby and running in the Chiltern Hills.
Read more
See other products by Alex Forrester
Profile icon Eran Boudjnah
Eran Boudjnah
LinkedIn iconGithub icon
Eran Boudjnah has been developing apps and leading mobile teams for a wide range of clients, from start-ups (Just Eat and Plume Design) to large-scale companies (Sky and HSBC) and conglomerates, since 1997. He has been working with Android since around 2013. Eran is a developer with almost three decades of experience in developing mobile applications, websites, desktop applications, and interactive attractions. He is passionate about board games and has a 1990s Transformers collection that he's quite proud of. Eran lives in Brentwood, Essex, in the United Kingdom, with Lea, his incredibly supportive wife.
Read more
See other products by Eran Boudjnah
Profile icon Alexandru Dumbravan
Alexandru Dumbravan
LinkedIn iconGithub icon
Alexandru Dumbravan is an Android developer with more than 10 years of experience in building Android apps, focusing on fintech applications since 2016 when he moved to London. In his spare time, Alex enjoys playing video games, watching movies, and occasionally visiting the gym.
Read more
See other products by Alexandru Dumbravan
Profile icon Jomar Tigcal
Jomar Tigcal
LinkedIn iconGithub icon
Jomar Tigcal is an Android developer with over 15 years of experience in mobile and software development. He has worked on various stages of Android app development for small start-ups and large companies since 2012. Jomar has also given talks and conducted training and workshops on Android. In his free time, he likes running and reading. He lives in Vancouver, BC, Canada, with his wife, Celine.
Read more
See other products by Jomar Tigcal
Profile icon Gaurav Chandorkar
Gaurav Chandorkar
Contacted TR for "The Android Workshop" by Anindya Sil on 21/04/2020
Read more
See other products by Gaurav Chandorkar
Profile icon Kaushal Dhruw
Kaushal Dhruw
Github icon
Contacted TR for "The Android Workshop" on 02/06/2020 by Anindya Sil
Read more
See other products by Kaushal Dhruw
Right arrow icon
Getfree access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the digital copy I get with my Print order?Chevron down iconChevron up icon

When you buy any Print edition of our Books, you can redeem (for free) the eBook edition of the Print Book you’ve purchased. This gives you instant access to your book when you make an order via PDF, EPUB or our online Reader experience.

What is the delivery time and cost of print book?Chevron down iconChevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium:Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge?Chevron down iconChevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order?Chevron down iconChevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries:www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges?Chevron down iconChevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live inMexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live inTurkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order?Chevron down iconChevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy?Chevron down iconChevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged?Chevron down iconChevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use?Chevron down iconChevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books?Chevron down iconChevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium:Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela

Create a Free Account To Continue Reading

Modal Close icon
OR
    First name is required.
    Last name is required.

The Password should contain at least :

  • 8 characters
  • 1 uppercase
  • 1 number
Notify me about special offers, personalized product recommendations, and learning tips By signing up for the free trial you will receive emails related to this service, you can unsubscribe at any time
By clicking ‘Create Account’, you are agreeing to ourPrivacy Policy andTerms & Conditions
Already have an account? SIGN IN

Sign in to activate your 7-day free access

Modal Close icon
OR
By redeeming the free trial you will receive emails related to this service, you can unsubscribe at any time.

[8]ページ先頭

©2009-2025 Movatter.jp