Android Tip: Detecting long click at options menu item

Android Tip: Detecting long click at options menu item

    In Android, the options menu is where you should include actions and other options that are relevant to the current activity context, such as "Search," "Compose email," and "Settings". The option menu always located in Action Bar/Toolbar (from API 11):
    By reading my previous post, you've learned about detecting overflow button (3) clicked (opening/closing hidden menu). With other options menu item which always displayed on the Action Bar/Toolbar, handling it's click event is very easy through overriding onOptionItemSelected(MenuItem item) method but detecting it's long click event is not simple, we must set a custom view for this menu item and handle the view long click event.
    Now, with this tip, I will present a solution to solve this problem.

Default long click event of the option menu item

    Suppose we have a simple menu file with only one item like this:
menu_main.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/camera"
        android:title="Camera"
        android:icon="@android:drawable/ic_menu_camera"
        app:showAsAction="always" />

</menu>
    With menu item which has icon property, when running, this icon will be displayed instead of item title and if you long click at this item, the item title will be shown by a Toast (like ImageView's contentDescription):

Custom option menu item actionView

    So, if you want to custom the long click event of menu item, the first work is creating a new layout for it:
res\layout\layout_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    style="@android:style/Widget.ActionButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical">
    
    <Button
        android:id="@+id/button1"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:background="@android:drawable/ic_menu_camera" />
</RelativeLayout>
    And now, you must set this layout as the MenuItem action view. Get the Button by call findViewId() and handle it's long click event (by use setOnLongClickListener() method). These works are perform in onCreateOptionsMenu():
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        MenuItem item1 = menu.findItem(R.id.camera);

        MenuItemCompat.setActionView(item1, R.layout.layout_menu);
        View menuLayout = MenuItemCompat.getActionView(item1);

        View cameraMenu = menuLayout.findViewById(R.id.button1);
        cameraMenu.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                Toast.makeText(MainActivity.this, "Options menu item long clicked!", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        return super.onCreateOptionsMenu(menu);
    }
    And this is our new output when long click at the camera icon in the options menu:
    There is an important note here: with this custom, you can not handle this item "normal click" event by override onOptionsItemSelected(MenuItem item). So, if you want to perform this work, please call setOnClickListener() for the Button inside onCreateOptionsMenu(Menu menu):
cameraMenu.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this, "option menu item clicked!", Toast.LENGTH_SHORT).show();
            }
        });
    And you'll get this output when click at the camera icon:

Conclusions

    With this small tip, I hope you've learned one more trick with options menu in Android to apply to your own work. For more posts about Android menu topic, please visit this tag link. Finally, as usual, please get my full project by click the button below!

Android Tip: Detecting overflow button click event in Options Menu

Android Tip: Detecting overflow button click event in Options Menu

    In Android, the options menu is where you should include actions and other options that are relevant to the current activity context, such as "Search," "Compose email," and "Settings". The option menu always located in Action Bar/Toolbar (from API 11):
    In this post, I would like to talk about overflow button in the option menu (number (3) in the picture). By setting app:showAsAction="never" with an <item>, it will be hidden in the overflow button.
    Handling click event of each menu item is a basic work in Android app development and this is not hard to do, you only need to override onOptionsItemSelected(Menu menu) method and based on their id to perform different tasks. But, are we be able to handling the overflow button click event?
    The answer is "Yes", when click the overflow button, a menu will be shown and onMenuOpened() was called. This method description can be seen at the official document of Google developer:
boolean onMenuOpened (int featureId, Menu menu): Called when a panel's menu is opened by the user. This may also be called when the menu is changing from one type to another (for example, from the icon menu to the expanded menu).
    Absolutely similarly, onPanelClosed() will be called when the option menu closed. According to Google doc, we can notice this:
void onPanelClosed (int featureId, Menu menu): Default implementation of onPanelClosed(int, Menu) for activities. This calls through to onOptionsMenuClosed(Menu) method for the FEATURE_OPTIONS_PANEL panel, so that subclasses of Activity don't need to deal with feature codes. For context menus (FEATURE_CONTEXT_MENU), the onContextMenuClosed(Menu) will be called.
    For example, suppose we have a menu file like this:
res/menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/add"
        android:icon="@android:drawable/ic_menu_add"
        android:title="add"
        app:showAsAction="always" />

    <!-- Hidden in overflow button -->
    <item
        android:id="@+id/btn1"
        android:icon="@android:drawable/ic_menu_add"
        android:title="Item 1"
        app:showAsAction="never" />

    <item
        android:id="@+id/btn2"
        android:icon="@android:drawable/ic_menu_add"
        android:title="Item 2"
        app:showAsAction="never" />

    <item
        android:id="@+id/btn3"
        android:icon="@android:drawable/ic_menu_add"
        android:title="Item 3"
        app:showAsAction="never" />
</menu>
    And in the main activity, putting this code:
MainActivity.java
package info.devexchanges.overflowbuttonevent;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {

        Log.i("MainActivity", "open");
        return super.onMenuOpened(featureId, menu);
    }

    @Override
    public void onPanelClosed(int featureId, Menu menu) {
        super.onPanelClosed(featureId, menu);
        Log.i("MainActivity", "closed");
    }
}
    After running this activity and open/close the menu in overflow button, we have this output in LogCat:
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: open
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: open
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: closed
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: closed
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: closed
1686-1686/info.devexchanges.overflowbuttonevent I/MainActivity: closed
    As you can see, onMenuOpened() and onPanelClosed() were called many times with only one open/close action. If you would like to do something when the menu opened/closed, please attention, just do it at the first time 2 methods called!
    For example, showing a Toast when clicking at this overflow button, just rewrite your activity code like this:
MainActivity.java
package info.devexchanges.overflowbuttonevent;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private boolean isOpened = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.add) {
            Toast.makeText(this, "Adding button in action bar clicked", Toast.LENGTH_SHORT).show();
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        if (!isOpened) Toast.makeText(this, "Menu opened", Toast.LENGTH_SHORT).show();
        isOpened = true;
        return super.onMenuOpened(featureId, menu);
    }

    @Override
    public void onPanelClosed(int featureId, Menu menu) {
        super.onPanelClosed(featureId, menu);
        if (isOpened) Toast.makeText(this, "menu closed", Toast.LENGTH_SHORT).show();
        isOpened = false;
    }
}
    Running this code, you'll notice that the Toast show only one time when menu opened/closed:
    References:

Android Tip: SearchView below ActionBar/Toolbar

Android Tip: SearchView below ActionBar/Toolbar

    In some applications, they have a widget below Action Bar title like a Search View, the worth mentioning here that it make us feel that the View seem belongs to the Action Bar (because they look like in one block)! Through this post, I will present the way to make a layout like this:

Prerequisites

    We should use Toolbar as an Action Bar, so please use a "No Action Bar" theme:
styles.xml
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

Building layout

    In this sample layout, I put a SearchView below Toolbar:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/colorPrimary"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

    <RelativeLayout
        android:id="@+id/search_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:background="@color/colorPrimary"
        android:padding="@dimen/activity_horizontal_margin">

        <android.support.v7.widget.SearchView
            android:id="@+id/search_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/corners"
            app:queryHint="Type something..." />
    </RelativeLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/search_layout"
        android:padding="@dimen/activity_horizontal_margin"
        android:text="@string/short_text" />

</RelativeLayout>
    The important note here is you must set SearchView background same as Toolbar's. For this layout, the background color is colorPrimary. In programmatically code, set Toolbar as Action Bar and locating Options menu like another app:
MainActivity.java
package info.devexchanges.searchbarbelowtoolbar;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }
}
    Running application, we have this output:
    As you can see, Action Bar and Search View look like locating in one block!

More complicated layout

    With this style, we can realize that "the Action Bar region" take a large area in the display. It's not a good design when the main content is much complicated (for example, the main content can be scrolled). So, in this case, we can hide the Toolbar when scrolling the screen, our UX maybe better. In order to making this effect, follow these step:
  • Set the root layout is CoordinatorLayout.
  • Put Toolbar into AppBarLayout. 
  • Set app:layout_scrollFlags="scroll|enterAlways" for the Toolbar, it will disappear when scrolling screen. 
  • And the last, put your TextView inside NestedScrollView. 
And this is full layout code (xml file):
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusableInTouchMode="true"
    android:fitsSystemWindows="true">

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="@dimen/activity_horizontal_margin"
            android:text="@string/long_text" />
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

        <RelativeLayout
            android:id="@+id/search_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/toolbar"
            android:background="@color/colorPrimary"
            android:padding="@dimen/activity_horizontal_margin">

            <EditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/corners"
                android:drawableLeft="@android:drawable/ic_menu_camera"
                android:drawablePadding="22dp"
                android:drawableRight="@android:drawable/ic_menu_search"
                android:gravity="left|center"
                android:hint="Type some text..."
                android:padding="10dp"
                android:textColorHint="@android:color/darker_gray" />
        </RelativeLayout>

    </android.support.design.widget.AppBarLayout>

</android.support.design.widget.CoordinatorLayout>
    You should change SearchView to EditText, it better compatibility with AppBarLayout. Running app, we have this output:

Final thoughts

    Now, you know how to make a "multi-line Action Bar" with putting a widget below it. Moreover, you can read this post to find out some exciting Toolbar animations. With Material Design technology, we can make a lot of interesting UIs, you should read this official document to deep understanding this powerful design style from Google.
Bring ActionBar to pre-Honeycomb devices with ActionBarSherlock

Bring ActionBar to pre-Honeycomb devices with ActionBarSherlock

    As Android developers have known, the native Action Bar (App Bar) is available from API 11 (Android 3.0). But what about bring it to lower API devices (Android 2.x)? We can use an external library to deal with this problem.
    ActionBarSherlock is a library by Jake Wharton, that enables you to use action bars even on older devices without having to code an action bar from scratch. ActionBarSherlock automatically uses the native action bar when appropriate or wrap a custom implementation around your layouts. Using ActionBarSherlock allows you to easily develop an application with an Action Bar for every version of Android from 2.x and up.
    In this tutorial, you will learn how to implement ActionBarSherlock into your Android application.

Import to Android Studio Project

    After creating a new Android Project (I chosed the min-sdk is 8), you must import in to your project.
    Step 1: Download this library from Github, extract it, you will see these folders:
   Step 2: Add this library as an Android module, in Android Studio menu, choose File --> New... --> Import Module... and link to the "actionbarsherlock" folder:
    Step 3: After above step, we have a new module named "actionbarsherlock" in project. Edit the build.gradle file of 'app' module and add this library dependency:
    After sync gradle, we have integrated ActionBarSherlock successful to our project.

Coding project

    The first important in code is define theme for project. ActionbarSherlock provide set of themes start with Theme.Sherlock prefix. For example:     Turn to Activity programmatically code, it must extends SherlockActivity, SherlockFragmentActivity,... (your Activity type with Sherlock prefix), depending on your purpose.
    For example:
import android.os.Bundle;

import com.actionbarsherlock.app.SherlockActivity;

public class MainActivity extends SherlockActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
    And it default layout:
    Output when running app (in Android 2.3.3 device):

Styling theme for application

    Each ActionBarSherlock theme can be styled and customized like any Android SDK theme. Of course, adding some codes to styles.xml file to make your app look like better:
    Note: you can quick generate the Action Bar style with Android Action Bar Style Generator site.
    In the Activity code, I also provide more codes to create and display an Options Menu in ActionBar:
package info.devexchanges.actionbarsherlockexample;

import android.os.Bundle;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;

public class MainActivity extends SherlockActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        getSupportMenuInflater().inflate(R.menu.menu_main, menu);

        //Bring a menu item to ActionBar in Android 2.3.3 device
        menu.findItem(R.id.about).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);

        switch(item.getItemId()){

            case R.id.computer:
                Toast.makeText(getBaseContext(), "You selected Computer", Toast.LENGTH_SHORT).show();
                break;

            case R.id.camera:
                Toast.makeText(getBaseContext(), "You selected Camera", Toast.LENGTH_SHORT).show();
                break;

            case R.id.email:
                Toast.makeText(getBaseContext(), "You selected EMail", Toast.LENGTH_SHORT).show();
                break;

        }
        return true;
    }
}
    And code for menu file (locate in res/menu folder):     When running app in 2.3.3 device, with the trick in onCreateOptionsMenu() method above, you will see only "About" label is shown in the ActionBar:
    Other Menu items will displayed when you press Menu button on your device:
    And after click any Menu item, a Toast will be shown:

ActionBarSherlock vs AppCompat library

    Finally, Google has released the v7 appcompat library. It adds support for the Action Bar user interface design pattern (to the lowest API 7). This library includes support for material design user interface implementations and works very well, bring the native Action Bar to 2.x devices, so ActionBarSherlock seem out-of-date. But, it still remains one of the most famous Android libraries, very helpful for developers in the previous period of time at Android programming work.

References


