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.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

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
}
  1. This section checks if user leaves titleEditText blank or inserts whitespace only. Then it'll set an error to titleTextInput field and stops proceeding farther by returning false
  2. This section tries to convert user input on priceEditText to a Double value. toDoubleOrNull() returns null 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 the priceTextInput field which stops proceeding farther by returning false if the price is null or no more than 0.0
  3. 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 Toasts 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.

  1. CoordinatorLayout is the root layout and the container for it's child views. By specifying a behavior to a direct child of CoordinatorLayout, 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!
  2. AppBarLayout allows your Toolbar and other views (such as the ImageView) to react to scroll events in a sibling view.
    android:theme="@style/AppTheme.AppBarOverlay"
    
  3. CollapsingToolbarLayout is a wrapper for Toolbar which allows the Toolbar to expand or collapse as the user scrolls through a view.
    app:contentScrim="@color/colorPrimary"
    
    app:layout_scrollFlags="scroll|exitUntilCollapsed"
    
  4. Toolbar is actually a more flexible and customizable ActionBar that holds your navigation button, activity title etc. Here, using
    android:layout_height="?attr/actionBarSize"
    
    app:layout_collapseMode="pin"
    
    app:popupTheme="@style/AppTheme.PopupOverlay"
    
  5. You are including a layout from content_details.xml that shows the price, title and details of the item.
  6. The FloatingActionButton allows you to share your item via a share-intent.
    android:onClick="onClickShareFab"
    
    app:layout_anchor="@+id/appBar"
    app:layout_anchorGravity="bottom|end"
    
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: