In this tutorial, we’ll be implementing android nested ViewPager i.e a ViewPager within a ViewPager. Also, we’ll discuss and implement Vertical ViewPager too. We’ll be developing an application that replicates the swiping feature present in applications like Inshorts and Flipboard.
Android Nested ViewPager
If you’ve used the above-mentioned applications, you would have realized how easy it is to browse through different contents by just swiping up or down the current page.
The underlying view used to create such features is a ViewPager.
To brief up, ViewPagers use a PagerAdapter that adds pages (generally in the form of Fragment views) to our ViewPager.
By default, ViewPager
has a built-in swipe gesture to swipe left or right. There’s an interface named PageTransformer
that gets invoked while a scrolling on a ViewPager happens.
The PagerTransform
interface contains the following public method that can be overridden and implemented.
1 2 3 |
void transformPage (View page, float position) |
page
: The current view on which the transformation would be applied.position
: Position of the page relative to the current front-and-center position of the pager. 0 is front and center. 1 is one full page position to the right, and -1 is one-page position to the left.
Nested ViewPager is simply a ViewPager wrapped around another ViewPager.
What does this mean?
It means that we have a parent ViewPager in each page of a fragment. The child ViewPager would be hosted inside each of the above fragments.
In the following application, our parent View Pager would we a vertical swiping one. The child View Pager would be the default one that swipes/scrolls horizontally.
We’ll be developing an application that holds a list of Android Tutorials with their descriptions in a Vertical ViewPager. On swiping right, the tutorial would be displayed in a WebView.
Android Nested ViewPager Project
Create a new project in Android Studio and select the Tabbed Activity template from the below screen.
We will see a MainActivity.java class generated as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
package com.journaldev.swipeviewpagerinshorts; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; 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.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; 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}. */ private SectionsPagerAdapter mSectionsPagerAdapter; /** * The {@link ViewPager} that will host the section contents. */ private ViewPager mViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); // 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.container); mViewPager.setAdapter(mSectionsPagerAdapter); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }); } @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). return PlaceholderFragment.newInstance(position + 1); } @Override public int getCount() { // Show 3 total pages. return 3; } } /** * A placeholder fragment containing a simple view. */ public static 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); TextView textView = (TextView) rootView.findViewById(R.id.section_label); textView.setText(getString(R.string.section_format, getArguments().getInt(ARG_SECTION_NUMBER))); return rootView; } } } |
Alongside the activity, the class holds the Adapter and Fragment classes as well. This basic template displays a text on each of the pages in a ViewPager. Let’s look at the project structure before we begin with our implementation.
Android Nested ViewPager Project Structure
Add the Internet permission in the AndroidManifest.xml
file in the manifest tag.
1 2 3 |
<uses-permission android:name="android.permission.INTERNET"/> |
MainActivity
– That holds the parent ViewPager, namely theVerticalViewPager
. Invokes theParentViewPagerAdapter
that creates instances ofParentFragment
VerticalViewPager
– Contains a custom implementation of ViewPager that scrolls vertically. The pages hold aParentFragment
view which is supplied byParentViewPagerAdapter
.ParentFragment
– It’s present inside theVerticalViewPager
. It holds another ViewPager and it’s adapter code is in the fileChildViewPagerAdapter
. Fragment’s layout is defined infragment_parent.xml
.ChildViewPagerAdapter
– Contains the implementation of nested ViewPager. It holds and suppliesChildFragment
‘s to theParentFragment
.ChildFragment
– Contains the UI that you’ll see. Layout file :fragment_child.xml
DataModel
– Contains the data that’s supplied to theChildFragment
.
Android Nested ViewPager, Vertical ViewPager Code
The code for the VerticalViewPager.java
class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
package com.journaldev.swipeviewpagerinshorts; import android.content.Context; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; public class VerticalViewPager extends ViewPager { public VerticalViewPager(Context context) { super(context); init(); } public VerticalViewPager(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { setPageTransformer(true, new VerticalPageTransformer()); setOverScrollMode(OVER_SCROLL_NEVER); } private class VerticalPageTransformer implements ViewPager.PageTransformer { @Override public void transformPage(View view, float position) { if (position < -1) { // [-Infinity,-1) // This page is way off-screen to the left. view.setAlpha(0); } else if (position <= 1) { // [-1,1] view.setAlpha(1); // Counteract the default slide transition view.setTranslationX(view.getWidth() * -position); //set Y position to swipe in from top float yPosition = position * view.getHeight(); view.setTranslationY(yPosition); } else { // (1,+Infinity] // This page is way off-screen to the right. view.setAlpha(0); } } } /** * Swaps the X and Y coordinates of your touch event. */ private MotionEvent swapXY(MotionEvent ev) { float width = getWidth(); float height = getHeight(); float newX = (ev.getY() / height) * width; float newY = (ev.getX() / width) * height; ev.setLocation(newX, newY); return ev; } @Override public boolean onInterceptTouchEvent(MotionEvent ev){ boolean intercepted = super.onInterceptTouchEvent(swapXY(ev)); swapXY(ev); return intercepted; } @Override public boolean onTouchEvent(MotionEvent ev) { return super.onTouchEvent(swapXY(ev)); } } |
setOverScrollMode(OVER_SCROLL_NEVER)
is used to prevent over scrolling.
As we’d discussed before, we create a custom implementation of PageTransformer
class where instead of translating the view horizontally using translationX
, we do so vertically using translationY
. Same is done for motion events when the user swipes on the screen.
The code for DataModel.java
is given below.
1 2 3 4 5 6 7 8 9 10 11 |
package com.journaldev.swipeviewpagerinshorts; public class DataModel { public String title, description, url; public DataModel(String title, String description, String url) { this.title = title; this.description = description; this.url = url; } } |
The code of the activity_main.xml
layout file is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:id="@+id/main_content" android:layout_width="match_parent" android:layout_height="match_parent"> <com.journaldev.swipeviewpagerinshorts.VerticalViewPager android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimaryDark" /> </RelativeLayout> |
It hosts a VerticalViewPager
only, which will in turn host the Parent Fragment.
The code of the MainActivity.java class is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
package com.journaldev.swipeviewpagerinshorts; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; public class MainActivity extends AppCompatActivity implements ParentFragment.ToggleVerticalViewPagerScrolling { private ParentViewPagerAdapter verticalPagerAdapter; private VerticalViewPager verticalViewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayList<DataModel> dataModels = new ArrayList<>(); dataModels.add(new DataModel("Android Volley Tutorial", getString(R.string.volley_description), getString(R.string.volley_url))); dataModels.add(new DataModel("Android Dagger 2", getString(R.string.dagger_description), getString(R.string.dagger_url))); dataModels.add(new DataModel("Android Geocoder Reverse Geocoding", getString(R.string.geocoder_description), getString(R.string.geocoder_url))); dataModels.add(new DataModel("Android Notification Direct Reply", getString(R.string.notification_description), getString(R.string.notification_url))); dataModels.add(new DataModel("RecyclerView Android with Dividers and Contextual Toolbar", getString(R.string.recyclerview_description), getString(R.string.recyclerview_url))); verticalPagerAdapter = new ParentViewPagerAdapter(getSupportFragmentManager(), dataModels); verticalViewPager = findViewById(R.id.container); verticalViewPager.setAdapter(verticalPagerAdapter); } @Override public void trigger(int page) { if (page == 1) { verticalViewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return true; } }); } else { verticalViewPager.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return false; } }); } } } |
Things to note:
- The VerticalViewPager’s adapter, namely
ParentViewPagerAdapter
is loaded with the data. - The
trigger
method is overridden from an interface defined in the ParentFragment class. It’s invoked by theParentFragment
whenever a page is changed. Its goal is to disable VerticalViewPager from scrolling/swiping when the Nested ViewPager is showing the second page(since the second page holds a WebView) that we’ll be seeing shortly.
Note: To avoid long strings in the class, we’ve defined them in the strings.xml
resources file in our project:
Code for the ParentViewPagerAdapter.java
class is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.journaldev.swipeviewpagerinshorts; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import java.util.ArrayList; public class ParentViewPagerAdapter extends FragmentPagerAdapter { ArrayList<DataModel> dataModels = new ArrayList<>(); public ParentViewPagerAdapter(FragmentManager fm, ArrayList<DataModel> dataModels) { super(fm); this.dataModels = dataModels; } @Override public Fragment getItem(int position) { return ParentFragment.newInstance(dataModels.get(position)); } @Override public int getCount() { return 5; } } |
The adapter creates a new ParentFragment
class for each page. In total 5 pages for each of the DataModel
elements.
The layout for the ParentFragment class is defined in fragment_parent.xml
file as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 |
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:id="@+id/rl" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorPrimaryDark"> <android.support.v4.view.ViewPager android:id="@+id/nestedViewPager" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout> |
The ParentFragment class holds the Nested ViewPager.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package com.journaldev.swipeviewpagerinshorts; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class ParentFragment extends Fragment { ViewPager nestedViewPager; Activity mActivity; ToggleVerticalViewPagerScrolling tv; public ParentFragment() { } public static ParentFragment newInstance(DataModel dataModel) { ParentFragment fragment = new ParentFragment(); Bundle args = new Bundle(); args.putString("title", dataModel.title); args.putString("description", dataModel.description); args.putString("url", dataModel.url); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_parent, container, false); String title = getArguments().getString("title"); String description = getArguments().getString("description"); String url = getArguments().getString("url"); DataModel model = new DataModel(title, description, url); nestedViewPager = rootView.findViewById(R.id.nestedViewPager); nestedViewPager.setAdapter(new ChildViewPagerAdapter(getChildFragmentManager(), model)); nestedViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { tv.trigger(position); } @Override public void onPageSelected(int position) { Log.d("API123", "onPageSelected " + position); } @Override public void onPageScrollStateChanged(int state) { } }); return rootView; } public interface ToggleVerticalViewPagerScrolling { void trigger(int page); } @Override public void onAttach(Context context) { super.onAttach(context); if (context instanceof Activity) { mActivity = (Activity) context; } try { tv = (ToggleVerticalViewPagerScrolling) mActivity; } catch (ClassCastException e) { throw new ClassCastException("Error in retrieving data. Please try again"); } } } |
Things to note:
- Each
ParentFragment.java
class holds a Nested ViewPager. - The
ChildViewPagerAdapter
creates a nested fragment (ChildFragment) instance for each page. - The Nested ViewPager would hold two pages(ChildFragment instances) only. The first would contain the title and description of the tutorial. The second page would show the tutorial in a WebView.
addOnPageChangeListener
callback is used to determine the current page index.interface ToggleVerticalViewPagerScrolling
contains the methodtrigger
. The trigger method passes the relevant page index to theMainActivity
. Read communicating data from fragments- If the page index is 2 i.e. the WebView UI, the VerticalViewPager swiping is disabled.
The code for the ChildViewPagerAdapter.java class is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.journaldev.swipeviewpagerinshorts; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; public class ChildViewPagerAdapter extends FragmentPagerAdapter { DataModel model; public ChildViewPagerAdapter(FragmentManager fm, DataModel model) { super(fm); this.model = model; } @Override public int getCount() { return 2; } @Override public Fragment getItem(int position) { switch (position) { case 0: return ChildFragment.newInstance(model, false); case 1: return ChildFragment.newInstance(model, true); default: return ChildFragment.newInstance(model, true); } } @Override public CharSequence getPageTitle(int position) { return "Child Fragment " + position; } } |
The code for the layout fragment_child.xml is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android" xmlns:app="https://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="@dimen/activity_vertical_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginTop="@dimen/activity_vertical_margin" app:cardCornerRadius="8dp" app:cardElevation="8dp" app:cardPreventCornerOverlap="true" app:cardUseCompatPadding="true"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /> <RelativeLayout android:id="@+id/rl" android:layout_width="match_parent" android:layout_height="200dp" android:background="#262626"> <TextView android:id="@+id/txtTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:textColor="#FFF" android:textSize="26sp" /> </RelativeLayout> <TextView android:id="@+id/txtDescription" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/rl" android:layout_centerHorizontal="true" android:layout_marginBottom="@dimen/activity_vertical_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginStart="@dimen/activity_horizontal_margin" android:layout_marginTop="@dimen/activity_vertical_margin" android:textSize="18sp" /> <Button android:id="@+id/button" android:background="#801f2124" android:text="TAP/SLIDE RIGHT TO VIEW TUTORIAL" android:textColor="#FFF" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" /> </RelativeLayout> </android.support.v7.widget.CardView> |
The code for the ChildFragment.java class is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
package com.journaldev.swipeviewpagerinshorts; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.webkit.WebView; import android.widget.Button; import android.widget.RelativeLayout; import android.widget.TextView; public class ChildFragment extends Fragment { public ChildFragment() { } public static ChildFragment newInstance(DataModel dataModel, boolean isWebView) { ChildFragment fragment = new ChildFragment(); Bundle args = new Bundle(); args.putString("title", dataModel.title); args.putString("description", dataModel.description); args.putString("url", dataModel.url); args.putBoolean("isWebView", isWebView); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_child, container, false); RelativeLayout rl = rootView.findViewById(R.id.rl); WebView webView = rootView.findViewById(R.id.webView); TextView txtTitle = rootView.findViewById(R.id.txtTitle); Button button = rootView.findViewById(R.id.button); TextView txtDescription = rootView.findViewById(R.id.txtDescription); boolean isWebView = getArguments().getBoolean("isWebView"); if (isWebView) { webView.setVisibility(View.VISIBLE); rl.setVisibility(View.GONE); button.setVisibility(View.GONE); txtDescription.setVisibility(View.GONE); webView.loadUrl(getArguments().getString("url")); } else { webView.setVisibility(View.GONE); rl.setVisibility(View.VISIBLE); txtDescription.setVisibility(View.VISIBLE); button.setVisibility(View.VISIBLE); txtTitle.setText(getArguments().getString("title")); txtDescription.setText(getArguments().getString("description")); } button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ParentFragment parentFrag = ((ParentFragment) ChildFragment.this.getParentFragment()); parentFrag.nestedViewPager.setCurrentItem(1); } }); return rootView; } } |
The isWebView
parameter from newInstance
method determines whether the WebView
is shown/hidden. Button click would directly take the user to the WebView page.
Changing ViewPager page dynamically.
To change the ViewPager page we call the setCurrentItem()
method on the ViewPager instance.
Don’t forget to add the CardView dependency to the build.gradle
.
Android Vertical ViewPager App Output
The output of the above application in action.
Adding Animation to Swipe Effect
Let’s animate the VerticalViewPager scrolling by creating a different PageTransformer class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
private class VerticalPageTransformerAnimate implements ViewPager.PageTransformer { private static final float MIN_SCALE = 0.75f; @Override public void transformPage(View view, float position) { int pageWidth = view.getWidth(); int pageHeight = view.getHeight(); float alpha = 0; if (0 <= position && position <= 1) { alpha = 1 - position; } else if (-1 < position && position < 0) { float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); float verticalMargin = pageHeight * (1 - scaleFactor) / 2; float horizontalMargin = pageWidth * (1 - scaleFactor) / 2; if (position < 0) { view.setTranslationX(horizontalMargin - verticalMargin / 2); } else { view.setTranslationX(-horizontalMargin + verticalMargin / 2); } view.setScaleX(scaleFactor); view.setScaleY(scaleFactor); alpha = position + 1; } view.setAlpha(alpha); view.setTranslationX(view.getWidth() * -position); float yPosition = position * view.getHeight(); view.setTranslationY(yPosition); } } |
Initialise in the init
method of the VerticalViewPager class in the following way.
1 2 3 |
setPageTransformer(true, new VerticalPageTransformerAnimate()); |
The new output of the application is:
This brings an end to android vertical ViewPager tutorial. Since the ViewPagers are swipeable in all four directions we’ve named the project as SwipeViewPagerInshorts. You can download it from the link below.