Animating Toolbar Titles

Overview

I’m sure by now, everyone has used the support library’s Toolbar element. Inside of this element are things like the ActionMenu and Title. However, since this is a packaged element. We don’t have access to the child views of this view. So I am going to show you how to get the title and attach animations to it for smoothly transitioning between title changes.

Toolbar

Now, in order for us to use the Toolbar we first need to have one in the layout. So make sure you have the style set to no actionbar, and make sure you have a varient of the following in your layout:

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/Widget.MarketBot.Toolbar.ItemToolbar"
    android:elevation="4dp">

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

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

Now when you start your activity, you won’t need to do anything different. However, there is one thing you should take into account. If you set your title to an empty string, then the TextView will be removed from the Toolbar so if you plan on animating your title, you should set your title to ‘ ‘ if you want to have an empty title.

Finding the TextView

Seeing as how we don’t actually hae access to the children of the Toolbar we will have to do a pretty nasty search for it. What you will need to do is iterate over each child of the Toolbar until you find a TextView. You need to do this on the Toolbar not the support ActionBar you are setting the Toolbar to when you start an activity! It should look something like this:

private View getToolbarTitle() {
    int childCount = toolbar.getChildCount();
    for (int i = 0; i < childCount; i++) {
        View child = toolbar.getChildAt(i);
        if (child instanceof TextView) {
            return child;
        }
    }

    return new View(this);
}

Animating Title Change

When you finally have the view for your title, you can just run an animation like you usually would. For this example I just set up an AlphaAnimation that fades the title out of view, updates the value, then fades it back in. Here is what it looks like:

private void animateTitleChange(String newTitle) {
    final View view = getToolbarTitle();

    if (view instanceof TextView) {
        AlphaAnimation fadeOut = new AlphaAnimation(1f, 0f);
        fadeOut.setDuration(250);
        fadeOut.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                actionBar.setTitle(newTitle);
                
                AlphaAnimation fadeIn = new AlphaAnimation(0f, 1f);
                fadeIn.setDuration(250);
                view.startAnimation(fadeIn);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });

        view.startAnimation(fadeOut);
    }
}

Keep in mind that the TextView won’t be in the Toolbar unless it has a value, and you can’t count on it always being in the same place. Otherwise, enjoy animating the title to your hearts content.

Separate your Release and Debug only code

Overview

I’m sure you have things you want to only include inside of your project when you are making a debug build or a release build. I’m going to walk you through a way to manage this code, so you don’t have to manually remove/comment it out ever time you wanted to change your build type. This walkthrough will show you how to set up your project to use Stetho for debug builds and Crashlytics for release builds.

Gradle Setup

There are actually two different compile types that let you only include specific dependencies for different build types. So for Stetho on debug and Crashlytics on release, it should look like this:

debugCompile 'com.facebook.stetho:stetho:1.1.1'
debugCompile 'com.facebook.stetho:stetho-okhttp:1.1.1'
debugCompile 'com.facebook.stetho:stetho-urlconnection:1.1.1'

releaseCompile('com.crashlytics.sdk.android:crashlytics:2.5.2@aar') {
    transitive = true;
}

Build Folders

Inside of */src/ we need to create two new folders, debug and release. Inside of both of these folders, create a java folder. If you want to make a specific namespace this is where it would go. Since we are setting up libraries that require initialization we need to create some application classes to use. If you already have an application class, just extend these two from that one. Create these two classes in their corresponding java folders.

public final class DebugApplication extends MyApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        Stetho.initialize(
            Stetho.newInitializerBuilder(this)
                .enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
                .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(this))
                .build());
    }
}

public final class ReleaseApplication extends MyApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        Fabric.with(this, new Crashlytics());
    }
}

Additional Android Manifests

Now all we have to do is let gradle know that we want to replace the application name property depending on the build we wanted. This requires us to create two new AndroidManifest.xml files. These files should sit direct childs of the debug and release folders and should look like these:

<manifest
    package="com.w9jds.myapplication"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        tools:replace="android:name"
        android:name=".DebugApplication"/>

</manifest>

<manifest
    package="com.w9jds.myapplication"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        tools:replace="android:name"
        android:name=".ReleaseApplication"/>

</manifest>

Now when you switch between builds it will automatically switch between these two files, and you don’t have to worry about commenting out any code anymore!

Using The Design Library's CoordinatedLayout

What is CoordinatedLayout

Recently Google released a new Support library called Design. This library was specifically designed to help the user create Material Design look and feel elements to add into their application without having to create their own custom views. With an added bonus is that it supports lower versions of Android. Inside of this library we got a handful of things, but probably the most powerful tool they released was the CoordinatedLayout. This layout allows us to create context aware elements inside of our layouts so that they can behave in a specific way based on different criteria. These are Behaviors, and can be specified from a set that Google already made, or by making your own. Some of the animations that CoordinatedLayout make possible are: hiding the FloatingActionButton on scroll, hiding the ActionBarLayout on scroll, swiping away SnackBars, FloatingActionButton moving up when SnackBar appears, etc.

It is extremely import to remember that the behavior is REQUIRED to be attached to the direct child of the CoordinatedLayout.

How to use it

CoordinatedLayout is actually extremely easy to use. All it is, is a standard layout that you include in one of your layout xml files. It will look a bit like this:

<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">
    
    <FrameLayout
        android:id="@+id/content_frame"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

    </FrameLayout>
    
</android.support.design.widget.CoordinatorLayout>

As you can see, it look a lot like a LinearLayout or a FrameLayout. However, look at the FrameLayout we have in there. That layout_behavior option is specifically for direct children of CoordinatedLayout. This specific behavior is used to collapse things like an Action Bar or FloatingActionButton. Keep in mind though, for the scrolling to work you will either need a RecyclerView or a NestedScrollView otherwise this behavior will never get fired.

I can’t stress enough that the behavior has to be set to the direct child element of CoordinatedLayout. Even if the element you are putting the tag on doesn’t even scroll (like above) it still goes on that FrameLayout even though I will be loading a RecyclerView in later. This even works if you are loading a ViewPager with Fragments containing RecyclerViews into that FrameLayout. Another good example of this is if you are using a SwipeRefreshLayout. Since SwipeRefreshLayout is the child of CoordinatedLayout you need to put it on there. Then the RecyclerView contained in it will have the scroll events fire the behavior.

Other uses

CoordinatedLayout also allows you to use an anchor feature. This anchors FloatingActionButtons to a specific location on your screen so when a SnackBar appears, the button slides up to stay in position. This is also a pretty simple item to implement. All it requires are a couple of attributes on your FloatingActionButton. Below is an example of how you would anchor it to the bottom right hand side like it is for standard Material Design. This example will anchor it to the list, so that when you scroll it hides:

<android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="16dp"
    android:src="@drawable/ic_done"
    app:layout_anchor="@id/content_list"
    app:layout_anchorGravity="bottom|end" />

This also works if you want to attach it to the bottom of an action bar that collapses. Instead of having it move up from an item appearing, it follows the bottom of the action bar and disappears when the action bar is completely collapsed.

This also allows for CollapsingToolbarLayout. This layout is effected by the scrolling behavior shown above, and allows for the action bar to expand (moving the title with it) and even switch into a parallax mode where the background turns into an image. A good example of this effect is the native Android contacts application. Here is a snippet of xml to show you how to do a parallax version of the Toolbar.

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    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"
        app:layout_scrollFlags="scroll|exitUntilCollapsed"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:expandedTitleMarginStart="48dp"
        app:expandedTitleMarginEnd="64dp">

        <ImageView
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:scaleType="centerCrop"
            app:layout_collapseMode="parallax"
            android:minHeight="250dp"/>

        <android.support.v7.widget.Toolbar
            android:id="@+id/main_toolbar"
            android:layout_height="?attr/actionBarSize"
            android:layout_width="match_parent"
            android:background="@android:color/transparent"
            android:theme="@style/ToolbarOverlay"
            app:layout_scrollFlags="scroll|enterAlways"/>

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

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