ListView with Parallax Header Android

     As you can see at my previous post (about make Parallax Toolbar), with Material Design style, developer can make a lot of animations with Toolbar/ActionBar when scrolling screen. Unfortunately, we can only make them with RecyclerView or NestedScrollView, it is a pity that ListView and other "traditional" scrollable views have not been supported. If we would like to create these effections, we must handle ListView scroll event (through OnScrollListener). Today, in this post, I will provide a solution to make ListView with parallax header, please see this DEMO VIDEO to see output first:

Design layouts

    Firstly, providing activity layout contains a ListView and ImageView included in a FrameLayout as root view (always use FrameLayout, remember!):
    TextView in this layout acts as ListView header then.
    Now, design a "reality" ListView header, only includes two Spaces (subclass of View, used to occupy invisible, transparent space on the screen):
    These two Spaces height must be same with ImageView and TextView in activity layout, these invisible views will be useful to calculate the view position and will help to create the parallax effect.
    Adding a layout for each ListView item to complete xml design:
    Always providing a background for each rows like above, when ListView scrolled, this background will cover the ImageView.

Activity programmatically code

    Handling ListView scroll event is the point in code. We will check if the first ListView item is reached to top and set image header scrolls half of the amount that of ListView:
private AbsListView.OnScrollListener onScrollListener () {
        return new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                // Check if the first item is already reached to top
                if (listView.getFirstVisiblePosition() == 0) {
                    View firstChild = listView.getChildAt(0);
                    int topY = 0;
                    if (firstChild != null) {
                        topY = firstChild.getTop();
                    }

                    int headerTopY = headerSpace.getTop();
                    headerText.setY(Math.max(0, headerTopY + topY));

                    // Set the image to scroll half of the amount that of ListView
                    headerView.setY(topY * 0.5f);
                }
            }
        };
    }

Final code

    Over here, impotant codes have done. Adding some necessary methods, we have complete activity code:
package info.devexchanges.parallaxheaderlistview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private TextView headerText;
    private ListView listView;
    private View headerView;
    private View headerSpace;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView = (ListView) findViewById(R.id.list_view);
        headerView = findViewById(R.id.header_image_view);
        headerText = (TextView) findViewById(R.id.header_text);

        setListViewHeader();
        setListViewData();

        // Handle list View scroll events
        listView.setOnScrollListener(onScrollListener());
    }

    private void setListViewHeader() {
        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        @SuppressLint("InflateParams") View listHeader = inflater.inflate(R.layout.listview_header, null, false);
        headerSpace = listHeader.findViewById(R.id.header_space);

        listView.addHeaderView(listHeader);
    }

    private void setListViewData() {
        List<String> modelList = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            modelList.add("Item " + (i+1));
        }

        ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.item_listview, R.id.item, modelList);
        listView.setAdapter(adapter);
    }

    private AbsListView.OnScrollListener onScrollListener () {
        return new AbsListView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

                // Check if the first item is already reached to top
                if (listView.getFirstVisiblePosition() == 0) {
                    View firstChild = listView.getChildAt(0);
                    int topY = 0;
                    if (firstChild != null) {
                        topY = firstChild.getTop();
                    }

                    int headerTopY = headerSpace.getTop();
                    headerText.setY(Math.max(0, headerTopY + topY));

                    // Set the image to scroll half of the amount that of ListView
                    headerView.setY(topY * 0.5f);
                }
            }
        };
    }
}
    Dimensions resource (ImageView and ListView header height):

Output & Conclusions

    After running app, we have this screen:

    With some simple steps in code, we now have a ListView with parallax header. From now on, you can see my previous post to learn about this effect with RecyclerView (recomended use it instead of ListView) or go to this post to know a powerful libary which making this "scroll style". And, finally, see full project source code on @Github.

Android Material Design: Expanding/Collapsing ActionBar/Toolbar and more animations when scrolling screen

     With Meterial Design technology, it has become easier for us to create some great animations with minimal effort. By this, Toolbar is alternative to ActionBar, this change provides a lot of customizing options. Moreover, some new widgets like CoordinatorLayout, CollapsingToolbarLayout, AppBarLayout,... will help us to make a parallax Toolbar, expansible/collapsible Toolbar and other animations.
     In this post, I will present the way to make above design, please watch my DEMO VIDEO first:


Expansible/Collapsible Toolbar

    We design layout in xml file. The widget which use to set "Action Bar area" is AppBarLayout. First, putting a Toolbar object in it:
