Saturday, March 11, 2017

Android TabLayout Tutorial - Material Design Support Library

TabLayout is excellent user experience design component to arrange heterogeneous data in similar fashion aka categorization. Even Google Play store categorize Apps, Games, Magazines apps under tabs. Drilling down in depth Apps are categorized as Top Paid Apps, Free Apps, Trending Apps and even more as Sliding Tabs. Tabs became famous when they were introduced in Google I/O iosched application where it was implemented using SlidingTabLayout with help of viewpager component, co-ordinating the both working TabLayout was born and brought to Support library as plug and play UI component.




Download:

You can check out the source code from github repo.

Use case:

  1. When you are able categorize different types of tag containing huge amount of similar data
  2. Manage different options as tabs under single activity

Prerequisites:

  1. Android Studio with Appropriate SDK installed - Installation Steps for Ubuntu 16.04
  2. Android Support Repository installed via SDK manager
  3. Create New Android Studio project

Getting Started:

compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:design:25.2.0'
compile 'com.android.support:support-v4:25.2.0'    

Add these dependencies to buid.gradle file and hit the gradle sync button. Once you've synced these libraries and methods are available for you in offline.

Adding XML layout:

Setting up Tablyout for your application is pretty simple and straight forward. open your main activity.xml file and start typing the sample code given below.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context="com.ivisionblog.apps.materialtabsexample.MainActivity">

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

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

        <android.support.design.widget.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabIndicatorColor="@color/colorAccent"
            app:tabGravity="fill"/>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="0px"
        android:layout_weight="1"
        android:background="@android:color/white" />
</LinearLayout>
Here AppBarLayout contains Toolbar and TabLayout which co-ordinates the scrolling behavior, and then Viewpager to help to manage different fragments corresponding to the tabs by managing memory efficiently.

Bind Fragments to Tabs:

After creating XML design layout, we need to bind layout to corresponding Fragments with Tabs using Adapter. So, we need to create TabAdapter to hold all our fragments which is used by viewpager to manage those fragments and its corresponding memory.

public class MainActivity extends AppCompatActivity {

    private TabLayout tabLayout;
    private ViewPager viewPager;

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

        Toolbar mtoolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mtoolbar);
        getSupportActionBar().setTitle("Customer App");

        viewPager = (ViewPager) findViewById(R.id.viewpager);
        addFragmentsToViewPager(viewPager);

        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(viewPager);

    }
    private void addFragmentsToViewPager(ViewPager viewPager) {
        TabAdapter adapter = new TabAdapter(getSupportFragmentManager());
        adapter.addFragment(new CustomersFragment(), "Customers");
        adapter.addFragment(new ContactsFragment(), "Contacts");
        viewPager.setAdapter(adapter);
    }

}

TabAdapter.java:

class TabAdapter extends FragmentPagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public TabAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}
Thus above code maintains fragment and title as List which is extended from FragmentPagerAdapter and override the getItem and getCount method to implement. Whenever Viewpager(While swipeing or changing the tabs) requires the Fragment it calls the getItem method and which returns the Fragment to inflate into the view.

Sample Fragment:

I've added sample Fragment containing Recyclerview which is populated with Contacts data with Contacts Adapter and bind the data into the Recyclerview single contact view. The most important thing is it's good to write the recyclerview implementation in onActivityCreated method inside the fragment.


public class ContactsFragment extends Fragment {

    private RecyclerView mRecyclerview;

    public ContactsFragment() {
        // Required empty public constructor
    }

    public static ContactsFragment newInstance() {
        ContactsFragment fragment = new ContactsFragment();
        Bundle args = new Bundle();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_contacts, container, false);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mRecyclerview = (RecyclerView) getActivity().findViewById(R.id.contactlist);

        ContactsAdapter contactsAdapter = new ContactsAdapter(generateData());
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getContext());
        mRecyclerview.setLayoutManager(layoutManager);
        mRecyclerview.setAdapter(contactsAdapter);
    }

    private ArrayList<ContactsModal> generateData(){
        ArrayList<ContactsModal> contactsModals = new ArrayList<>();

        contactsModals.add(new ContactsModal("Midhun Vignesh S","987654321"));
        contactsModals.add(new ContactsModal("Shivasurya S","987654321"));
        contactsModals.add(new ContactsModal("Aswin Vayiravan","987654321"));
        contactsModals.add(new ContactsModal("Muthu Alagappan M","987654321"));
        contactsModals.add(new ContactsModal("SriramaMoorthy S","987654321"));
        contactsModals.add(new ContactsModal("Puviyarasu V","987654321"));
        contactsModals.add(new ContactsModal("Arun Kumar K R","987654321"));
        contactsModals.add(new ContactsModal("Venkat Raman","987654321"));

        return contactsModals;
    }
}
    

Sample RecyclerView Adapter & Holder:

Check out the sample recyclerview adapter and holder class to support our recyclerview which is embedded in our fragment xml file.

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.ContactsHolder> {

    private ArrayList<ContactsModal> mContactsModals;
    private FragmentManager mFm;

    public ContactsAdapter(ArrayList<ContactsModal> contactsModals){
        mContactsModals = contactsModals;
    }

    @Override
    public ContactsHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Context context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);

        View contactView = inflater.inflate(R.layout.single_contact, parent, false);

        ContactsHolder viewHolder = new ContactsHolder(contactView);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(ContactsHolder holder, int position) {
        ContactsModal contact = mContactsModals.get(position);

        holder.mPhoneView.setText(contact.getmPhoneNumber());
        holder.mContactsNameView.setText(contact.getmName());
    }

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


    public static class ContactsHolder extends RecyclerView.ViewHolder{

        TextView mContactsNameView;
        TextView mPhoneView;

        public ContactsHolder(View itemView) {
            super(itemView);
            mContactsNameView = (TextView) itemView.findViewById(R.id.nameView);
            mPhoneView = (TextView) itemView.findViewById(R.id.phoneNumberView);
        }

    }
}

And finally Fragment and single contact xml file code goes below,

<FrameLayout 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"
    tools:context="com.ivisionblog.apps.materialtabsexample.fragments.ContactsFragment">
    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:id="@+id/contactlist"
        android:layout_height="match_parent">

    </android.support.v7.widget.RecyclerView>
</FrameLayout>
Single Contact XMl file which used by contacts adapter to inflate inside recyclerview,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:clickable="true"
    android:focusable="true"
    android:foreground="?android:attr/selectableItemBackground"
    android:background="?android:attr/selectableItemBackground"
    android:layout_height="wrap_content">
    <LinearLayout
        android:layout_width="match_parent"
        android:orientation="horizontal"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight="2"
            android:layout_gravity="center"
            android:layout_height="wrap_content">
            <ImageView
                android:layout_width="match_parent"
                android:src="@mipmap/ic_launcher_round"
                android:layout_height="wrap_content" />
        </LinearLayout>
        <LinearLayout
            android:layout_width="0dp"
            android:layout_weight="8"
            android:orientation="vertical"
            android:layout_height="wrap_content">
            <TextView
                android:layout_width="match_parent"
                android:text="Shivasurya S"
                android:textSize="18sp"
                android:layout_marginTop="15dp"
                android:layout_marginLeft="15dp"
                android:textStyle="bold"
                android:id="@+id/nameView"
                android:layout_height="wrap_content" />
            <TextView
                android:layout_width="match_parent"
                android:text="9788029400"
                android:textSize="15sp"
                android:layout_marginTop="10dp"
                android:layout_marginLeft="15dp"
                android:id="@+id/phoneNumberView"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>
    <View
        android:layout_width="match_parent"
        android:background="#e7e7e7"
        android:layout_marginTop="5dp"
        android:layout_height="3dp"/>
</LinearLayout>
Thus, We have successfully integrated Tabs with Recyclerview inside fragments in our application. If you want customization of tabs in your application you can check the table and attribute into your TabLayout xml code as app:attribute namespace

The most important properties available are listed below:
NameOptionsDescription
tabBackground@drawable/imageBackground applied to the tabs
tabGravitycenterfillGravity of the tabs
tabIndicatorColor@color/blueColor of the tab indicator line
tabIndicatorHeight@dimen/tabhHeight of the tab indicator line
tabMaxWidth@dimen/tabmaxwMaximum width of the tab
tabModefixedscrollableSmall number of fixed tabs or scrolling list
tabTextColor@color/blueColor of the text on the tab

Hope you have enjoyed this post, checkout official Google Android documentation for further updates in forcoming support library versions. Feel free to comment below for doubts or chat with me in Google+/Facebook or drop me e-mail for replies. Share is care.

0 comments:

Post a comment

feel free to post your comments! Don't Spam here!