In this post we will see how to make an image slider with parallax effect. You can download the code, and the apk: normal, flickR style.

First I will explain how to achieve this effect without copying the FlickR layout. This is what we will make:

Fast resume: we will create a ViewPager in the MainActivity and attach it an OnPageChangeListener, then in the onPageScrolled method add the parallax effect.

These are the elements of our project:

Layout

Main activity layout

Page adapter element layout:

We define the ImageView in a FrameLayout to keep the image inside and not to overlap the other images when applying the parallax effect.

Code

We will start defining the ViewPage adapter. Inside it we will override the intantiateItem method to inflate our layout and attach the image.

public class ImagePageAdapter extends PagerAdapter { private final Activity activity; private int[] imagesId; Map imageViews = new HashMap(); public ImagePageAdapter(Activity activity, int[] imagesId) { this.activity = activity; this.imagesId = imagesId; } public Map getImageViews() { return imageViews; } @Override public int getCount() { return imagesId.length; } public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { LayoutInflater inflater = activity.getLayoutInflater(); View view = inflater.inflate(R.layout.page_adapter_element, null); ImageView imageView = (ImageView) view.findViewById(R.id.image); imageView.setImageResource(imagesId[position]); container.addView(view); imageViews.put(position, imageView); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((FrameLayout) object); } }

We have defined the field imageViews that saves the position and the view of the image. We will access this field later from the MainActivity to get the views and apply the parallax effect.

Saving the views at this point improves the performance due to we don’t have to search for them lather.

Now we will analyze the MainActivity code:

public class MainActivity extends ActionBarActivity implements ViewPager.OnPageChangeListener { int[] pictures = new int[]{ R.drawable.picture_1, R.drawable.picture_2, R.drawable.picture_3, R.drawable.picture_4, R.drawable.picture_5, R.drawable.picture_6 }; private int width; ViewPager viewPager; private ImagePageAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViewPagerAndSetAdapter(); calculateWidth(); } private void initViewPagerAndSetAdapter(){ viewPager = (ViewPager) findViewById(R.id.parallaxSlider); adapter = new ImagePageAdapter(this, pictures); viewPager.setAdapter(adapter); addPageChangeListenerIfSDKAbove11(); } private void addPageChangeListenerIfSDKAbove11() { if (Build.VERSION.SDK_INT >11) { viewPager.setOnPageChangeListener(this); } } private void calculateWidth() { Display display = getWindowManager().getDefaultDisplay(); Point size = new Point(); viewPager.getWidth(); if (Build.VERSION.SDK_INT <13) { width = display.getWidth(); } else { display.getSize(size); width = size.x; } } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { parallaxImages(position, positionOffsetPixels); } private void parallaxImages(int position, int positionOffsetPixels) { Map imageViews = adapter.getImageViews(); for (Map.Entry entry: imageViews.entrySet()){ int imagePosition = entry.getKey(); int correctedPosition = imagePosition - position; int displace = -(correctedPosition * width/2)+ (positionOffsetPixels / 2); View view = entry.getValue(); view.setX(displace); } } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { } }

We set the content view, init the view pager, set the onPageChangeListener and finally calculate the width of the screen (because we will need it to make the parallax effect).

As we can see we don’t do nothing to complicated, all the “magic” its done in the parallaxImages method.

The parallax effect

First we will see how is the effect we want to achieve, and later we will recheck the code again.

In an view pager the positions of the views are assigned from 0 to N in order. In the onPageScrolled method the position parameter indicates the view that is more to the left in the Screen. Here we can see the values for that parameter in function of the screen position (the black rectangle). The orange squares are the views.

In this image (click to full size) we can see the starting and end position of the first images (position 0 and 1), and its correlation with the code.

private void parallaxImages(int position, int positionOffsetPixels) { Map imageViews = adapter.getImageViews(); for (Map.Entry entry: imageViews.entrySet()){ int imagePosition = entry.getKey(); int correctedPosition = imagePosition - position; int displace = -(correctedPosition * width/2)+ (positionOffsetPixels / 2); View view = entry.getValue(); view.setX(displace); } }

On this method we process all the views we saved (in the instantiate method) adjusting the X.

Custom layouts, FlickR like layout example

Once you get this point is easy to customize your layouts. For example I will change the layout to have the slider similar to the flickR.

Here are the new layout files:

Lets see the viewpager layout first:

The only differences are that now we want to match the parent size, and we add a small padding to the sides to the FrameLayout.

Now lets see the MainActivity layout:

In this case:

I set the height of the ViewPager to match parent height.

I added a view on top of the view pager with a gradient from grey to transparent and white text in top. This way we can read the text even with a bright images.

Here is the code for the gradient:

You can check the code at bitbucket, in the flickRLike branch.