And then what you can do, is add in the anchored FloatingActionButton so that it attached to the bottom of the action bar. This usually means that the top and the content are related and the action inside of the FloatingActionButton effects both areas. This usage is better outlined in the Material Design guidelines here: Floating Action Button Guidlines. Just add this button to the end of the CoordinatedLayout that the above AppBarLayout is in and you will have the same effect.

<android.support.design.widget.FloatingActionButton
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    app:fabSize="normal"
    app:layout_anchor="@id/app_bar"
    app:layout_anchorGravity="bottom|end"
    android:layout_marginEnd="16dp"/>

Overview

CoordinatedLayouts are extremely powerful and really help make your applications fluid and nice to use. The support design library really adds loads of functionality to our applications and all it really takes to implement most of them is just a little xml. All of this functionality is packed into a single support library and can be added into your project by adding this to your dependencies in gradle:

compile 'com.android.support:design:23.0.1'
Using RecyclerViews Part 2

Overview

A couple weeks ago I talked about using RecyclerViews in your application and gave you a way to put OnItemClicked back into it. That solution only works in specific places and I want to give you another option on how to do it another way. I will also go over a couple other things, like including the ripple animation on your items. The EnhancedRecyclerViewAdapter I supplied in my last post showed that you can make an interface inside of your adapter so you can set and store it externally. However, this required the listener to be static so the ViewHolder to access it. This causes a problem if you want to use the adapter for multiple RecyclerViews at once (even if it is inherited from different adapter instances). So to solve this problem you need to supply the listener in the constructor, and the interface will live inside of the ViewHolder.

OnItemClicked 2

Like before we want to put the onClick inside of our ViewHolder. However, instead of referencing a global set up inside the adapter, we want to use a listener that is passed to the constructor of the ViewHolder. All of the add methods I showed before still work for this method, so the only thing that should change is the location and declaration of the Listener. It should look something like this:

public final class NewsAdapter extends RecyclerView.Adapter<NewsAdapter.ViewHolder> {

    private Context mContext;
    private ArrayList<Object> mItems = new ArrayList<>();
    private ViewHolder.onItemClickListener mListener;

    public NewsAdapter(Context context, ViewHolder.onItemClickListener listener) {
        mContext = context;
        mListener = listener;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        TextView title;
        TextView subtitle;
        onItemClickListener listener;

        public ViewHolder(View itemView, onItemClickListener listener) {
            super(itemView);

            this.title = (TextView)itemView.findViewById(R.id.title);
            this.subtitle = (TextView)itemView.findViewById(R.id.subtitle);
            this.listener = listener;

            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (listener != null) {
                listener.onItemClick(view, getAdapterPosition());
            }
        }

        public interface onItemClickListener {
            void onItemClick(View view, int position);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        LayoutInflater inflater = LayoutInflater.from(mContext);
        View view = inflater.inflate(R.layout.twoline_item_layout, parent, false);

        return new ViewHolder(view, mListener);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        RssItem thisItem = (RssItem) getItem(position);

        Resources resources = mContext.getResources();
        holder.title.setText(thisItem.getTitle());
        holder.subtitle.setText(String.format(resources.getString(R.string.by_format), thisItem.getAuthor()));
    }

    public Object getItem(int position) {
        return mItems.get(position);
    }

    public void clear() {
        int size = mItems.size();
        mItems.clear();
        notifyItemRangeRemoved(0, size);
    }

    public void addAllItems(Collection<?> items) {
        int startPosition = mItems.size() > 0 ? mItems.size() - 1 : 0;

        mItems.addAll(items);
        notifyItemRangeInserted(startPosition, items.size());
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }
}

Ripple Animations

Okay, so you have your RecyclerView set up with your adapter set and full of items. However, when you tap any of the items there is no ripple like it does for the items of a ListView. This is actually a pretty simple fix. All you need to do is add an attribute to either the foreground, or background of your layout that is being inflated in the onCreateViewHolder of your adapter. The base layout element should look like this:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:background="?attr/selectableItemBackground"
    android:layout_height="72dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp">

Notice the ?attr/selectableItemBackground value. This is what adds the Ripple to your item.

Using RecyclerViews

Overview

RecyclerView is meant to replace the ListView since it is built to have better performance handling your recycling and remaking of views. There are a lot of advantages to using them, like a class already made for you to use as a ViewHolder. However, there are some things that are missing from the RecyclerView like the onItemClickedListener. I’m going to show you how to add that functionality back into your RecyclerView along with adding and removing items correctly and letting the adapter know how to animate the changes.

Fix Missing ItemClickedListener

First thing you will probably notice when you are going to implement your listeners on your new RecyclerView is that the setOnItemClickedListener method is gone. Don’t panic, this is actually pretty easy to add back in. Go inside of your adapter you have set up for your RecyclerView and add in an interface like so:

public interface onItemClickListener {
    void onItemClick(int position);
}

Along with that you will want to add in a static variable to the top of your adapter to store the listener you will be storing, so it should be something like this:

static onItemClickListener mItemClickListener;

Cool, so now we have the listener, but how do we add it? Well we just add in a public method that does the same thing that the old one did. Add setOnItemClickListener:

public void setOnItemClickListener(final onItemClickListener itemClickListener) {
    mItemClickListener = itemClickListener;
}

Now we are talking, just one more part you need to implement. We need to somehow trigger this event when we click on one of the items. We just need to add that inside of our ViewHolder. So it should look something like this:

public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    //Your UI elements here

