Kiory Stores

Creating an Android app using Tabs with Swipe Views

INICIO / Programación Android Creating an Android app using Tabs with Swipe Views

In this tutorial we are going to learn how to create an Android app using Tabs with Swipe Views so users can navigate horizontally between different fragments or screens. In this example we are going to create an app with three screens.

 

Captura de pantalla 2015-08-26 a las 17.33.17

 

 

CREATE NEW PROJECT

 

First of all we have to create a new Android project following this steps, in this case, we have used a Tabbed Activity because it will start our project with a ViewPager element on out Main Activity.

 

Captura de pantalla 2015-08-26 a las 9.27.20 Captura de pantalla 2015-08-26 a las 9.29.59 Captura de pantalla 2015-08-26 a las 9.32.19 Captura de pantalla 2015-08-26 a las 9.32.32

 

 

ADDING MATERIAL DESIGN TO OUR PROJECT

 

Next step is to apply Material Design to our application. We will need to add a color resource file in order to create new colors to apply in our theme.
Create a colors.xml resource file by right clicking in the values folder and selecting New / Values resource file.

 

Captura de pantalla 2015-08-26 a las 9.38.55

 

 

Copy in your new file the following code:

[xml]

<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="primary">#673AB7</color>
<color name="primary_dark">#512DA8</color>
<color name="accent">#8BC34A</color>
</resources>

[/xml]

 

Now we need to create two different styles.xml, one will be used on devices with api below 21 (before Lollipop), and the other one will be used in devices over api 21.
Create a values folder for devices with api over 21 right clicking the res folder and selecting New / Directory.

Captura de pantalla 2015-08-26 a las 9.56.14

 

Inside this folder create a styles.xml file and copy this styles. This styles will be used by devices with Android 5.0 or later.

 

[xml]

<resources>

<!– Base application theme. –>
<style name="AppTheme" parent="AppTheme.Base">
<!– Main theme colors –>
<!– your app branding color for the app bar –>
<item name="android:colorPrimary">@color/primary</item>
<!– darker variant for the status bar and contextual app bars –>
<item name="android:colorPrimaryDark">@color/primary_dark</item>
<!– theme UI controls like checkboxes and text fields –>
<item name="android:colorAccent">@color/accent</item>
</style>

</resources>
[/xml]

 

Now we have to edit the default styles.xml to add a Material Design theme compatible with devices below Android 5.0.

 

[xml]

<resources>

<!– Base application theme. –>
<style name="AppTheme" parent="AppTheme.Base">
</style>

<style name="AppTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<!– your app branding color for the app bar –>
<item name="colorPrimary">@color/primary</item>
<!– darker variant for the status bar and contextual app bars –>
<item name="colorPrimaryDark">@color/primary_dark</item>
<!– theme UI controls like checkboxes and text fields –>
<item name="colorAccent">@color/accent</item>
</style>
</resources>

[/xml]

 

Now your app should look like this.

 

Captura de pantalla 2015-08-26 a las 11.02.21

 

 

CREATE FRAGMENTS FOR OUR VIEWPAGER

 

No we are going to move the PlaceholderFragment class from the MainActivity.java file to it’s own java file so we can work with cleaner code. Create a new file called PlaceholderFragment.java and move the PlaceholderFragment class to it. This will be our first screen.

Remember to delete the PlaceholderFragment class from the MainActivity class.

 

[java]

package com.app.example.swapappexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class PlaceholderFragment extends Fragment {
/**
* The fragment argument representing the section number for this
* fragment.
*/
private static final String ARG_SECTION_NUMBER = "section_number";

/**
* Returns a new instance of this fragment for the given section
* number.
*/
public static PlaceholderFragment newInstance(int sectionNumber) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}

public PlaceholderFragment() {
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
return rootView;
}
}

[/java]

 

 

Let’s create the other two fragment screens for our example application. Add this Tab2 xml and java files.

tab2.xml

[xml]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/btn_star"/>

</LinearLayout>
[/xml]

Tab2.java

[java]

package com.app.example.swapappexample;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab2 extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";

public static Tab2 newInstance(int sectionNumber) {
Tab2 fragment = new Tab2();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab2,container,false);
return v;
}
}

[/java]

 

Add this Tab3 xml and java files.

 

tab3.xml

[xml]

<RelativeLayout 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" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin">

<TextView android:id="@+id/section_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tab3_textview"/>

</RelativeLayout>

[/xml]

Tab3.java

[java]

package com.app.example.swapappexample;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class Tab3 extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";

public static Tab3 newInstance(int sectionNumber) {
Tab3 fragment = new Tab3();
Bundle args = new Bundle();
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
fragment.setArguments(args);
return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab3,container,false);
return v;
}
}

[/java]

We want an application with swipe screens and tabs, in order to make it work with tabs, we have to add a new dependency on our build.gradle file:

 

[xml]

compile ‘com.android.support:design:22.2.1’

[/xml]

Captura de pantalla 2015-09-02 a las 11.15.43 Captura de pantalla 2015-09-02 a las 11.16.19

 

ADD TAB BAR NAVIGATION

 

Now we are going to modify our MainActivity’s layout to include our navigation toolbar, update the activity_main.xml with the following:

 

[xml]

<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<android.support.design.widget.TabLayout
android:id="@+id/tabbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />

<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="@android:color/white" />

</LinearLayout>

[/xml]

 

We add android.support.design.widget.TabLayout here which will provide the horizontal layout to display the different tabs. The android.support.v4.view.ViewPager component creates the “pages” under the tabs which the user can navigate through by swiping left or right. Each page will contain a fragment or screen which we will create later in the tutorial.

 

android-swap-views-example-kiory

 

ATTACH FRAGMENTS TO OUR VIEWPAGER

 

The last step is adding some code to our MainActivity.java file. Because we created the project with Android Studio’s template TabbedActivity, we were given some for default code to create the ViewPager. We will now modify this code to implement the fragments that we created earlier (i.e. PlaceholderFragment.java, Tab2.java, Tab3.java).

In our MainActivity.java file, we were provided a SectionsPageAdapter class which looks like the code below. Notice in the getItem method we will return a different fragment screen depending on the ViewPager position, and the same in the getPageTitle method, which will return the title for the page that is selected if one exists.

 

[java]

public class SectionsPagerAdapter extends FragmentPagerAdapter {

public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position) {
case 0:
return PlaceholderFragment.newInstance(position + 1);
case 1:
return Tab2.newInstance(position + 1);
case 2:
return Tab3.newInstance(position + 1);
}
return null;
}

@Override
public int getCount() {
// Show 3 total pages.
return 3;
}

@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}

[/java]

 

 

BINDING VIEWPAGER AND TAB BAR NAVIGATION

 

Now we have to instance the ViewPager in the MainActivity, this will be done on the onCreate method. Previously we added a TabLayout widget to our activity_main.xml layout, so we will now add the tab bar navigation and set it up with our ViewPager:

 //Add a tab bar navigation
 TabLayout tabLayout = (TabLayout) findViewById(R.id.tabbar);
 tabLayout.setupWithViewPager(mViewPager);

 

Our onCreate method will look like this.

 

[java]

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

// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);

//Add a tab bar navigation
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabbar);
tabLayout.setupWithViewPager(mViewPager);
}

[/java]

 

Finally our MainActivity.java will look like this.

 

[java]

package com.app.example.swapappexample;

import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;

import java.util.Locale;

public class MainActivity extends AppCompatActivity {

/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link 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}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;

/**
* The {@link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;

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

// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());

// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);

//Add a tab bar navigation
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabbar);
tabLayout.setupWithViewPager(mViewPager);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {

public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}

@Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
switch (position) {
case 0:
return PlaceholderFragment.newInstance(position + 1);
case 1:
return Tab2.newInstance(position + 1);
case 2:
return Tab3.newInstance(position + 1);
}
return null;
}

@Override
public int getCount() {
// Show 3 total pages.
return 3;
}

@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.title_section1).toUpperCase(l);
case 1:
return getString(R.string.title_section2).toUpperCase(l);
case 2:
return getString(R.string.title_section3).toUpperCase(l);
}
return null;
}
}

}

[/java]

 

 

We’ve made a lot of modification to the code to create an app with Tabs and SwipeViews! Here’s a recap of how all the different components fit together and roughly when they are called: 
 
What happens when the app is first created
  • MainActivity’s onCreate method gets called
    • SectionsPagerAdapter’s constructor gets called to create the adapter which will attach our fragments to our pages so data can be displayed
    • Get a handle on mViewPager, which is the white space underneath the tabs, by using findViewById(R.id.pager)
    • On this mViewPager, we set the adapter to be SectionsPagerAdapter which in turn (loosely) does the following
      • SectionsPagerAdapter.getItem and SectionsPagerAdapter.getPageTitle get called with position equal to 0
      • PlaceHolderFragment gets initiated
      • PlaceHolderFragment.onCreateView gets called which inflates R.layout.fragment_main
      • We see “Hello World!”
    • Get a handle on TabLayout, which is the horizontal layout of tabs using findViewById(R.id.tabbar)
    • On this tabLayout, we set the setupWithViewPager
      • This binds the tabs to the associated page below
What happens when you click Tab 3
  • The SectionsPagerAdapter is notified that position 2 has been selected (position numbers start at 0)
    • SectionsPagerAdapter.getItem and SectionsPagerAdapter.getPageTitle get called with position equal to 2
    • a new instance of the Tab3 class is created
    • Tab3.onCreateView gets called which inflates R.layout.tab3
    • We see “Hello blank fragment”

 

DOWNLOAD EXAMPLE PROJECT

Captura de pantalla 2015-08-26 a las 17.33.17


5 Comments so far:

  1. Halil dice:

    case 1:
    return Tab2.newInstance(position + 1);
    case 2:
    return Tab3.newInstance(position + 1);
    }
    When I come this line It gives me an error like this : android.support.v4.app.Fragment Required.How can I solve this problem? There is no error on case1.Only 2 and 3

  2. Noemi dice:

    Hi Halil!

    In your Tab2 and Tab3 are you using support fragment?

    You can import support fragment:
    import android.support.v4.app.Fragment;

    Or you can extend your class directly from this support fragment class:
    public class Tab3 extends android.support.v4.app.Fragment {

    Hope this makes it work!

  3. Avneet dice:

    I am having problem in the manifest. It says URI not registered

  4. Niranjan dice:

    I am working with custom adapter inside tabs how to implement it ,

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *