In this article by Antonio Pachón, author of the book, Mastering Android Application Development, we take a look on the TabLayout design library and the different activities you can do with it.
The TabLayout design library allows us to have fixed or scrollable tabs with text, icons, or a customized view. You would remember from the first instance of customizing tabs in this book that it isn't very easy to do, and to change from scrolling to fixed tabs, we need different implementations.
(For more resources related to this topic, see here.)
We want to change the color and design of the tabs now to be fixed; for this, we need to first go to activity_main.xml and add TabLayout, removing the previous PagerTabStrip. Our view will look as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android_layout_height="fill_parent"
android_layout_width="fill_parent"
android_orientation="vertical"
>
<android.support.design.widget.TabLayout
android_id="@+id/tab_layout"
android_layout_width="match_parent"
android_layout_height="50dp"/>
<android.support.v4.view.ViewPager
android_id="@+id/pager"
android_layout_width="match_parent"
android_layout_height="wrap_content">
</android.support.v4.view.ViewPager>
</LinearLayout>
When we have this, we need to add tabs to TabLayout. There are two ways to do this; one is to create the tabs manually as follows:
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
The second way, which is the one we'll use, is to set the view pager to TabLayout. In our example, MainActivity.java should look as follows:
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyPagerAdapter adapter = new
MyPagerAdapter(getSupportFragmentManager());
ViewPager viewPager = (ViewPager)
findViewById(R.id.pager);
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout)
findViewById(R.id.tab_layout);
tabLayout.setupWithViewPager(viewPager);
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper
.wrap(newBase));
}
}
If we don't specify any color, TabLayout will use the default color from the theme, and the position of the tabs will be fixed. Our new tab bar will look as follows:
Before continuing to add motion and animation to our app, we need to clarify the concepts of toolbar, the action bar, the app bar, and AppBarLayout, which might cause a bit of confusion.
The action bar and app bar are the same component; app bar is just a new name that has been acquired in material design. This is the opaque bar fixed at the top of our activity that usually shows the title of the app, navigation options, and the different actions. The icon is or isn't displayed depending on the theme.
Since Android 3.0, the theme Holo or any of its descendants is used by default for the action bar.
Moving onto the next concept, the toolbar; introduced in API 21, Andorid Lollipop, it is a generalization of the action bar that doesn't need to be fixed at the top of the activity. We can specify whether a toolbar is to act as the activity action bar with the setActionBar() method. This means that a toolbar can act as an action bar depending on what we want.
If we create a toolbar and set it as an action bar, we must use a theme with the .NoActionBar option to avoid having a duplicated action bar. Otherwise, we would have the one that comes by default in a theme along with the toolbar that we have created as the action bar.
A new element, called AppBarLayout, has been introduced in the design support library; it is LinearLayout intended to contain the toolbar to display animations based on scrolling events. We can specify the behavior while scrolling in the children with the app:layout_scrollFlag attribute. AppBarLayout is intended to be contained in CoordinatorLayout—the component introduced as well in the design support library—which we will describe in the following section.
CoordinatorLayout allows us to add motion to our app, connecting touch events and gestures with views. We can coordinate a scroll movement with the collapsing animation of a view, for instance. These gestures or touch events are handled by the Coordinator.Behaviour class; AppBarLayout already has this private class. If we want to use this motion with a custom view, we would have to create this behavior ourselves.
CoordinatorLayout can be implemented at the top level of our app, so we can combine this with the application bar or any element inside our activity or fragment. It also can be implemented as a container to interact with its child views.
Continuing with our app, we will show a full view of a job offer when we click on a card. This will be displayed in a new activity. This activity will contain a toolbar showing the title of the job offer and logo of the company. If the description is long, we will need to scroll down to read it, and at the same time, we want to collapse the logo at the top, as it is not relevant anymore. In the same way, while scrolling back up, we want it to expand it again. To control the collapsing of the toolbar we will need CollapsingToolbarLayout.
The description will be contained in NestedScrollView, which is a scroll view from the android v4 support library. The reason to use NestedScrollView is that this class can propagate the scroll events to the toolbar, while ScrollView can't. Ensure that compile com.android.support:support-v4:22.2.0 is up to date.
So, for now, we can just place an image from the drawable folder to implement the CoordinatorLayout functionality.
Our offer detail view, activity_offer_detail.xml, will look as follows:
<android.support.design.widget.CoordinatorLayout
android_layout_width="match_parent"
android_layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android_id="@+id/appbar"
android_layout_height="256dp"
android_layout_width="match_parent">
<android.support.design.widget.CollapsingToolbarLayout
android_id="@+id/collapsingtoolbar"
android_layout_width="match_parent"
android_layout_height="match_parent"
app_layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android_id="@+id/logo"
android_layout_width="match_parent"
android_layout_height="match_parent"
android_scaleType="centerInside"
android_src="@drawable/googlelogo"
app_layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android_id="@+id/toolbar"
android:layout_height="?attr/actionBarSize"
android_layout_width="match_parent"
app_layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android_layout_width="fill_parent"
android_layout_height="fill_parent"
android_paddingLeft="20dp"
android_paddingRight="20dp"
app_layout_behavior=
"@string/appbar_scrolling_view_behavior">
<TextView
android_id="@+id/rowJobOfferDesc"
android_layout_width="fill_parent"
android_layout_height="fill_parent"
android_text="Long scrollabe text"
android_textColor="#999"
android_textSize="18sp"
/>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
As you can see here, the CollapsingToolbar layout reacts to the scroll flag and tells its children how to react. The toolbar is pinned at the top, always remaining visible, app:layout_collapseMode="pin" however the logo will disappear with a parallax effect app:layout_collapseMode="parallax". Don't forget to add to NestedScrollview to the app:layout_behavior="@string/appbar_scrolling_view_behavior" attribute and clean the project to generate this string resource internally. If you have problems, you can set the string directly by typing "android.support.design.widget.AppBarLayout$ScrollingViewBehavior", and this will help you to identify the issue.
When we click on a job offer, we need to navigate to OfferDetailActivity and send information about the offer. As you probably know from the beginner level, to send information between activities we use intents. In these intents, we can put the data or serialized objects to be able to send an object of the JobOffer type we have to make the The JobOffer class implements Serialization. Once we have done this, we can detect the click on the element in JobOffersAdapter:
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
public TextView textViewName;
public TextView textViewDescription;
public MyViewHolder(View v){
super(v);
textViewName =
(TextView)v.findViewById(R.id.rowJobOfferTitle);
textViewDescription =
(TextView)v.findViewById(R.id.rowJobOfferDesc);
v.setOnClickListener(this);
v.setOnLongClickListener(this);
}
@Override
public void onClick(View view) {
Intent intent = new Intent(view.getContext(),
OfferDetailActivity.class);
JobOffer selectedJobOffer =
mOfferList.get(getPosition());
intent.putExtra("job_title",
selectedJobOffer.getTitle());
intent.putExtra("job_description",
selectedJobOffer.getDescription());
view.getContext().startActivity(intent);
}
Once we start the activity, we need to retrieve the title and set it to the toolbar. Add a long text to the TextView description inside NestedScrollView to first test with dummy data. We want to be able to scroll to test the animation:
public class OfferDetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offer_detail);
String job_title =
getIntent().getStringExtra("job_title");
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout)
findViewById(R.id.collapsingtoolbar);
collapsingToolbar.setTitle(job_title);
}
}
Finally, ensure that your styles.xml file in the folder values uses a theme with no action bar by default:
<resources>
<!-- Base application theme. -->
<style name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
We are now ready to test the behavior of the app. Launch it and scroll down. Take a look at how the image collapses and the toolbar is pinned at the top. It will look similar to this:
We are missing an attribute to achieve a nice effect in the animation. Just collapsing the image doesn't collapse it enough; we need to make the image disappear in a smooth way and be replaced by the background color of the toolbar.
Add the contentScrim attribute to CollapsingToolbarLayout, and this will fade in the image as is collapsing using the primary color of the theme, which is the same used by the toolbar at the moment:
<android.support.design.widget.CollapsingToolbarLayout
android_id="@+id/collapsingtoolbar"
android_layout_width="match_parent"
android_layout_height="match_parent"
app_layout_scrollFlags="scroll|exitUntilCollapsed"
app_contentScrim="?attr/colorPrimary">
With this attribute, the app looks better when collapsed and expanded:
We just need to style the app a bit more by changing colors and adding padding to the image; we can change the colors of the theme in styles.xml through the following code:
<resources>
<!-- Base application theme. -->
<style name="AppTheme"
parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#8bc34a</item>
<item name="colorPrimaryDark">#33691e</item>
<item name="colorAccent">#FF4081</item>
</style>
</resources>
Resize AppBarLayout to 190dp and add 50dp of paddingLeft and paddingRight to ImageView to achieve the following result:
In this article we learned what the TabLayout design library is and the different activities you can do with it.
Further resources on this subject: