Android Design Support Library: Getting Started
The Design Support Library helps you implement shiny, interactive Material Design components and supports phones running extremely old versions of Android! By Zahidur Rahman Faisal.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Android Design Support Library: Getting Started
30 mins
- Getting started
- Managing Design Support Dependencies
- Showcasing Items with RecyclerView
- Highlight with CardView
- RecyclerView in Action
- Listening to Click Events
- Using VectorDrawables for Icons
- Categorize items with BottomNavigationView
- Implementing BottomNavigationView
- Handling Item Selection on BottomNavigationView
- Adding New Items
- Using FloatingActionButton
- Interacting with TextInputLayout
- Using Snackbar for Temporary Messages
- Animating Item Details
- Using CoordinatorLayout and CollapsingToolbarLayout
- Adding Parallax Scrolling Effect
- Where To Go From Here?
Interacting with TextInputLayout
Add a TextInputLayout
in activity_add_item.xml to wrap titleEditText
and priceEditText
:
<android.support.design.widget.TextInputLayout
android:id="@+id/titleTextInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/categoryTitle"
app:counterEnabled="true"
app:counterMaxLength="30">
<EditText
android:id="@+id/titleEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_title"
android:maxLines="2" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/priceTextInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/titleTextInput"
app:counterEnabled="true"
app:counterMaxLength="30">
<EditText
android:id="@+id/priceEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hint_price"
android:inputType="numberDecimal" />
</android.support.design.widget.TextInputLayout>
<EditText
android:id="@+id/detailsEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/priceTextInput"
android:hint="@string/hint_details"
android:lines="2" />
TextInputLayout
is basically a wrapper for EditText
enhancing it to display floating hint animations, error message and character counts in a more material way.
For example, adding
app:counterEnabled="true"
app:counterMaxLength="30"
in titleTextInput
inside activity_add_item.xml displays 30 character limit for titleEditText
input and shows character count interactively and no extra line needed!
Showing error message is easy when using TextInputLayout
. You might want to check if user inputs title and price before adding an item, and show an error near the input fields if empty or invalid. Write a function in AddItemActivity.kt that does exactly that:
private fun hasValidInput(): Boolean {
// 1
val title = titleEditText.text.toString()
if (title.isNullOrBlank()) {
titleTextInput.error = "Please enter a valid Title"
return false
}
// 2
val price = priceEditText.text.toString().toDoubleOrNull()
if (price == null || price <= 0.0) {
priceTextInput.error = "Please enter a minimum Price"
return false
}
// 3
return true
}
- This section checks if user leaves
titleEditText
blank or inserts whitespace only. Then it'll set an error totitleTextInput
field and stops proceeding farther by returningfalse
- This section tries to convert user input on
priceEditText
to aDouble
value.toDoubleOrNull()
returnsnull
if the conversion fails due to and invalid input or even whitespace. Again, user must input a price greater than 0. So you set an error on thepriceTextInput
field which stops proceeding farther by returningfalse
if the price isnull
or no more than 0.0 - It returns
true
if all validation filters above passed and proceeds to add an item.
Call hasValidInput()
from inside onClickAddItem(view: View)
function like this:
fun onClickAddItem(view: View) {
if (hasValidInput()) {
val selectedCategory = categorySpinner.selectedItem as Category
DataProvider.addItem(Item(
imageId = imageFromCategory(selectedCategory),
title = titleEditText.text.toString(),
details = detailsEditText.text.toString(),
price = priceEditText.text.toString().toDouble(),
category = selectedCategory,
postedOn = System.currentTimeMillis())
)
}
}
You should clear all error message whenever user starts typing on the input fields again. To do that you need to modify beforeTextChanged()
function as following:
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
titleTextInput.error = null
priceTextInput.error = null
}
easy-peasy, huh?
Run the app and try to add an item without price - you'll be stopped with an error message!
Using Snackbar for Temporary Messages
Snackbar
is the smarter version of Toast
s in Android. You can provide action buttons like "OK" or "CANCEL" along with a message using a Snackbar
. Unlike Toast
, you need to provide a view to display the Snackbar
.
It's good practice to show a confirmation message after adding an item successfully and take back to the item list after user's acknowledgement. Let's add a function for that inside AddItemActivity
:
private fun showAddItemConfirmation() {
Snackbar.make(addItemRootView, getString(R.string.add_item_successful), Snackbar.LENGTH_LONG)
.setAction(getString(R.string.ok)) {
navigateBackToItemList()
}
.show()
}
It shows a Snackbar in addItemRootView
with a success message for a longer duration defined by Snackbar.LENGTH_LONG
.
You added an action button with text "OK" by appending
.setAction(getString(R.string.ok)) {
navigateBackToItemList()
}
which performs navigateBackToItemList()
action on button click.
Add showAddItemConfirmation()
at the bottom of onClickAddItem()
function:
fun onClickAddItem(view: View) {
if (hasValidInput()) {
val selectedCategory = categorySpinner.selectedItem as Category
DataProvider.addItem(Item(
imageId = imageFromCategory(selectedCategory),
title = titleEditText.text.toString(),
details = detailsEditText.text.toString(),
price = priceEditText.text.toString().toDouble(),
category = selectedCategory,
postedOn = System.currentTimeMillis())
)
showAddItemConfirmation()
}
}
Run the app again and to add a new item title, price and details, The output should be like this:
Animating Item Details
Presenting item details is an attractive way of providing the user more information that may lead to more items sold. One approach to making the detail page more attractive is to use animation. In this section, you'll leverage what Design Support Library offers to make the app more lucrative...
Using CoordinatorLayout and CollapsingToolbarLayout
Combining CoordinatorLayout
with CollapsingToolbarLayout
is a killer-combo that can make your app lot more fascinating to the users. Before seeing them in action, replace everything inside activity_details.xml with the following:
<?xml version="1.0" encoding="utf-8"?>
<!-- 1 -->
<android.support.design.widget.CoordinatorLayout
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"
tools:context=".ui.activity.DetailsActivity">
<!-- 2 -->
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:theme="@style/AppTheme.AppBarOverlay">
<!-- 3 -->
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsingToolbarLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/itemImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"/>
<!-- 4 -->
<android.support.v7.widget.Toolbar
android:id="@+id/toolBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<!-- 5 -->
<include layout="@layout/content_details"/>
<!-- 6 -->
<android.support.design.widget.FloatingActionButton
android:id="@+id/shareFab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:onClick="onClickShareFab"
app:layout_anchor="@+id/appBar"
app:layout_anchorGravity="bottom|end"
app:srcCompat="@android:drawable/ic_menu_share"/>
</android.support.design.widget.CoordinatorLayout>
Switch to the layout-blueprint for a better overview, then you'll go over each item in the layout, one by one:
You used the above property to overwrite relevant attributes for light overlay style.
Using the above property gradually changes CollapsingToolbarLayout
's color to the provided color when it's collapsing.
The above property means the view should scroll off until it reaches to its minimum height (?attr/actionBarSize
in this case)
assures the Toolbar
has exactly same height of a regular ActionBar
, and
pins it on top when CollapsingToolbarLayout
is fully collapsed. Finally
styles it to be elevated as like a Popup when the Toolbar
is visible.
Setting the above property will fire the onClickShareFab(view: View)
function inside DetailsActivity when user clicks on it.
These last two properties keep the button it attached to the bottom-end of the AppBarLayout
. The CoordinatorLayout
automatically manages the visibility of FloatingActionButton
when AppBarLayout
is collapsed as you set the appBar
as layout_anchor
.
-
CoordinatorLayout
is the root layout and the container for it's child views. By specifying a behavior to a direct child ofCoordinatorLayout
, you’ll be able to intercept touch events, window insets, measurement, layout, and nested scrolling. Don't panic - you'll learn to implement them in the next section! -
AppBarLayout
allows yourToolbar
and other views (such as theImageView
) to react to scroll events in a sibling view.android:theme="@style/AppTheme.AppBarOverlay"
You used the above property to overwrite relevant attributes for light overlay style.
-
CollapsingToolbarLayout
is a wrapper forToolbar
which allows theToolbar
to expand or collapse as the user scrolls through a view.app:contentScrim="@color/colorPrimary"
Using the above property gradually changes
CollapsingToolbarLayout
's color to the provided color when it's collapsing.app:layout_scrollFlags="scroll|exitUntilCollapsed"
The above property means the view should scroll off until it reaches to its minimum height (
?attr/actionBarSize
in this case) -
Toolbar
is actually a more flexible and customizableActionBar
that holds your navigation button, activity title etc. Here, usingandroid:layout_height="?attr/actionBarSize"
assures the
Toolbar
has exactly same height of a regularActionBar
, andapp:layout_collapseMode="pin"
pins it on top when
CollapsingToolbarLayout
is fully collapsed. Finallyapp:popupTheme="@style/AppTheme.PopupOverlay"
styles it to be elevated as like a Popup when the
Toolbar
is visible. - You are including a layout from content_details.xml that shows the price, title and details of the item.
-
The FloatingActionButton
allows you to share your item via a share-intent.android:onClick="onClickShareFab"
Setting the above property will fire the
onClickShareFab(view: View)
function inside DetailsActivity when user clicks on it.app:layout_anchor="@+id/appBar" app:layout_anchorGravity="bottom|end"
These last two properties keep the button it attached to the bottom-end of the
AppBarLayout
. TheCoordinatorLayout
automatically manages the visibility ofFloatingActionButton
whenAppBarLayout
is collapsed as you set theappBar
aslayout_anchor
.
android:theme="@style/AppTheme.AppBarOverlay"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"
android:onClick="onClickShareFab"
app:layout_anchor="@+id/appBar"
app:layout_anchorGravity="bottom|end"
Put everything inside content_details.xml within a NestedScrollView
, so the layout will look like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
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"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context=".ui.activity.DetailsActivity"
tools:showIn="@layout/activity_details">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/default_padding">
<TextView
android:id="@+id/priceTextView"
style="@style/TextAppearance.AppCompat.Display1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
tools:text="@string/hint_price"/>
<TextView
android:id="@+id/titleTextView"
style="@style/TextAppearance.AppCompat.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin"
android:transitionName="@string/transition_title"
tools:targetApi="lollipop"
tools:text="@string/hint_title"/>
<TextView
android:id="@+id/detailsTextView"
style="@style/TextAppearance.AppCompat.Body1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@string/hint_details"/>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
app:layout_behavior="@string/appbar_scrolling_view_behavior"
With the above property, you share scroll events on the NestedScrollView
with AppBarLayout
so that it can expand or collapse accordingly.
Finally, set the Toolbar
inside onCreate()
function in DetailsActivity.kt:
setSupportActionBar(toolBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
and modify populateDetails(item: Item?)
function like this:
private fun populateDetails(item: Item?) {
supportActionBar?.title = item?.category?.name
itemImageView.setImageResource(item?.imageId!!)
priceTextView.text = getString(R.string.currency_symbol) + item?.price.toString()
titleTextView.text = item?.title
detailsTextView.text = item?.details
}
This sets the category name in the Toolbar
title and the Item
's image to the ImageView
.
Run the app again, and navigate to the DetailsActivity
of any item - you should see something amazing: