Tabs and swipe views

In the post Tab Layout in Android with ActionBar and Fragment I explain how to implement tabs and in the post Swipe views I explain how to implement the swipe views or scrolling tabs using the ViewPager of the Compatibility Package.
In this post I explain how to implement 3 tabs that look like those in the post Tab Layout in Android with ActionBar and Fragment but with similar functionality to the swipe views.
In Implementing Effective Navigation there is an example for this type of layout using ViewPager and FragmentPagerAdapter, which are part of the Compatibility Package.
I noticed that you get a similar code similar when you create a new Android project using Android SDK Tools Revision 20, and choosing as the Navigation Type in the project creation wizard “Tabs + Swipe” as shown.

The code in this post is similar to that in Implementing Effective Navigation but with the change that the layout of the fragments are taken from file .xml

  1. create an Android project
    • Build SDK: API 16
    • Minimum required SDK: API 14
    • don’t create activities
  2. import the Compatibility Package; maybe you already imported it if there is a directory “libs” with the file android-support-v4.jar, otherwise you do a right click on the project and select Android Tools -> Add Support Library or follow the instructions in Support Library
  3. edit the file AndroidManifest.xml
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="eu.lucazanini.swipeviews"
        android:versionCode="1"
        android:versionName="1.0">
    
        <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="16" />
    
       <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
            <activity
                android:name=".MainActivity"
                android:label="@string/app_name" >
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
  4. edit the file res/values/strings.xml
    <resources>
        <string name="app_name">Swipe Views</string>
        <string name="body1">one</string>
        <string name="body2">two</string>
        <string name="body3">three</string>
        <string name="label1">Tab 1</string>
        <string name="label2">Tab 2</string>
        <string name="label3">Tab 3</string>
    </resources>
    
  5. create the files main.xml, tab1.xml, tab2.xml e tab3.xml in the directory res/layout
    • main.xml
      <!--
        Copyright 2012 The Android Open Source Project
      
        Licensed under the Apache License, Version 2.0 (the "License");
        you may not use this file except in compliance with the License.
        You may obtain a copy of the License at
      
            http://www.apache.org/licenses/LICENSE-2.0
      
        Unless required by applicable law or agreed to in writing, software
        distributed under the License is distributed on an "AS IS" BASIS,
        WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
        See the License for the specific language governing permissions and
        limitations under the License.
        -->
      
      <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/pager"
          android:layout_width="match_parent"
          android:layout_height="match_parent">
      
      </android.support.v4.view.ViewPager>
      
    • tab1.xml
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/tab1"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical" 
          android:gravity="center">
      
          <TextView
              android:id="@+id/textView1"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="@string/body1" />
      
      </LinearLayout>
      
    • tab2.xml
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/tab2"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical" 
          android:gravity="center">
      
          <TextView
              android:id="@+id/textView2"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="@string/body2" />
      
      </LinearLayout>
      
    • tab3.xml
      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/tab3"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:orientation="vertical" 
          android:gravity="center">
      
          <TextView
              android:id="@+id/textView3"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_gravity="center"
              android:text="@string/body3" />
      
      </LinearLayout>
      

      the file main.xml contains a reference to ViewPager but it is missing the part on the bar of the tabs as defined in Swipe views;
      the files tab*.xml contain the layout of the fragments

  6. create the file eu/lucazanini/viewpager/MainActivity.java
    package eu.lucazanini.swipeviews;
    
    /**
     * Copyright 2012 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *     http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    import android.app.ActionBar;
    import android.app.FragmentTransaction;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentActivity;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentPagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class MainActivity extends FragmentActivity implements
    	ActionBar.TabListener {
    
        /**
         * The {@link android.support.v4.view.PagerAdapter} that will provide
         * fragments for each of the three primary sections of the app. We use a
         * {@link android.support.v4.app.FragmentPagerAdapter} derivative, which
         * will keep every loaded fragment in memory. If this becomes too memory
         * intensive, it may be best to switch to a
         * {@link android.support.v4.app.FragmentStatePagerAdapter}.
         */
        CollectionPagerAdapter mCollectionPagerAdapter;
    
        /**
         * The {@link android.support.v4.view.ViewPager} that will display the
         * object collection.
         */
        ViewPager mViewPager;
    
        public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.main);
    
    	// Create an adapter that when requested, will return a fragment
    	// representing an object in
    	// the collection.
    	//
    	// ViewPager and its adapters use support library fragments, so we must
    	// use
    	// getSupportFragmentManager.
    	mCollectionPagerAdapter = new CollectionPagerAdapter(
    		getSupportFragmentManager());
    
    	// Set up action bar.
    	final ActionBar actionBar = getActionBar();
    
    	// Specify that the Home/Up button should not be enabled, since there is
    	// no hierarchical
    	// parent.
    	actionBar.setHomeButtonEnabled(false);
    
    	// Specify that we will be displaying tabs in the action bar.
    	actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    
    	// Set up the ViewPager, attaching the adapter and setting up a listener
    	// for when the
    	// user swipes between sections.
    	mViewPager = (ViewPager) findViewById(R.id.pager);
    	mViewPager.setAdapter(mCollectionPagerAdapter);
    	mViewPager
    		.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
    		    @Override
    		    public void onPageSelected(int position) {
    			// When swiping between different app sections, select
    			// the corresponding tab.
    			// We can also use ActionBar.Tab#select() to do this if
    			// we have a reference to the
    			// Tab.
    			actionBar.setSelectedNavigationItem(position);
    		    }
    		});
    
    	// For each of the sections in the app, add a tab to the action bar.
    	for (int i = 0; i < mCollectionPagerAdapter.getCount(); i++) {
    	    // Create a tab with text corresponding to the page title defined by
    	    // the adapter.
    	    // Also specify this Activity object, which implements the
    	    // TabListener interface, as the
    	    // listener for when this tab is selected.
    	    actionBar.addTab(actionBar.newTab()
    		    .setText(mCollectionPagerAdapter.getPageTitle(i))
    		    .setTabListener(this));
    	}
        }
    
        public void onTabUnselected(ActionBar.Tab tab,
    	    FragmentTransaction fragmentTransaction) {
        }
    
        public void onTabSelected(ActionBar.Tab tab,
    	    FragmentTransaction fragmentTransaction) {
    	// When the given tab is selected, switch to the corresponding page in
    	// the ViewPager.
    	mViewPager.setCurrentItem(tab.getPosition());
        }
    
        public void onTabReselected(ActionBar.Tab tab,
    	    FragmentTransaction fragmentTransaction) {
        }
    
        /**
         * A {@link FragmentPagerAdapter} that returns a fragment corresponding to
         * one of the primary sections of the app.
         */
        public class CollectionPagerAdapter extends FragmentPagerAdapter {
    
    	final int NUM_ITEMS = 3; // number of tabs
    
    	public CollectionPagerAdapter(FragmentManager fm) {
    	    super(fm);
    	}
    
    	@Override
    	public Fragment getItem(int i) {
    	    Fragment fragment = new TabFragment();
    	    Bundle args = new Bundle();
    	    args.putInt(TabFragment.ARG_OBJECT, i);
    	    fragment.setArguments(args);
    	    return fragment;
    	}
    
    	@Override
    	public int getCount() {
    	    return NUM_ITEMS;
    	}
    
    	@Override
    	public CharSequence getPageTitle(int position) {
    	    String tabLabel = null;
    	    switch (position) {
    	    case 0:
    		tabLabel = getString(R.string.label1);
    		break;
    	    case 1:
    		tabLabel = getString(R.string.label2);
    		break;
    	    case 2:
    		tabLabel = getString(R.string.label3);
    		break;
    	    }
    
    	    return tabLabel;
    	}
        }
    
        /**
         * A fragment that launches other parts of the demo application.
         */
        public static class TabFragment extends Fragment {
    
    	public static final String ARG_OBJECT = "object";
    
    	@Override
    	public View onCreateView(LayoutInflater inflater, ViewGroup container,
    		Bundle savedInstanceState) {
    
    	    Bundle args = getArguments();
    	    int position = args.getInt(ARG_OBJECT);
    
    	    int tabLayout = 0;
    	    switch (position) {
    	    case 0:
    		tabLayout = R.layout.tab1;
    		break;
    	    case 1:
    		tabLayout = R.layout.tab2;
    		break;
    	    case 2:
    		tabLayout = R.layout.tab3;
    		break;
    	    }
    
    	    View rootView = inflater.inflate(tabLayout, container, false);
    
    	    return rootView;
    	}
        }
    
    }
    

    the main class extends FragmentActivity and not Activity as usually;
    in the event onCreate you bind an instance of FragmentPagerAdapter (mCollectionPagerAdapter) to an instance of ViewPager (mViewPager) defined in the file main.xml and it is very important you enable the NAVIGATION_MODE_TABS for the actionBar;
    in the inner class CollectionPagerAdapter you define the number of the tabs (NUM_ITEMS) and override 3 methods: getItem in which you create the fragments, getCount that simply returns the number of the fragments (tabs) and getPageTitle that returns the labels of the tabs; in an other inner class TabFragment you create the fragments choosing the layout depending on the bundle passed to the fragment in the CollectionPagerAdapter;
    the class CollectionPagerAdapter is not static like in Implementing Effective Navigation in order to get the string resources in the method getPageTitle, otherwise you need a static Context in the main class

  7. launch the app

Comments

47 responses to “Tabs and swipe views”

  1. Great tutorial! I’m trying to do the exact same in a project I’m working on. I followed your tutorial and it worked perfectly on its own as the main activity but when I set it to open as a separate activity with an intent the app always crashes. Any ideas why that might be?

    1. I figured it out: I started making the app and only later tried to implement the tabs. The original was too low an API and didn’t have an ActionBar configured. That’s why the newer ones worked, I had started them with this layout in mind.

      Thanks again for the help

  2. Thanks a lot for your example. Is the firts one I found that it works! Thansk a lot!

  3. How do i change tab text color in here??

    1. You can see Styling the Action Bar and Android Action Bar Style Generator to modify the style of the tabs.
      I wrote a post in Customizing the labels of an ActionBar where I use a TextView as tab but you have to merge the code.

  4. ciao luca!
    Grazie per la guida.
    piccola domanda…se dovessi interagire con degli elementi delle varie tab? per esempio se volessi scrivere un messaggio su un textView come posso recuperare l’id ?
    con findViewById ottengo sempre null.
    COnsigli?

    grazie!

    1. findViewById restituisce l’oggetto con un determinato id in un file di layout; nel file xml, ad esempio, una TextView è dichiarata:

      <TextView
      	android:id="@+id/myId"
      	android:layout_width="wrap_content"
      	android:layout_height="wrap_content"
      	... />
      
  5. Great tutorial. I was trying for sometime to do this.
    Very useful.

  6. Fabio Avatar

    Una spiegazione veramente molto ben fatta e lineare.

    Vorrei però porti un quesito:
    io ho messo nella prima tab uno Spinner (riempito dinamicamente con dati di un db) e una EditText per l’inserimento di stringhe per la ricerca all’interno dello Spinner.
    Il problema è che quando cambio e vado alla terza tab e poi torno alla prima non trovo i dati caricati nello spinner, mentre ad esempio rimangono le stringhe inserite nelle EditText.

    Puoi aiutarmi a risolvere questo problema?
    Grazie.

  7. sascha Avatar

    Hi,
    great tutorial, helped me a lot to add tabs to my project, thanks a lot!!!

    But how can I use icons instead of text set @ tabLabel = getString(R.string.label1); for the tabs?

    Thanks in advance!

  8. Complimenti !!
    Grande Tutorial….ho però un problema….quando l’ applicazione la faccio partire sull’ emulatore è tutto O.K.
    Di contro ho problemi quando cerco di farla girare sul mio smartphone samsung s plus quando lancio l’ esecuzione non compare il il dispositivo per poterlo selezionare … è un problema che ho solo con questo progetto….
    Sarà forse legato all’ Api 14 ?

    Grazie mille !!

    1. Se il dispositivo non compare nell’elenco fra quelli disponibili significa che è escluso sulla base di quanto specificato nell’AndroidManifest.xml e probabilmente il samsung s plus ha le API 10.

      1. Grazie mille !!

  9. Thanks a million friend.. Good explanation too..

  10. Hello,
    I m following ur tutorial

    I want to hide the title bar from my app.
    How to achieve this ?

    Thanks in advance

    1. actionBar.hide();

      1. By using actionBar.hide(); my application crashes.

        1. Sanskar Gupta Avatar
          Sanskar Gupta

          Use:
          getActionBar().hide;
          instead

  11. Bellissima guida è molto utile, però ho un problema, ho inserito un bottone nel file tab1.xml questo ha la funzione di aprire un altra activity, però quando vado a cliccare su di esso non funziona e non riesco a capire il perché.

  12. Nice…. i have wasted 3 days for implementing tab+swipe together……. finally i got a base idea from ur tutorial…. really thanks. :):):)

  13. Complimenti per il Tutorial, Una domanda:
    Ho provato ad inserire un precedente xml all’interno del tab1, nel mio xml era presente una ListView il cui layout era preso da un’altro xml (row.xml).
    Prima dell’implementazione dei tabs l’applicazione visualizzava correttamente la ListView, adesso mi da un’errore di compilazione nell’ attributo tools:listitem=”@layout/row” e l’errore e’: Attribute is missing the Android namespace prefix .
    hai una qualche idea per risolvere il problema?
    grazie

  14. Jon Finlay Avatar
    Jon Finlay

    I have the same question as a previous person (Hussain). Each tab will show a different layout with its own class which includes accessing a SQLite Database. It should be something like the this:

    int tabLayout = 0;
    switch (position) {
    case 0:
    //start new intent
    break;
    case 1:
    //start new intent
    break;
    case 2:
    //start new intent
    break;
    }

    I could not understand what meant in the reply. I´ve been searching day and night for help on this and I cannot find a suitable tutorial anywhere. I´ve followed two of your excellent tutorials, this and the swipe, but it just doesn´t help when each tab is meant to show a different class, with its own operations.

    Thanks
    Jon

  15. Hi, awesome tutorial! Easy to follow, I just have one question: I am using your sample for a project and I want to get rid of the tabs at the upper portion. I would just like to be able to slide through different activities without seeing the tabs.

    Thanks in advance!

    1. Also, I have a problem, although I have successfully made my modifications, the app works on the emulator but not on actual devices.

  16. […] the post Tabs and swipe views Szymon asks how to implement a WebView in order to preserve its state moving from one tab to […]

  17. Hi, I have a little problem with webview fragment and ViewPager. To be more specific, with reloading webview after swipe tab. I know that you made a tutorial with onPause() method in which you describe how to avoid reloaidng, but I think that it doesn’t works with this tutorial.
    An in addition another question. I made a progressdialog whish is showing while loading pages. After I added viewpager it doesn’t disappear when page is loaded. How to fix it, or add progressbar in actionbar directly?

    1. Luca Zanini Avatar
      Luca Zanini

      For your first question see here

  18. mahmoud Avatar

    hello,
    Great tutorial. how can I disable swipe for specific tab ?

  19. hussein Avatar

    Nice tutorial, but can any one helps me to start new Intent on each tab selected
    ex:

    int tabLayout = 0;
    switch (position) {
    case 0:
    //start new intent
    break;
    case 1:
    //start new intent
    break;
    case 2:
    //start new intent
    break;
    }

    1. Luca Zanini Avatar
      Luca Zanini

      In this example you must use Fragments or classes extending Fragments, but Intents launch activities, services or broadcast receivers.

  20. Complimenti! Ottimo sito ed ottimo tutorial!

  21. Thank you for this tutorial!

    1. You might be missing

      int position = args.getInt(ARG_OBJECT);

      int tabLayout = 0;
      switch (position) {
      case 0:
      tabLayout = R.layout.tab1;
      break;
      case 1:
      tabLayout = R.layout.tab2;
      break;
      case 2:
      tabLayout = R.layout.tab3;
      break;
      }

      In the onCreateView method.

      1. Luca Zanini Avatar
        Luca Zanini

        I don’t understand, I don’t miss this code, it is at the end of my post.

  22. […] dealing with this tutorial: http://www.lucazanini.eu/2012/android/tabs-and-swipe-views/?lang=en . The problem is that if I set as layout of the tab a simple static layout, like this (as the […]

  23. […] dealing with this tutorial: http://www.lucazanini.eu/2012/android/tabs-and-swipe-views/?lang=en . The problem is that if I set as layout of the tab a simple static layout, like this (as the […]

  24. While navigating from one tab to another I am getting a runtime exception which says ResourcesNotFoundException. Although all layouts have been registered.

  25. Thanks for this tutorial. I have a question. How can I set listener to editText in tab1. xml. I was trying to set it in mainActivity, but the null pointer error shows. I realised that app is searching for this editText in main.xml, not in tab1.xml. I think I should make tab1.class, and move all methods from mainActivity, to that new class. Can You give me some hints?

    1. Luca Zanini Avatar
      Luca Zanini

      You can replace the inner class TabFragment:

          public static class TabFragment extends Fragment {
      
      	public static final String ARG_OBJECT = "object";
      
      	@Override
      	public View onCreateView(LayoutInflater inflater, ViewGroup container,
      		Bundle savedInstanceState) {
      
      	    Bundle args = getArguments();
      	    int position = args.getInt(ARG_OBJECT);
      
      	    int tabLayout = 0;
      	    switch (position) {
      	    case 0:
      		tabLayout = R.layout.tab1;
      		break;
      	    case 1:
      		tabLayout = R.layout.tab2;
      		break;
      	    case 2:
      		tabLayout = R.layout.tab3;
      		break;
      	    }
      
      	    View rootView = inflater.inflate(tabLayout, container, false);
      
      	    if (position == 0) {
      		TextView tv = (TextView) rootView.findViewById(R.id.textView1);
      		tv.setOnClickListener(new View.OnClickListener() {
      		    public void onClick(View v) {
      			Context context = getActivity().getApplicationContext();
      			CharSequence text = "Hello toast!";
      			int duration = Toast.LENGTH_SHORT;
      
      			Toast toast = Toast.makeText(context, text, duration);
      			toast.show();
      		    }
      		});
      
      	    }
      
      	    return rootView;
      	}
          }
      

      Otherwise you can implement OnClickListener and override onClick

      1. hussein Avatar
        hussein

        Luca Zanini,
        Can you give me please the full project url

        1. Luca Zanini Avatar
          Luca Zanini

          There is not a full project url because there is not a full project.

  26. Very nice tutorial. It works on my Sony Xperia Arc S with Android 4.0. Do you maybe know if it’s possible to use icons instead of strings in titles of each tab?

  27. Hey, Luca.

    Great tutorial. I was trying for sometime to do this and finally got it.
    But now i’ve got another problem, maybe you can help me.
    I’m trying to setText a TextView on a tab_.xml, but i keep getting NullPointerException.
    After some research, i found out that’s something about the tab_.xml not being the main view, so i cant access de TextView id;
    So… how can I access the tab_’s content?

    Thanks. o/

    1. Luca Zanini Avatar
      Luca Zanini

      In the inner static class TabFragment, in the onCreateView event you can set the text of a TextView:

      ...
      if (position == 0) {
      	TextView tv = (TextView) rootView.findViewById(R.id.textView1);
      	tv.setText("Hello");
      }
      ...
      

      In the innser class CollectionPagerAdapter, in the getItem event you can set the text of a TextView:

      ...
      if (i == 2) {
      	TextView tv = (TextView) findViewById(R.id.textView2);
      	tv.setText("Bye");
      }
      ...
      
  28. Very nice tut !! I’m looking for a tut like this one since a long time. Can you explain also how to load fragment instead of xml view ??

    Best regards

    1. Luca Zanini Avatar
      Luca Zanini

      You can instantiate layout elements at runtime without a xml resource.
      See http://www.linux-mag.com/id/7705/

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.