Android Application Post Installation Wizard Using ViewPager
In many applications we download from Google Play Store, they show user wizard sort of walkthrough or asking user to accept license and signup etc for the first time after installation. User is shown current page on screen with strip of circled at bottom highlighted for current page. We will create an Android Application Post Installation Wizard Using ViewPager and add functionality of page indicator using circle strip at the bottom.
android.support.v4.view.ViewPager allows the user to flip left and right through pages of data. We need to set adapter for views to be shown as pages. We will use FragmentPagerAdapter for our ViewPager which will have Fragment as its data item. As soon as Page of ViewPager is changed, we will update page indicator.
Sample Android Application with ViewPagerIndicator
In this tutorial, I have not covered sign in with Facebook and Google+. If interested, you would like to check our posts for social login integration.
Facebook login in Android apps – Using latest SDK
Integrating Google Plus Sign In into your Android Application
1. Create a new Android Application Project with com.androidsrc.viewpagerindicator as package name.
2. Preparing Application Manifest File
I have set theme as no title bar and fullscreen for Wizard. If you don’t want title war to appear, set android:theme attribute for your wizard activity as @android:style/Theme.NoTitleBar.Fullscreen.
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 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.androidsrc.viewpagerindicator" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" 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> |
3. Create shape drawable for background of Page Indicator
Create a new file /res/drawble/indicator_bg.xml. Define shape as oval with solid white color.
1 2 3 4 5 6 |
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" > <solid android:color="@android:color/white"/> </shape> |
Also Read: Fastest Android Launchers
4. Preparing Layout Files
Our main activity layout will have FrameLayout as parent view so that we can show Page Indicators on top of ViewPager. In my sample, I have used four pages in ViewPager. For this I have used four different View’s wrapped in LinearLayout with horizontal orientation as parent.
/res/layout/activity_main.xml will be as below after including these views.
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 |
<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.androidsrc.viewpagerindicator.MainActivity" > <android.support.v4.view.ViewPager android:id="@+id/viewPager" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <LinearLayout android:layout_margin="10dp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="bottom" android:orientation="horizontal" > <View android:id="@+id/indicator1" android:layout_width="15dp" android:layout_height="15dp" android:layout_margin="2dp" android:background="@drawable/indicator_bg" /> <View android:id="@+id/indicator2" android:layout_width="15dp" android:layout_height="15dp" android:layout_margin="2dp" android:background="@drawable/indicator_bg" /> <View android:id="@+id/indicator3" android:layout_width="15dp" android:layout_height="15dp" android:layout_margin="2dp" android:background="@drawable/indicator_bg" /> <View android:id="@+id/indicator4" android:layout_width="15dp" android:layout_height="15dp" android:layout_margin="2dp" android:background="@drawable/indicator_bg" /> </LinearLayout> </FrameLayout> |
Layout for Page 1 incorporates ImageView and TextView. Create file /res/layout/page1.xml.
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 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#008000" android:gravity="center" > <ImageView android:id="@+id/icon_androidsrc" android:layout_width="200dp" android:layout_height="200dp" android:src="@drawable/camera" /> <TextView style="?android:attr/textAppearanceLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/icon_androidsrc" android:layout_margin="20dp" android:text="Camera Plus" android:textColor="@android:color/white" android:textSize="30dp" android:textStyle="bold" /> </RelativeLayout> |
Similarly create layout files for other three pages as per your design requirements. I have named them as page2.xml, page3.xml and page4.xml. If you want to keep same, you can get source for these layouts by downloading complete application source code above.
5. Preparing Source Files
Create a new class WizardFragment.java which will extend Fragment. We will pass page number to constructor in order to inflate corresponding layout in onCreateView().
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 |
package com.androidsrc.viewpagerindicator; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class WizardFragment extends Fragment { int wizard_page_position; public WizardFragment(int position) { this.wizard_page_position = position; } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { int layout_id = R.layout.page1; switch (wizard_page_position) { case 0: layout_id = R.layout.page1; break; case 1: layout_id = R.layout.page2; break; case 2: layout_id = R.layout.page3; break; case 3: layout_id = R.layout.page4; break; } return inflater.inflate(layout_id, container, false); } } |
Your MainActivity.java should extend FragmentActivity in order to use getSupportFragmentManager() from Support Library. Define a local class ViewPageAdapter which extends FragmentPagerAdapter. It will return new instance of WizardFragment for that item position.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
private class ViewPagerAdapter extends FragmentPagerAdapter { private int WIZARD_PAGES_COUNT = 4; public ViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new WizardFragment(position); } @Override public int getCount() { return WIZARD_PAGES_COUNT; } } |
Define a local class WizardPageChangeListener which will implement OnPageChangeListener. From onPageSelected, we will update page indicators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private class WizardPageChangeListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int position) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // TODO Auto-generated method stub } @Override public void onPageSelected(int position) { updateIndicators(position); } } |
override onBackPressed() to handle back key events for navigating back to previous pages in ViewPager.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@Override public void onBackPressed() { if (viewPager.getCurrentItem() == 0) { // If the user is currently looking at the first step, allow the // system to handle the // Back button. This calls finish() on this activity and pops the // back stack. super.onBackPressed(); } else { // Otherwise, select the previous step. viewPager.setCurrentItem(viewPager.getCurrentItem() - 1); } } |
For updating Page Indicator Views, update on change of page. Set current page indicator width and height relatively larger than other views.
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 |
public void updateIndicators(int position) { DisplayMetrics metrics = getResources().getDisplayMetrics(); int resizeValue = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 25, metrics); int defaultValue = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 15, metrics); switch (position) { case 0: indicator1.getLayoutParams().height = resizeValue; indicator1.getLayoutParams().width = resizeValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 1: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = resizeValue; indicator2.getLayoutParams().width = resizeValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 2: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = resizeValue; indicator3.getLayoutParams().width = resizeValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 3: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = resizeValue; indicator4.getLayoutParams().width = resizeValue; indicator4.requestLayout(); break; } } |
Final complete code for MainActivity.java after incorporating above changes will be as 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 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
package com.androidsrc.viewpagerindicator; 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.support.v4.view.ViewPager.OnPageChangeListener; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.View; public class MainActivity extends FragmentActivity { private ViewPager viewPager; private View indicator1; private View indicator2; private View indicator3; private View indicator4; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); indicator1 = (View) findViewById(R.id.indicator1); indicator2 = (View) findViewById(R.id.indicator2); indicator3 = (View) findViewById(R.id.indicator3); indicator4 = (View) findViewById(R.id.indicator4); viewPager = (ViewPager) findViewById(R.id.viewPager); viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager())); viewPager.setOnPageChangeListener(new WizardPageChangeListener()); updateIndicators(0); } @Override public void onBackPressed() { if (viewPager.getCurrentItem() == 0) { // If the user is currently looking at the first step, allow the // system to handle the // Back button. This calls finish() on this activity and pops the // back stack. super.onBackPressed(); } else { // Otherwise, select the previous step. viewPager.setCurrentItem(viewPager.getCurrentItem() - 1); } } private class ViewPagerAdapter extends FragmentPagerAdapter { private int WIZARD_PAGES_COUNT = 4; public ViewPagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return new WizardFragment(position); } @Override public int getCount() { return WIZARD_PAGES_COUNT; } } private class WizardPageChangeListener implements OnPageChangeListener { @Override public void onPageScrollStateChanged(int position) { // TODO Auto-generated method stub } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // TODO Auto-generated method stub } @Override public void onPageSelected(int position) { updateIndicators(position); } } public void updateIndicators(int position) { DisplayMetrics metrics = getResources().getDisplayMetrics(); int resizeValue = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 25, metrics); int defaultValue = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 15, metrics); switch (position) { case 0: indicator1.getLayoutParams().height = resizeValue; indicator1.getLayoutParams().width = resizeValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 1: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = resizeValue; indicator2.getLayoutParams().width = resizeValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 2: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = resizeValue; indicator3.getLayoutParams().width = resizeValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = defaultValue; indicator4.getLayoutParams().width = defaultValue; indicator4.requestLayout(); break; case 3: indicator1.getLayoutParams().height = defaultValue; indicator1.getLayoutParams().width = defaultValue; indicator1.requestLayout(); indicator2.getLayoutParams().height = defaultValue; indicator2.getLayoutParams().width = defaultValue; indicator2.requestLayout(); indicator3.getLayoutParams().height = defaultValue; indicator3.getLayoutParams().width = defaultValue; indicator3.requestLayout(); indicator4.getLayoutParams().height = resizeValue; indicator4.getLayoutParams().width = resizeValue; indicator4.requestLayout(); break; } } } |