    public ViewHolder(View itemView) {
        super(itemView);

        //findViewById to populate your elements here

        itemView.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        if (mItemClickListener != null) {
            mItemClickListener.onItemClick(getAdapterPosition());
        }
    }
}

This will get you the same functionality as the old onItemClick event.

Adding and Removing Items

So one of the nice features of a RecyclerView is that when you add and remove items the ItemAnimator you set auto animates the movement of you items. However, you need to tell it HOW to handle the data changes to properly trigger these animations. There are no built in add or remove methods so I will go over some basics on how to do this.

In practice using notifyDataSetChanged should only be used as a last resort since this will just refresh all of your items. So what we want to do is set up a could methods inside of your adapter that will allow you to add items, but only notify the items that have changed or moved so that the Recycler can properly animate them. Here are the two methods for adding one item:

public void addItem(Object item, int position) {
    mItems.add(position, item);
    notifyItemInserted(position);
    notifyItemMoved(position, mItems.size() - 1);
}

public void addItem(Object item) {
    int position = mItems.size() > 0 ? mItems.size() - 1 : 0;

    mItems.add(item);
    notifyItemInserted(position);
}

Notice how I place the item inside of the list of items, but instead of telling it I changed the dataset, I just say I pushed the item to a specified location and let the items after that, that they have moved. Not the same will go for adding in a range of items:

public void addAllItems(Collection<?> items) {
    int startPosition = mItems.size() > 0 ? mItems.size() - 1 : 0;

    mItems.addAll(items);
    notifyItemRangeInserted(startPosition, items.size());
}

public void addAllItems(Collection<?> items, int position) {
    mItems.addAll(position, items);
    notifyItemRangeInserted(position, items.size());
    notifyItemMoved(position, mItems.size() - 1);
}

Great! So we can now add in both a single item, and a range of items, but what if we want to remove items? Well that is almost exactly the same, just instead of insert we remove the item. It should look something like this:

public void removeItem(int position) {
    mItems.remove(position);
    notifyItemRemoved(position);
    notifyItemMoved(position, mItems.size() - 1);
}

Alright, just one more left. Say we want to completely clear out the RecyclerView. Well just add in a clear method to handle that for you:

public void clear() {
    int size = mItems.size();
    mItems.clear();
    notifyItemRangeRemoved(0, size);
}
    

With that you are all set!

Bonus

I did end up making an EnhancedRecyclerViewAdapter that you can throw into your appliation so that all you do is make an adapter and it handles everything for you if you extend it. It can be found here:

I will be creating this along with a couple more controls into a library for use through maven in the future, but until then you can just include that into your project.