<android.support.design.widget.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="180dp"
       android:theme="@style/ThemeOverlay.AppCompat.Dark">
 
       <android.support.v7.widget.Toolbar
           android:id="@+id/toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"/>
     
   </android.support.design.widget.AppBarLayout>
    In order to make a Collapsible Toolbar, use CollapsingToolbarLayout to wrap our own Toolbar:
<android.support.design.widget.AppBarLayout
       android:layout_width="match_parent"
       android:layout_height="180dp"
       android:theme="@style/ThemeOverlay.AppCompat.Dark">
 
       <android.support.design.widget.CollapsingToolbarLayout
           android:id="@+id/collapse_toolbar"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           app:layout_scrollFlags="scroll|exitUntilCollapsed">
 
           <android.support.v7.widget.Toolbar
               android:id="@+id/toolbar"
               android:layout_width="match_parent"
               android:layout_height="?attr/actionBarSize"
               app:layout_collapseMode="pin" />
       </android.support.design.widget.CollapsingToolbarLayout>
   </android.support.design.widget.AppBarLayout>
    As you can see, set app:layout_scrollFlags="scroll|exitUntilCollapsed" property to CollapsingToolbarLayout and app:layout_collapseMode="pin" to Toolbar to make this effect. Finally, put root container layout is CoordinatorLayout (A powerful FrameLayout that specifies behavior for child views for various interactions. Allows floating views to be anchored in layout), we've completed our xml design:
activity_expand_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:theme="@style/ThemeOverlay.AppCompat.Dark">
 
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapse_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
 
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
 
</android.support.design.widget.CoordinatorLayout>
    Important note: put android:fitsSystemWindows="true" to CoordinatorLayout is final design step and use RecyclerView instead of ListView (this design not support for it), this effect will be active.
    In the activity programmatically code, set dummy data, layout manager for our RecyclerView to get running:
ExpandableToolBarActivity.java
package devexchanges.info.expandcollapseactionbar.activities;
 
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
 
import java.util.ArrayList;
 
import devexchanges.info.expandcollapseactionbar.R;
import devexchanges.info.expandcollapseactionbar.adapter.RecyclerAdapter;
 
public class ExpandableToolBarActivity extends AppCompatActivity {
 
    private ArrayList<String> stringArrayList;
    private RecyclerView recyclerView;
    private RecyclerAdapter adapter;
 
    @SuppressWarnings("ConstantConditions")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_expand_toolbar);
 
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
        CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapse_toolbar);
        collapsingToolbar.setTitle(getString(R.string.expand));
 
        recyclerView = (RecyclerView) findViewById(R.id.recycler);
        recyclerView.setHasFixedSize(true);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
 
        setData(); //adding data to array list
        adapter = new RecyclerAdapter(this, stringArrayList);
        recyclerView.setAdapter(adapter);
 
    }
 
    private void setData() {
        stringArrayList = new ArrayList<>();
 
        for (int i = 0; i < 100; i++) {
            stringArrayList.add("Item " + (i + 1));
        }
    }
}
    After running, this screen will like this:

Parallax Toolbar

    With above Toolbar style, if we insert an ImageView to extended area, we can make a prettier Toolbar with animations which called Parallax Toolbar. Our design xml file with this style:
activity_paralax_toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="192dp"
        android:fitsSystemWindows="true"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
 
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:fitsSystemWindows="true"
            app:contentScrim="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
 
            <ImageView
                android:id="@+id/header"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@mipmap/midu_cover"
                android:contentDescription="@string/paralax"
                android:fitsSystemWindows="true"
                android:scaleType="centerCrop"
                app:layout_collapseMode="parallax" />
 
            <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/ThemeOverlay.AppCompat.Light" />
 
        </android.support.design.widget.CollapsingToolbarLayout>
 
    </android.support.design.widget.AppBarLayout>
 
    <android.support.design.widget.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        app:layout_anchor="@+id/appbar"
        app:layout_anchorGravity="bottom|right|end" />
 
</android.support.design.widget.CoordinatorLayout>
    Note: Remember to add app:layout_collapseMode="parallax" to ImageView with this design.
    Like above activity, put some programmatically code to java file to complete this screen:
ParalaxToobarActivity.java
package devexchanges.info.expandcollapseactionbar.activities;
 
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
 
import java.util.ArrayList;
 
import devexchanges.info.expandcollapseactionbar.R;
import devexchanges.info.expandcollapseactionbar.adapter.RecyclerAdapter;
 
public class ParalaxToobarActivity extends AppCompatActivity {
 
    private ArrayList<String> stringArrayList;
    private RecyclerView recyclerView;
    private RecyclerAdapter adapter;
 
    @SuppressWarnings("ConstantConditions")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_paralax_toolbar);
 
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 
        CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
        collapsingToolbar.setTitle(getString(R.string.expand));
 
        recyclerView = (RecyclerView) findViewById(R.id.recycler);
        recyclerView.setHasFixedSize(true);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
 
        setData(); //adding data to array list
        adapter = new RecyclerAdapter(this, stringArrayList);
        recyclerView.setAdapter(adapter);
 
    }
 
    private void setData() {
        stringArrayList = new ArrayList<>();
 
        for (int i = 0; i < 100; i++) {
            stringArrayList.add("Item " + (i + 1));
        }
    }
}
    Output of this screen when app run:

Auto hide Toolbar when scroll screen

     With Material Design, we can auto hide Action Bar (Toolbar) without using any "trick" or any other external library. Of course, only need:
  • Set CoordinatorLayout as root view (container layout) (Don't forget to set android:fitsSystemWindows="true" to it).
  • Wrap Toolbar in AppBarLayout and set app:layout_scrollFlags="scroll|enterAlways" property to it.
  • Set app:layout_behavior="@string/appbar_scrolling_view_behavior" to RecyclerView is last step.
    And now, we have this xml file:
activity_hidden_toolbar.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
 
    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
 
    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark">
 
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways" />
    </android.support.design.widget.AppBarLayout>
 
</android.support.design.widget.CoordinatorLayout>
    This screen output:

Making a redirect activity

    Final step to finish this project is create a redirect activity (main activity) to show above activities, which is running after app was launched, have a simple code like this:
MainActivity.java
package devexchanges.info.expandcollapseactionbar.activities;

import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;

import java.util.ArrayList;

import devexchanges.info.expandcollapseactionbar.R;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView paralaxActivity = (TextView)findViewById(R.id.parallax);
        TextView expandingActivity = (TextView)findViewById(R.id.expandable);
        TextView hideActivity = (TextView)findViewById(R.id.hide);

        //set event click handling for TextViews
        hideActivity.setOnClickListener(onClickListener(hideActivity, HiddenToolbarActivity.class));
        paralaxActivity.setOnClickListener(onClickListener(paralaxActivity, ParalaxToobarActivity.class));
        expandingActivity.setOnClickListener(onClickListener(expandingActivity, ExpandableToolBarActivity.class));
    }

    private View.OnClickListener onClickListener(final TextView textView, final Class c) {
        return new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                textView.setTextColor(Color.BLUE);
                // Go to selected Activity
                Intent i = new Intent(MainActivity.this, c);
                startActivity(i);
            }
        };
    }
}
    And it layout:
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <TextView
        android:id="@+id/expandable"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="@string/expand"
        android:textSize="20sp"
        android:textStyle="bold" />
 
    <TextView
        android:id="@+id/hide"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="@string/hide"
        android:textSize="20sp"
        android:textStyle="bold" />
 
    <TextView
        android:id="@+id/parallax"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:text="@string/paralax"
        android:textSize="20sp"
        android:textStyle="bold" />
</LinearLayout>
    Some resources use for this project (strings.xml, styles.xml and colors.xml):
strings.xml
<resources>
    <string name="app_name">Expand/Collapse ActionBar</string>
 
    <string name="action_settings">Settings</string>
    <string name="expand">Expand/Collapse ActionBar</string>
    <string name="hide">Show/Hide ActionBar when scroll</string>
    <string name="paralax">ActionBar Parallax Animations</string>
</resources>
styles.xml
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primaryDark</item>
        <item name="colorAccent">@color/accent</item>
    </style>
</resources>
colors.xml
<resources>
    <color name="primary">#009688</color>
    <color name="primaryDark">#00796b</color>
    <color name="accent">#eeff41</color>
    <color name="accentLight">#F4FF81</color>
</resources>

Conclusions & References

    Through my previous post, we can learn the way to make some ActionBar animations with an external libary. With this, I hope know more Material Design style usages, which can be apply to your app. By now, please see some official documents to deep understand some new widgets:
    Update: you can custom CoordinatorLayout.Behavior to make some special effects with Toolbar and items located in it. Read this post.