Skip to content

Instantly share code, notes, and snippets.

@ipereziriarte
Forked from fiskurgit/TutorialActivity.java
Last active September 11, 2015 15:10
Show Gist options
  • Save ipereziriarte/c66b02d1f4e11342b4b1 to your computer and use it in GitHub Desktop.
Save ipereziriarte/c66b02d1f4e11342b4b1 to your computer and use it in GitHub Desktop.
How to do code the slick 'product' tour' view pager animations with fading background colours and parallax scrolling seen in newer Google products
<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"
tools:context="eu.fiskur.pennineway.tutorial.TutorialActivity"
android:background="@android:color/transparent">
<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"
android:background="@color/tutorial_background_opaque"/>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_above="@+id/button_layout"
android:background="#33ffffff"/>
<RelativeLayout
android:id="@+id/button_layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@android:color/transparent"
>
<Button
android:id="@+id/skip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="SKIP"
android:textSize="18sp"
android:textColor="@color/tutorial_button"
android:background="@android:color/transparent"/>
<LinearLayout
android:id="@+id/circles"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_centerInParent="true">
</LinearLayout>
<Button
android:id="@+id/done"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="DONE"
android:textSize="18sp"
android:textColor="@color/tutorial_button"
android:background="@android:color/transparent"
android:visibility="gone"/>
<ImageButton
android:id="@+id/next"
android:layout_width="90dp"
android:layout_height="45dp"
android:paddingTop="13dp"
android:paddingBottom="12dp"
android:layout_alignParentRight="true"
android:src="@drawable/chevron"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
/>
</RelativeLayout>
</RelativeLayout>
package eu.fiskur.pennineway.tutorial;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class TutorialPane extends Fragment{
final static String LAYOUT_ID = "layoutid";
public static TutorialPane newInstance(int layoutId) {
TutorialPane pane = new TutorialPane();
Bundle args = new Bundle();
args.putInt(LAYOUT_ID, layoutId);
pane.setArguments(args);
return pane;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(getArguments().getInt(LAYOUT_ID, -1), container, false);
return rootView;
}
}
package eu.fiskur.pennineway.tutorial;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import eu.fiskur.pennineway.R;
public class TutorialActivity extends ActionBarActivity {
static final int NUM_PAGES = 5;
ViewPager pager;
PagerAdapter pagerAdapter;
LinearLayout circles;
Button skip;
Button done;
ImageButton next;
/*
This is nasty but as the transparency of the fragments increases when swiping the underlying
Activity becomes visible, so we change the pager opacity on the last slide in
setOnPageChangeListener below
*/
boolean isOpaque = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*
Setting this makes sure we draw fullscreen, without this the transparent Activity shows
the bright orange notification header from the main Activity below
*/
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
setContentView(R.layout.activity_tutorial);
getSupportActionBar().hide();
skip = Button.class.cast(findViewById(R.id.skip));
skip.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
endTutorial();
}
});
next = ImageButton.class.cast(findViewById(R.id.next));
next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pager.setCurrentItem(pager.getCurrentItem() + 1, true);
}
});
done = Button.class.cast(findViewById(R.id.done));
done.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
endTutorial();
}
});
pager = (ViewPager) findViewById(R.id.pager);
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
pager.setAdapter(pagerAdapter);
pager.setPageTransformer(true, new CrossfadePageTransformer());
pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//See note above for why this is needed
if(position == NUM_PAGES - 2 && positionOffset > 0){
if(isOpaque) {
pager.setBackgroundColor(Color.TRANSPARENT);
isOpaque = false;
}
}else{
if(!isOpaque) {
pager.setBackgroundColor(getResources().getColor(R.color.tutorial_background_opaque));
isOpaque = true;
}
}
}
@Override
public void onPageSelected(int position) {
setIndicator(position);
if(position == NUM_PAGES - 2){
skip.setVisibility(View.GONE);
next.setVisibility(View.GONE);
done.setVisibility(View.VISIBLE);
}else if(position < NUM_PAGES - 2){
skip.setVisibility(View.VISIBLE);
next.setVisibility(View.VISIBLE);
done.setVisibility(View.GONE);
}else if(position == NUM_PAGES - 1){
endTutorial();
}
}
@Override
public void onPageScrollStateChanged(int state) {
//Unused
}
});
buildCircles();
}
/*
The last fragment is transparent to enable the swipe-to-finish behaviour seen on Google's apps
So our viewpager circle indicator needs to show NUM_PAGES - 1
*/
private void buildCircles(){
circles = LinearLayout.class.cast(findViewById(R.id.circles));
float scale = getResources().getDisplayMetrics().density;
int padding = (int) (5 * scale + 0.5f);
for(int i = 0 ; i < NUM_PAGES - 1 ; i++){
ImageView circle = new ImageView(this);
circle.setImageResource(R.drawable.circle);
circle.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
circle.setAdjustViewBounds(true);
circle.setPadding(padding, 0, padding, 0);
circles.addView(circle);
}
setIndicator(0);
}
private void setIndicator(int index){
if(index < NUM_PAGES){
for(int i = 0 ; i < NUM_PAGES - 1 ; i++){
ImageView circle = (ImageView) circles.getChildAt(i);
if(i == index){
circle.setImageResource(R.drawable.circle_selected);
}else {
circle.setImageResource(R.drawable.circle);
}
}
}
}
private void endTutorial(){
finish();
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
}
@Override
public void onBackPressed() {
if (pager.getCurrentItem() == 0) {
super.onBackPressed();
} else {
pager.setCurrentItem(pager.getCurrentItem() - 1);
}
}
private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
TutorialPane tp = null;
switch(position){
case 0:
tp = TutorialPane.newInstance(R.layout.fragment_tutorial_one);
break;
case 1:
tp = TutorialPane.newInstance(R.layout.fragment_tutorial_two);
break;
case 2:
tp = TutorialPane.newInstance(R.layout.fragment_tutorial_three);
break;
case 3:
tp = TutorialPane.newInstance(R.layout.fragment_tutorial_four);
break;
case 4:
tp = TutorialPane.newInstance(R.layout.fragment_tutorial_transparent);
break;
}
return tp;
}
@Override
public int getCount() {
return NUM_PAGES;
}
}
public class CrossfadePageTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(View page, float position) {
int pageWidth = page.getWidth();
View backgroundView = page.findViewById(R.id.background);
View text = page.findViewById(R.id.content);
View phone = page.findViewById(R.id.phone);
View map = page.findViewById(R.id.map);
View mountain = page.findViewById(R.id.mountain);
View mountainNight = page.findViewById(R.id.mountain_night);
View rain = page.findViewById(R.id.rain);
View hands = page.findViewById(R.id.screenshot);
if (position <= 1) {
page.setTranslationX(pageWidth * -position);
}
if(position <= -1.0f || position >= 1.0f) {
} else if( position == 0.0f ) {
} else {
if(backgroundView != null) {
backgroundView.setAlpha(1.0f - Math.abs(position));
}
//Text both translates in/out and fades in/out
if (text != null) {
text.setTranslationX(pageWidth * position);
text.setAlpha(1.0f - Math.abs(position));
}
//Map + phone - map simple translate, phone parallax effect
if(map != null){
map.setTranslationX(pageWidth * position);
}
if(phone != null){
phone.setTranslationX((float)(pageWidth/1.2 * position));
}
//Mountain day - fade in/out
if(mountain != null){
mountain.setAlpha(1.0f - Math.abs(position));
}
//Mountain night - fade in, but translate out, rain fades in but parallax translate out
if(mountainNight != null){
if(position < 0){
mountainNight.setTranslationX(pageWidth * position);
}else{
mountainNight.setAlpha(1.0f - Math.abs(position));
}
}
if(rain != null){
if(position < 0){
rain.setTranslationX((float)(pageWidth/1.2 * position));
}else{
rain.setAlpha(1.0f - Math.abs(position));
}
}
//Long click device + hands - translate both way but only fade out
if(hands != null){
hands.setTranslationX(pageWidth * position);
if(position < 0) {
hands.setAlpha(1.0f - Math.abs(position));
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment