How to use ViewPager to create a Tutorial screen with Parallax effect
Source: ParallaxPagerTransformer
Question: How to use ViewPager to create a Tutorial screen with Parallax effect?
Answer:
Set up ParallaxPagerTransformer.
Create TutorialActivity
:
public class TutorialActivity extends FragmentActivity {
private ViewPager mPager;
private TutorialParallaxAdapter mAdapter;
private CircleIndicatorPager mIndicator;
private Button mButton;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//openOnce(); //todo uncomment this
setContentView(R.layout.my_tutorial_activity_parallax);
mPager = (ViewPager) findViewById(R.id.pager);
mPager.setBackgroundColor(0xFF000000);
ParallaxPagerTransformer pt = new ParallaxPagerTransformer((R.id.tutorial_image));
pt.setBorder(20);
pt.setSpeed(0.8f);
mPager.setPageTransformer(false, pt);
mAdapter = new TutorialParallaxAdapter(getSupportFragmentManager());
mAdapter.setPager(mPager); //only for this transformer
Bundle page1 = new Bundle();
page1.putInt("image", R.drawable.page1_background);
page1.putInt("imageLogo", R.drawable.page1_logo);
page1.putInt("imageText", R.drawable.page1_text);
TutorialParallaxFragment page1Fragment = new TutorialParallaxFragment();
page1Fragment.setArguments(page1);
Bundle page2 = new Bundle();
page2.putInt("image", R.drawable.page2_background);
page2.putInt("imageLogo", R.drawable.page2_logo);
page2.putInt("imageText", R.drawable.page2_text);
TutorialParallaxFragment page2Fragment = new TutorialParallaxFragment();
page2Fragment.setArguments(page2);
Bundle page3 = new Bundle();
page3.putInt("image", R.drawable.page3_background);
page3.putInt("imageLogo", R.drawable.page3_logo);
page3.putInt("imageText", R.drawable.page3_text);
TutorialParallaxFragment page3Fragment = new TutorialParallaxFragment();
page3Fragment.setArguments(page3);
Bundle page4 = new Bundle();
page4.putInt("image", R.drawable.page4_background);
page4.putInt("imageLogo", R.drawable.page1_logo);
page4.putInt("imageText", R.drawable.page4_text);
TutorialParallaxFragment page4Fragment = new TutorialParallaxFragment();
page4Fragment.setArguments(page4);
Bundle page5 = new Bundle();
page5.putInt("image", R.drawable.page5_background);
page5.putInt("imageLogo", R.drawable.page5_logo);
page5.putInt("imageText", R.drawable.page5_text1);
TutorialParallaxFragment page5Fragment = new TutorialParallaxFragment();
page5Fragment.setArguments(page5);
mAdapter.add(page1Fragment);
mAdapter.add(page2Fragment);
mAdapter.add(page3Fragment);
mAdapter.add(page4Fragment);
mAdapter.add(page5Fragment);
mPager.setAdapter(mAdapter);
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().show();
}
// setup indicator
mIndicator = (CircleIndicatorPager) findViewById(R.id.indicator);
mIndicator.setRadius(10);
mIndicator.setViewPager(mPager);
//setup get started button
mButton = (Button)findViewById(R.id.getStarted_button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (getToken(TutorialActivity.this)) {
Intent intent = new Intent(TutorialActivity.this, HomeActivity.class);
startActivity(intent);
} else {
Intent intent = new Intent(TutorialActivity.this, LoginActivity.class);
startActivity(intent);
}
finish();
}
});
}
private void openOnce(){
// prevents Tutorial from opening after first log-in
SharedPreferences pref = getSharedPreferences("ActivityPREF", Context.MODE_PRIVATE);
if(pref.getBoolean("activity_executed", false)){
Intent intent = new Intent(this, HomeActivity.class);
startActivity(intent);
finish();
} else {
SharedPreferences.Editor ed = pref.edit();
ed.putBoolean("activity_executed", true);
ed.commit();
}
}
private static boolean getToken(Context context) {
SharedPreferences sharedPrefs = context.getSharedPreferences(IS_LOGGED, Context.MODE_PRIVATE);
return sharedPrefs.getBoolean("isLogged", false);
}
}
Then my_tutorial_activity_parallax.xml
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<co.shutta.shuttapro.widgets.CircleIndicatorPager
android:id="@+id/indicator"
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="72dp"
android:padding="5dp" />
<Button
android:id="@+id/getStarted_button"
android:layout_width="150dp"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:background="@drawable/login_btn"
android:text="Get Started"
android:textColor="#ffffff" />
</RelativeLayout>
In login_btn.xml
:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:width="3dp"
android:color="#4c7eed" />
<corners android:radius="5dp" />
<gradient android:angle="270"
android:centerColor="#4c7eed"
android:endColor="#4c7eed"
android:startColor="#4c7eed" />
</shape>
Then TutorialParallaxFragment
:
public class TutorialParallaxFragment extends Fragment {
private TutorialParallaxAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View v = inflater.inflate(R.layout.my_tutorial_parallax_fragment, container, false);
final ImageView image = (ImageView) v.findViewById(R.id.tutorial_image);
image.setImageResource(getArguments().getInt("image"));
image.post(new Runnable() {
@Override
public void run() {
Matrix matrix = new Matrix();
matrix.reset();
float wv = image.getWidth();
float hv = image.getHeight();
float wi = image.getDrawable().getIntrinsicWidth();
float hi = image.getDrawable().getIntrinsicHeight();
float width = wv;
float height = hv;
if (wi / wv > hi / hv) {
matrix.setScale(hv / hi, hv / hi);
width = wi * hv / hi;
} else {
matrix.setScale(wv / wi, wv / wi);
height= hi * wv / wi;
}
matrix.preTranslate((wv - width) / 2, (hv - height) / 2);
image.setScaleType(ImageView.ScaleType.MATRIX);
image.setImageMatrix(matrix);
}
});
ImageView logo = (ImageView) v.findViewById(R.id.tutorial_logo);
logo.setImageResource(getArguments().getInt("imageLogo"));
ImageView imageText = (ImageView) v.findViewById(R.id.tutorial_text);
imageText.setImageResource(getArguments().getInt("imageText"));
return v;
}
public void setAdapter(TutorialParallaxAdapter adapter) {
mAdapter = adapter;
}
}
Then my_tutorial_parallax_fragment.xml
:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/parallaxContent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/tutorial_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="center"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"/>
<ImageView
android:id="@+id/tutorial_logo"
android:layout_width="250dp"
android:layout_height="250dp"
android:paddingTop="50dp"
android:layout_centerHorizontal="true" />
<ImageView
android:id="@+id/tutorial_text"
android:layout_width="250dp"
android:layout_height="250dp"
android:layout_centerInParent="true"
android:layout_below="@id/tutorial_logo"/>
</RelativeLayout>
Then TutorialParallaxAdapter
:
public class TutorialParallaxAdapter extends FragmentStatePagerAdapter {
private ArrayList<TutorialParallaxFragment> mFragments;
private ViewPager mPager;
public TutorialParallaxAdapter(FragmentManager fm) {
super(fm);
mFragments = new ArrayList<>();
}
@Override
public Fragment getItem(int i) {
return mFragments.get(i);
}
@Override
public int getCount() {
return mFragments.size();
}
public void add(TutorialParallaxFragment parallaxFragment) {
parallaxFragment.setAdapter(this);
mFragments.add(parallaxFragment);
notifyDataSetChanged();
mPager.setCurrentItem(getCount() - 1, true);
}
public void remove(int i) {
mFragments.remove(i);
notifyDataSetChanged();
}
public void remove(TutorialParallaxFragment parallaxFragment) {
mFragments.remove(parallaxFragment);
int pos = mPager.getCurrentItem();
notifyDataSetChanged();
mPager.setAdapter(this);
if (pos >= this.getCount()) {
pos = this.getCount() - 1;
}
mPager.setCurrentItem(pos, true);
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public void setPager(ViewPager pager) {
mPager = pager;
}
}
Finally CircleIndicatorPager
:
public class CircleIndicatorPager extends View implements ViewPager.OnPageChangeListener {
private int mCurrentPage;
private float mPageOffset;
private ViewPager mViewPager;
private Paint mPaintFill;
private Paint mPaintCur;
public float getRadius() {
return mRadius;
}
public void setRadius(float mRadius) {
this.mRadius = mRadius;
}
private float mRadius;
public CircleIndicatorPager(Context context) {
super(context);
init(context);
}
public CircleIndicatorPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CircleIndicatorPager(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context ctx) {
// Paint default
mPaintFill = new Paint();
setupPaint(mPaintFill, ctx.getResources().getColor(R.color.white_80));
// Paint index
mPaintCur = new Paint();
setupPaint(mPaintCur, ctx.getResources().getColor(R.color.blue));
}
private void setupPaint(Paint paint, int color) {
paint.setColor(color);
paint.setAntiAlias(true);
paint.setStrokeWidth(mRadius);
paint.setStyle(Paint.Style.FILL);
}
public void setViewPager(ViewPager view) {
if (mViewPager == view) {
return;
}
if (mViewPager != null) {
mViewPager.setOnPageChangeListener(null);
}
if (view.getAdapter() == null) {
throw new IllegalStateException("ViewPager does not have adapter instance.");
}
mViewPager = view;
mViewPager.setOnPageChangeListener(this);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Update background indicator
drawBackgroundCircle(canvas);
// Update current indicator
drawCurrentPageCircle(canvas);
}
private void drawBackgroundCircle(Canvas canvas) {
final int pageNum = mViewPager.getAdapter().getCount();
if (pageNum == 0) {
return;
}
int centerX = canvas.getWidth() / 2;
int centerY = canvas.getHeight() / 2;
float indicatorWidth = pageNum * (6 * mRadius) - (4 * mRadius);
float firstCirX = centerX - indicatorWidth / 2 + mRadius;
float firstCirY = centerY;
for (int i = 0; i < pageNum; i++) {
float cX = firstCirX + i * (mRadius * 6);
float cY = firstCirY;
canvas.drawCircle(cX, cY, mRadius * 0.8f, mPaintFill);
}
}
private void drawCurrentPageCircle(Canvas canvas) {
final int pageNum = mViewPager.getAdapter().getCount();
if (pageNum == 0) {
return;
}
int centerX = canvas.getWidth() / 2;
int centerY = canvas.getHeight() / 2;
float indicatorWidth = pageNum * (6 * mRadius) - (4 * mRadius);
float firstCirX = centerX - indicatorWidth / 2 + mRadius;
float firstCirY = centerY;
float cX = firstCirX + mCurrentPage * (mRadius * 6) + mPageOffset * 6 * mRadius;
float cY = firstCirY;
canvas.drawCircle(cX, cY, mRadius + 2, mPaintCur);
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mCurrentPage = position;
//mPageOffset = positionOffset; //enable for slide animation, it looks stupid btw
invalidate();
}
@Override
public void onPageSelected(int position) {
invalidate();
}
}