Swipe views

In a previous post I implemented a layout with 2 tabs using an action bar and fragments; the disadvantage of this layout is that dragging horizzontally the views in a similar way to Google Play would be very natural for the user.
In Horizontal View Swiping with ViewPager you can see how to get this layout using ViewPager, which is 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 “Swipe Views + Title Strip” as shown.

The code in this post is the same as that in Horizontal View Swiping with ViewPager 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">
      
          <!--
          This title strip will display the currently visible page title, as well as the page
          titles for adjacent pages.
          -->
          <android.support.v4.view.PagerTitleStrip android:id="@+id/pager_title_strip"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:layout_gravity="top"
              android:background="#33b5e5"
              android:textColor="#fff"
              android:paddingTop="4dp"
              android:paddingBottom="4dp" />
      
      </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 consists of 2 parts: in the first part (android.support.v4.view.ViewPager) you define the ViewPager that contains the fragments of the tabs, in the second part (android.support.v4.view.PagerTitleStrip) you define the bar of the tabs setting its positions, colors and padding;
    the files tab*.xml contain the layout of the fragments

  6. create the file eu.lucazanini.viewpager.MainActivity.java
    /**
     * 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.
     */
    
    package eu.lucazanini.swipeviews;
    
    import android.app.ActionBar;
    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.FragmentStatePagerAdapter;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    public class MainActivity extends FragmentActivity {
    
        /**
         * The {@link android.support.v4.view.PagerAdapter} that will provide
         * fragments representing each object in a collection. We use a
         * {@link android.support.v4.app.FragmentStatePagerAdapter} derivative,
         * which will destroy and re-create fragments as needed, saving and
         * restoring their state in the process. This is important to conserve
         * memory and is a best practice when allowing navigation between objects in
         * a potentially large collection.
         */
        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 button should show an "Up" caret, indicating
    	// that touching the
    	// button will take the user one step up in the application's hierarchy.
    	actionBar.setDisplayHomeAsUpEnabled(true);
    
    	// Set up the ViewPager, attaching the adapter.
    	mViewPager = (ViewPager) findViewById(R.id.pager);
    	mViewPager.setAdapter(mCollectionPagerAdapter);
        }
    
        /**
         * A {@link android.support.v4.app.FragmentStatePagerAdapter} that returns a
         * fragment representing an object in the collection.
         */
        public class CollectionPagerAdapter extends FragmentStatePagerAdapter {
    
    	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 dummy fragment representing a section of the app, but that simply
         * displays dummy text.
         */
        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 FragmentStatePagerAdapter (mCollectionPagerAdapter) to an instance of ViewPager (mViewPager) defined in the file main.xml;
    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 Horizontal View Swiping with ViewPager 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

25 responses to “Swipe views”

  1. Ciao Luca seguito tutti i tuoi passaggi di questo tutorial, ma dopo aver lanciato l’applicazione sul mio telefono mi compare la notifica che l’applicazione si è fermata, in Android Studio non ci sono errori! da cosa può dipendere? La mia versione di Android è 4.4.2! Ho anche seguito la spiegazione sul sito di Android Studio l’applicazione viene eseguita ma non avendo i singoli tab nella cartella Layout non so come inserire i contenuti! Complimenti per i tuoi tutorial!

  2. Leonardo Avatar

    Ancora ciao, Vorrei inserire un bottone con un onClickActionListener, ad esempio in tab1. Come potrei fare? Ho dichiarato il bottone nel manifest ma nel codice java non so proprio dove inserirlo. Se provo mi crasha !

    1. Luca Zanini Avatar
      Luca Zanini

      Intendi setOnClickListener?
      Eventualmente qui trovi un esempio che forse riesci ad adattare.
      Il pulsante non deve essere dichiarato in AndroidManifest.xml

      1. Leonardo Avatar

        Scusa, ho sbagliato a dire manifest; volevo dire nel layout xml 😀 . In ogni caso poi ho risolto.
        Grazie ancora 🙂

        1. kawingtam Avatar
          kawingtam

          I’m not able to create onClickListener in my page, how can I fix it?

          1. Luca Zanini Avatar
            Luca Zanini

            you need

            import android.view.View.OnClickListener;

  3. Leonardo Avatar

    Ciao, grazie per il tutorial. Come mai, nonostante il minimo SDK richiesto sia il 14, ovvero IceCream Sandwich, ci sono applicazioni simili anche per Android Gingerbread? Se provassi questo tutorial sulla mia versione, cioè 2.3.3, funzionerebbe?
    Grazie ancora.

    1. Luca Zanini Avatar
      Luca Zanini

      Credo di si, ma devi commentare le righe con actionBar nella classe MainActivity perchè la ActionBar è stata aggiunta con Android 3.0 (API level 11)

      1. Leonardo Avatar

        Grazie poi ho risolto. Utilizzando la libreria Sherlock si riesce a bypassare questo problema di compatibilità e, al posto di estendere FragmentActivity, si estende SherlockFragmentActivity che ha gli stessi metodi.

        1. Ciao, sono nuovo della programmazione Android, sono nella stessa situazione proposta da te in questo commento (compatibilità con la 2.3.3), volevo chiedere come avere la libreria Sherlock, se si deve scaricare esternamente al sdk e come caricarla.

          1. Ho risolto, ma in
            final ActionBar actionBar = getSupportActionBar();
            mi da l’errore “Type mismatch: cannot convert from com.actionbarsherlock.app.ActionBar to android.app.ActionBar”,
            come potrei risolvere??

  4. stefano Avatar

    Ciao Luca!avrei un dubbio. La mia app deve simulare una sorta di portafoglio all’interno del quale sono presenti diverse carte di credito. Nella mia schermata principale ho le prime 3 carte dell’utente. Se esegue lo swipe a destra vorrei vedere le altre carte (ovviamente se disponibili), sempre a 3 per view. Il mio problema è relativo al fatto che l’app è stata inizialmente progettata per un massimo di 3 carte, di conseguenza con un layout container con all’interno altri 3 layout(ognuno relativo ad una carta). Volendo ottenere ciò che ti ho descritto nelle righe precedenti, sarebbe possibile utilizzare la tua soluzione usando/ripetendo lo stesso layout(e quindi gli stessi id) per ogni view? O devo crearmi un layout con id diversi per ogni pagina?
    Spero di essermi spiegato chiaramente. Se hai bisogno di altri dettagli chiedi pure. Ti ringrazio. Ciao!!!

    1. Luca Zanini Avatar
      Luca Zanini

      Avendo lo stesso id, mi aspetto che anche il contenuto mostrato sia lo stesso, cioè stesso id stessa carta di credito.

      1. stefano Avatar

        In realtà no. Mi spiego meglio: ho modo di conoscere sin da subito il numero di carte totali che ha l’utente. La mia idea è che se mi trovo sulla view 1, mostro le carte con i dati relativi alle prime 3, se sono sulla view 2 mostro le carte(stessi id, ma dati diversi) con i dati delle carte dalla 4 alla 6, e cosi’ via..

        1. Luca Zanini Avatar
          Luca Zanini

          Durante lo swipe si vedono contemporaneamente anche se parzialmente il layout corrente e quello successivo, se ci sono View con lo stesso id mi aspetto che mostrino lo stesso contenuto ma questo non è il comportamento voluto.
          Quindi si dovrebbero usare più layout con id differenti; in alternativa è possibile usare il metodo setId della classe View per modificare l’id in runtime ma l’ho visto usato sempre in un contesto in cui tutto il layout era creato in runtime.

  5. Ciao, ho seguito il procedimento ma non riesco a capire come far si che all’avvio dell’app venga mostrato un tab diverso dal primo. Come si fa?

    1. Luca Zanini Avatar
      Luca Zanini

      Nella classe MainActivity alla fine del metodo onCreate aggiungi la riga

      mViewPager.setCurrentItem(1);
      

      per selezionare la seconda tab all’avvio.

  6. Cristian Avatar

    Ciao Luca,
    ho eseguito il tuo tutorial e, funziona tutto alla perfezione, ora però mi trovo davanti ad un quesito.
    Devo popolare le view dei fragment tramite valori presi da un db in maniera tale d’avere una sorta di catalogo che scorra con l’effetto swipe.
    Come posso eseguire un’operazione del genere?

    Grazie mille!

    1. Luca Zanini Avatar
      Luca Zanini

      Con effetto swipe credo si intenda il passare da un layout all’altro orizzontalmente, ma forse tu intendi voler scorrere il catalogo in senso verticale, allora potresti fare una ricerca per ListFragment.

  7. Thanks for this wonderful tutorial.
    I would like to know the flow of this activity, like which method is called at what event. i tried putting debug points but could not configure the flow. please help me.

    Thanks in advance

  8. Nice! I use this setup for my new app. Now I want to implement some communication
    betwenn the fragments. How is it possible to calculate a value in lets say fragment one
    and communicate this to fragment two and use it for an ongoing calculation there?
    Do you have some examples or tutorials or a general setup?
    Bye Tom

    1. Luca Zanini Avatar
      Luca Zanini

      You can use a variable member of the main activity.
      Let me guess that the value you want to pass between the fragments is a String called myString with setter and getter methods.
      You can access to myString in the fragments with the code:

      • MainActivity.getMyString();
        MainActivity.setMyString(…);
        if myString and getter and setter methods are static
      • ((MainActivity)getActivity()).getMyString();
        ((MainActivity)getActivity()).setMyString(…);
        if myString and getter and setter methods aren’t static

      You can also use the setArguments and getArguments to pass a Bundle object; in the example of this post I use a bundle to pass the position of the tabs (an int value).

      1. Hi Luca,
        thank you for you answer. Works very fine for me (…and it’s so simple)!

        Best regards

  9. Cormac DC Avatar

    You are Incredible!!!

    Thank you so much for taking the time to write this out!
    I have tried a total of 6 other similar tutorials but none actually explained clearly how to break down each tab into an xml file for easy editing.
    2 days searching until I came across this gem!
    Thanks again!

    Have a wonderful day! =)

  10. […] 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 […]

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.