Create a class hierarchy, that is a tree of classes where children inherit functionality from parent classes. Properties and functions are inherited by subclasses.
Class hierarchy. An arrangement where classes are organized in a hierarchy of parents and children. Hierarchy diagrams are usually drawn with the parents shown above children.
Child or subclass. Any class that is below another class in the hierarchy.
Parent or superclass or base class. Any class with one or more child classes.
Root or top-level class. The class at the top (or root) of the class hierarchy.
Inheritance. When a child class includes (or inherits) all the properties and methods of its parent class. This allows you to share and reuse code, which makes programs easier to understand and maintain.
Create an abstract class where some functionality is left to be implemented by its subclasses.
An abstract class can therefore not be instantiated.
Create subclasses of an abstract class.
Use override keyword to override properties and functions in subclasses.
Use the super keyword to reference functions and properties in the parent class.
Make a class open so that it can be subclassed.
Make a propertyprivate, so it can only be used inside the class.
Use the with construct to make multiple calls on the same object instance.
View binding lets you more easily write code that interacts with the UI elements in your app.
Enable view binding
Open the app’s build.gradle file ( Gradle Scripts > build.gradle (Module: Tip_Time.app) )
In the android section, add the following lines:
Using view binding
Declare a top-level variable in the class for the binding object. It’s defined at this level because it will be used across multiple methods in MainActivity class.
The lateinit keyword is something new. It’s a promise that your code will initialize the variable before using it. If you don’t, your app will crash.
Initializes the binding object which you’ll use to access Views in the activity_main.xml layout.
Set the content view of the activity. Instead of passing the resource ID of the layout, R.layout.activity_main, this specifies the root of the hierarchy of views in your app, binding.root.
The full code in MainActivity now should be like:
Now when you need a reference to a View in your app, you can get it from the binding object instead of calling findViewById(). Thebinding object automatically defines references for every View in your app that has an ID. Using view binding is so much more concise that often you won’t even need to create a variable to hold the reference for a View, just use it directly from the binding object.
The Double data type in Kotlin can store a decimal number. Kotlin provides a method for converting a String to a Double, called toDouble().
Calling toDouble() on a string that is empty or a string that doesn’t represent a valid decimal number doesn’t work. Fortunately Kotlin also provides a method called toDoubleOrNull() which handles these problems. It returns a decimal number if it can, or it returns null if there’s a problem.
Use the checkedRadioButtonId attribute of a RadioGroup to find which RadioButton is selected.
For a Switch element, you can check the isChecked attribute to see if the switch is “on”.
To round a number you can use kotlin.math.ceil() method.
Use NumberFormat.getCurrencyInstance() to get a formatter to use for formatting numbers as currency.
You can use string parameters like %s to create dynamic strings that can still be easily translated into other languages.
You can set the string parameters by calling getString(R.string.tip_amount, formattedTip) and assign that to the text attribute of the tip result TextView.
When developing your app (and viewing the preview), it’s useful to have a placeholder for that TextView.
Testing is important!
You can use Logcat in Android Studio to troubleshoot problems like the app crashing.
A stack trace shows a list of methods that were called. This can be useful if the code generates an exception.
2020-06-24 10:09:41.564 24423-24423/com.example.tiptime E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.tiptime, PID: 24423
java.lang.NumberFormatException: empty String
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1842)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at com.example.tiptime.MainActivity.calculateTip(MainActivity.kt:22)
at com.example.tiptime.MainActivity$onCreate$1.onClick(MainActivity.kt:17)
Exceptions indicate a problem that code didn’t expect.
Null means “no value”.
Not all code can handle null values, so be careful using it.
Use Analyze > Inspect Code for suggestions to improve your code.
Place app icon files in the mipmap resource directories.
Provide different versions of an app icon bitmap image in each density bucket (mdpi, hdpi, xhdpi, xxhdpi, xxxhdpi) for backwards compatibility with older versions of Android.
mdpi - resources for medium-density screens (~160 dpi)
hdpi - resources for high-density screens (~240 dpi)
xhdpi - resources for extra-high-density screens (~320 dpi)
xxhdpi - resources for extra-extra-high-density screens (~480dpi)
xxxhdpi - resources for extra-extra-extra-high-density screens (~640dpi)
nodpi - resources that are not meant to be scaled, regardless of the screen’s pixel density
anydpi - resources that scale to any density
Add resource qualifiers onto resource directories to specify resources that should be used on devices with a certain configuration (e.g. v26).
Vector drawables are Android’s implementation of vector graphics. They are defined in XML as a set of points, lines, and curves along with associated color information. Vector drawables can be scaled for any density without loss of quality.
Adaptive icons were introduced to the Android platform in API 26. They are made up of a foreground and background layer that follow specific requirements, so that your app icon looks high-quality on a range of devices with different OEM masks.
Use Image Asset Studio in Android Studio to create legacy and adaptive icons for your app.
Use Material Design Components where possible to adhere to Material Design guidelines and allow for more customization.
In “ Add:
To create a text field as shown above, use a TextInputLayout with an enclosed TextInputEditText from the MDC library. The Material text field can be easily customized to:
Display input text or a label that’s always visible
Display an icon in the text field
Display helper or error messages
To use SwitchMaterial, you must explicitly specify SwitchMaterial in your layout and use the fully qualified path name. In the activity_main.xml layout, change the XML tag from Switch to:
it’s important to note that support for vector drawables on the Android platform wasn’t added until Android 5.0 (API level 21). in “ add:
Add icons to give users visual cues about how parts of your app will function.
Use ConstraintLayout to position elements in your layout.
A style is a collection of view attributes values for a single type of widget. For example, a TextView style can specify font color, font size, and background color, to name a few. By extracting these attributes into a style, you can easily apply the style to multiple views in the layout and maintain it in a single place (styles.xml).
Test your app for edges cases (e.g. rotating your app in landscape mode) and make improvements where applicable.
You can add a ScrollView around the ConstraintLayout to prevent UI items from getting truncated in landscape mode.
Comment your code to help other people who are reading your code understand what your approach was.
Reformat your code and clean up your code to make it as concise as possible.
Hide keyboard on Enter key
It’s a bit cumbersome to manually hide the keyboard each time to better access the calculate button. Instead, make the keyboard automatically hide itself when the Enter key is pressed.
The handleKeyEvent() is a private helper function that hides the onscreen keyboard if the keyCode input parameter is equal to KeyEvent.KEYCODE_ENTER. The InputMethodManager controls if a soft keyboard is shown, hidden, and allows the user to choose which soft keyboard is displayed. The method returns true if the key event was handled, and returns false otherwise.
The onKey() method takes in 3 input arguments: the view, the code for the key that was pressed, and a key event (which you won’t use, so you can call it “_”). When the onKey() method is called, you should call your handleKeyEvent() method and pass along the view and key code arguments. The syntax for writing this out is: view, keyCode, _ -> handleKeyEvent(view, keyCode).
Adjust the tint of the vector drawables
You can change the tint of the icons based on the primary color of the theme, so that the icons appear differently in light vs. dark theme.
One of the advantages of VectorDrawables versus bitmap images is the ability to scale and tint them. Below we have the XML representing the bell icon. There are two specific color attributes to take notice of: android:tint and android:fillColor.
Kotlin provides functionality to help you manage and manipulate collections of data more easily through the Kotlin Standard Library. A collection can be defined as a number of objects of the same data type. There are different basic collection types in Kotlin: lists, sets, and maps. This codelab focused specifically on lists, and you’ll learn more about sets and maps in future codelabs.
A list is an ordered collection of elements of a specific type, such as a list of Strings.
The index is the integer position that reflects the position of the element (e.g. myList[2]).
In a list, the first element is at index 0 (e.g. myList[0]), and the last element is at myList.size-1 (e.g. myList[myList.size-1] or myList.last()).
Kotlin also supports first() and last() operations on a list.
Another useful list operation is the contains() method to find out if a given element is in the list.
There are two types of lists: List and MutableList.
A List is read-only and cannot be modified once it has been initialized. However, you can apply operations such as sorted() and reversed() which return a new list without changing the original.
A MutableList can be modified after creation such as adding, removing, or modifying elements.
You can add a list of items to a mutable list using addAll().
Kotlin gives you a way to check if a list is empty using isEmpty() function.
Use a while loop to execute a block of code until the expression evaluates to false and you exit the loop.
while (expression) {
// While the expression is true, execute this code block
}
Use a for loop to iterate over all items of a list:
for (item in myList) {
// Execute this code block for each element of the list
}
Variations of for loop
Note: Here are some other variations of what you can do with for loops, including using them with ranges with specific steps (instead of incrementing by 1 each time).
The vararg modifier allows you to pass in a variable number of arguments to a function or constructor.
To specify a different separator other than a comma, pass in the desired separator string as an argument to the joinToString() method. Example: joinToString(" ") to separate each item with a space.
Organizing your code logically helps you and other developers understand, maintain, and extend it. In the same way that you can organize paperwork into files and folders, you can organize your code into files and packages.
RecyclerView widget helps you display a list of data.
item - One data item of the list to display. Represents one Affirmation object in your app.
Adapter - Takes data and prepares it for RecyclerView to display.
ViewHolders - A pool of views for RecyclerView to use and reuse to display affirmations.
ConstraintLayout is ideal and flexible when you want to position multiple child views in a layout. Since your layout only has a single child view, RecyclerView, you can switch to a simpler ViewGroup called FrameLayout that should be used for holding a single child view.
RecyclerView uses the adapter pattern to adapt and display the data.
ViewHolder creates and holds the views for RecyclerView.
Each item in the RecyclerView has its own layout, which you define in a separate layout file. For example, list_item.xml.
RecyclerView comes with built in LayoutManagers. RecyclerView delegates how items are laid out to LayoutManagers.
To be able to scroll through a vertical list of items that is longer than the screen, you need to add a vertical scrollbar.
Create the Affirmation data class
Create a class to be a data source
Data displayed in your app may come from different sources (e.g. within your app project or from an external source that requires connecting to the internet to download data). As a result, data may not be in the exact format that you need. The rest of the app should not concern itself with where the data originates from or in what format it is originally. You can and should hide away this data preparation in a separate Datasource class that prepares the data for the app.
For example we used strings as data sources:
The Datasource class code should be like this:
How To implement the adapter
Create a new class for the adapter, for example, ItemAdapter.
Create a custom ViewHolder class that represents a single list item view. Extend from RecyclerView.ViewHolder class.
Modify the ItemAdapter class to extend from the RecyclerView.Adapter class with the custom ViewHolder class.
Implement these methods within the adapter: getItemsCount(), onCreateViewHolder(), and onBindViewHolder().
Modify the MainActivity to use a RecyclerView
Create an instance of Datasource, and call the loadAffirmations() method on it. Store the returned list of affirmations in a val named myDataset.
Create a variable called recyclerView and use findViewById() to find a reference to the RecyclerView within the layout.
To tell the recyclerView to use the ItemAdapter class you created, create a new ItemAdapter instance. ItemAdapter expects two parameters: the context (this) of this activity, and the affirmations in myDataset.
Assign the ItemAdapter object to the adapter property of the recyclerView.
Since the layout size of your RecyclerView is fixed in the activity layout, you can set the setHasFixedSize parameter of the RecyclerView to true. This setting is only needed to improve performance. Use this setting if you know that changes in content do not change the layout size of the RecyclerView.
When you are done, the code for MainActivity should be similar to the following.
To display additional content in a RecyclerView, modify the underlying data model class and data source. Then update the list item layout and adapter to set that data onto the views.
Affirmation.kt
Datasource.kt
list_item.xml
ItemAdapter.kt
Use resource annotations to help ensure that the right type of resource ID is passed into a class constructor.
Use the Material Components for Android library to have your app more easily follow the recommended Material Design guidelines.
Use MaterialCardView to display content in a Material card.
Small visual tweaks to your app in terms of color and spacing can make the app look more polished and consistent.