150

I'm designing a view with multiple pages. I want edges of previous and next pages to be show like below and implement a 2 finger swipe to switch between pages.

enter image description here

I tried using ViewPager with negative page margin as suggested here but that only shows one of the edges on the screen, not both simultaneously.

Alternatively, is there any way i can position part of my view outside screen and then animate it giving it a ViewPager type effect.

How should I go about it ? Thanks !

Community
  • 1
  • 1
Gaurav Arora
  • 16,016
  • 5
  • 31
  • 42
  • "only shows one of the edges on the screen, not both simultaneously." Are you on page 0 and you only see part of page 1? Perhaps you need to use a circular pager, example and then set your page always to the "middle" position. See this post and the comment : http://stackoverflow.com/a/8304474/1851478 – logray Dec 20 '12 at 20:29

8 Answers8

118

I have a similar solution:

On the viewpager set left and right padding, e.g. 20dp. Do also set the page margin on the viewpager, e.g. half of the pager padding. And do not forget to disable clip padding.

tilePager.setPadding(defaultGap, 0, defaultGap, 0);
tilePager.setClipToPadding(false);
tilePager.setPageMargin(halfGap);
Thomas R.
  • 7,592
  • 3
  • 28
  • 38
102

Quoting myself from a blog post on this subject:

The third approach comes from Dave Smith, co-author of the well-regarded book Android Recipes. He went in a very different direction, using a custom container that disabled children clipping to show more than one page at a time.

His published sample code shows the whole thing in action. His container (com.example.pagercontainer.PagerContainer) wraps the ViewPager and calls setClipChildren(false); on itself, so even though the ViewPager is focused on one selected page, other pages that have coordinates beyond the ViewPager bounds are still visible, so long as they fit within the PagerContainer. By sizing the ViewPager to be smaller than the PagerContainer, the ViewPager can size its pages to that size, leaving room for other pages to be seen. PagerContainer, though, needs to help out a bit with touch events, as ViewPager will only handle swipe events on its own visible bounds, ignoring any pages visible to the sides.

enter image description here

Community
  • 1
  • 1
CommonsWare
  • 910,778
  • 176
  • 2,215
  • 2,253
  • 1
    by using this,I am able to show part of previous and next page as shown in image above,but now I don't want to show sharp edges on images.I want them to blur towards edges..please guide me on how can i use z-index for achieving the same – Shruti Jun 11 '13 at 14:41
  • 2
    @Shruti - just add an overlay image with the effect you want – Daniel L. Oct 08 '13 at 15:20
  • am try this example it works perfectly in emulator but not work in Samsung real device.it overlaps the images. – Yugesh Mar 31 '14 at 09:55
  • 2
    I do the same but it disables the over-scroll effect for the last item. Any leads on that ? – Swayam May 07 '14 at 13:40
  • @Swayam: I actually haven't used Dave's technique much. I tend to go with [the `getPageWidth()` solution](http://commonsware.com/blog/2012/08/20/multiple-view-viewpager-options.html) (see option #1), which still supports overscroll. – CommonsWare May 07 '14 at 13:51
  • Thanks for your prompt reply sir! I am yet to try the solution out! But until now, what I have been experiencing is that as soon as give padding to the Pager, the last page doesn't have the overscroll effect. Funnily though, the first one still has overscroll. Would you have any idea? – Swayam May 07 '14 at 14:06
  • @Swayam: As I noted, I haven't used Dave's technique much, and so I have no idea how it behaves with respect to overscroll. – CommonsWare May 07 '14 at 14:12
  • Alright sir! I will try your solution then! Thanks much! :) – Swayam May 07 '14 at 14:14
  • 1
    @CommonsWare : Sir, I tried your solution! It worked pretty well. The overscroll is there. Only problem now is that the next card shows, but not the previous card. That is, if I am on page 2, I can see page 3 peeking out, but not page 1. Where could I have been going wrong? – Swayam May 08 '14 at 04:40
  • @Swayam: You aren't "going wrong". That is how the first technique works. – CommonsWare May 08 '14 at 11:08
  • So, what do I have to do to preserve the overscroll and also show both the previous and next page ? – Swayam May 08 '14 at 11:09
  • 2
    @Swayam: I have no idea. – CommonsWare May 08 '14 at 11:12
  • No problem sir. I will figure something from your blogpost. Thanks for all the help. :) – Swayam May 08 '14 at 11:13
  • @CommonsWare your solution work pretty well..is there way to make left/right side view semi transparent and then gradually start visibility as it start to come in centre. Please suggest. – CoDe Dec 09 '14 at 10:22
  • Beware that this solution calls setOnPageChangeListener(...). There can only be one page change listener for a viewpager. Therefore you need to add code to relay the listener through the container otherwise you get weird artifiacts due to redraw problems. – Greg Ennis Dec 19 '14 at 22:34
  • This doesn't work for first time when using custom page transformers. After a bit of scrolling, it works. Any suggestions please? – Nick Nov 11 '15 at 14:03
  • Is there a way to make the next item and the preview item blurred, then when on focus remove the blur ? – Mr T Jul 21 '16 at 06:42
  • @MrG: You could overlay the `ViewPager` with something that applies the blur on the sides. There are probably better solutions; you may want to ask a separate Stack Overflow question on this. – CommonsWare Jul 21 '16 at 13:08
  • @CommonsWare i tried that demo..but the issue is that i am not able to get my last item position. – Aditya Vyas-Lakhan Jan 06 '17 at 06:24
  • @CommonsWare it works as expected but it remove corner radius of CardView? any help to fix this please? – Think Twice Code Once Jun 19 '19 at 07:30
  • 1
    @ThinkTwiceCodeOnce: This question does not appear to have anything to do with `CardView`. You should be able to call [`setRadius()` on a `CardView`](https://developer.android.com/reference/androidx/cardview/widget/CardView.html#setRadius(float)) to change its radius. If you have further concerns in this area, and you are not finding any existing questions that cover it, ask a fresh question. – CommonsWare Jun 19 '19 at 11:01
76
  1. Set left and right padding for whole item view. Example xml (page_item.xml):

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingLeft="20dp"
    android:paddingRight="20dp"/>
    
    <TextView
        android:id="@+id/text1"
        android:text="Large Text"
        android:textAppearance="?android:attr/textAppearanceLarge" />
    
    </LinearLayout>
    
  2. Then set negative page margin for PageView equal to 2*(previous view padding)

    int margin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());
    mViewPager.setPageMargin(-margin);
    
  3. Optional. Set zero left padding for first item and zero right padding to last item to hide empty edges. You may do this in the PageAdapter or Page fragment class.

Ryan Kohn
  • 11,921
  • 10
  • 50
  • 80
SergeyA
  • 4,087
  • 1
  • 20
  • 14
  • @Sergey, I can't make this work with your solution, could you post a example? thx – Marckaraujo Sep 06 '13 at 14:42
  • 12
    just adding a note: with this solution when you slide from page 1 to page 2, the page 3 isn't in memory, so it will appear with a delay. to fix this just add - yourViewPager.setOffscreenPageLimit(2); – José Barbosa Mar 03 '14 at 11:03
  • I do the same but it disables the over-scroll effect for the last item. Any leads on that ? – Swayam May 07 '14 at 13:42
  • I can't seem to get this to work either...the margins seem to display randomly if I use images w/ scale set to center crop. Anyone have a working code example they can share? – kenyee May 08 '14 at 17:40
  • 2
    How to touch first and last item? By checking page index in OnPageListener? – Hardik9850 Jul 06 '17 at 13:36
  • When I try to zoom the center layout, its zooming to top and bottom. The left and right is not visible due to the padding. Please see the question https://stackoverflow.com/questions/52710076/android-pinch-to-zoom-layout-inside-a-viewpager-with-padding-left-and-right – Anooj Krishnan G Oct 09 '18 at 17:03
49

To show preview of left and right pages set the following two values

viewpager.setClipToPadding(false)
viewpager.setPadding(left,0,right,0)

If you need space between two pages in the viewpager then add viewpager.setPageMargin(int)

Android ViewPager - Show preview of page on left and right

Community
  • 1
  • 1
molu2008
  • 1,189
  • 1
  • 12
  • 19
10

if someone still looking for solution, I had customized the ViewPage to achieve it without using negative margin, find a sample project here https://github.com/44kksharma/Android-ViewPager-Carousel-UI it should work in most cases but you can still define page margin with mPager.setPageMargin(margin in pixel);

44kksharma
  • 2,235
  • 22
  • 27
1

Download the source code from here(ViewPager with previous and next page boundaries)

MainActivity.java

package com.deepshikha.viewpager;

import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
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.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends FragmentActivity {

    ViewPager pager;
    MyPageAdapter obj_adapter;
    String str_device;

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();


    }

    private void init() {
        pager = (ViewPager) findViewById(R.id.viewpager);
        differentDensityAndScreenSize(getApplicationContext());
        List<Fragment> fragments = getFragments();
        pager.setAdapter(obj_adapter);
        pager.setClipToPadding(false);


        if (str_device.equals("normal-hdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-mdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-xhdpi")){
            pager.setPadding(160, 0, 160, 0);
        }else if (str_device.equals("normal-xxhdpi")){
            pager.setPadding(180, 0, 180, 0);
        }else if (str_device.equals("normal-xxxhdpi")){
            pager.setPadding(180, 0, 180, 0);
        }else if (str_device.equals("normal-unknown")){
            pager.setPadding(160, 0, 160, 0);
        }else {

        }

        obj_adapter = new MyPageAdapter(getSupportFragmentManager(), fragments);
        pager.setPageTransformer(true, new ExpandingViewPagerTransformer());
        pager.setAdapter(obj_adapter);
    }

    class MyPageAdapter extends FragmentPagerAdapter {

        private List<Fragment> fragments;

        public MyPageAdapter(FragmentManager fm, List<Fragment> fragments) {

            super(fm);

            this.fragments = fragments;

        }

        @Override

        public Fragment getItem(int position) {

            return this.fragments.get(position);

        }

        @Override

        public int getCount() {

            return this.fragments.size();

        }

    }

    private List<Fragment> getFragments() {

        List<Fragment> fList = new ArrayList<Fragment>();

        fList.add(MyFragment.newInstance("Fragment 1",R.drawable.imags));
        fList.add(MyFragment.newInstance("Fragment 2",R.drawable.image1));
        fList.add(MyFragment.newInstance("Fragment 3",R.drawable.image2));
        fList.add(MyFragment.newInstance("Fragment 4",R.drawable.image3));
        fList.add(MyFragment.newInstance("Fragment 5",R.drawable.image4));

        return fList;

    }

    public int differentDensityAndScreenSize(Context context) {
        int value = 20;
        String str = "";
        if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_SMALL) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "small-ldpi";
                    // Log.e("small 1","small-ldpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    str = "small-mdpi";
                    // Log.e("small 1","small-mdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    str = "small-hdpi";
                    // Log.e("small 1","small-hdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    str = "small-xhdpi";
                    // Log.e("small 1","small-xhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    str = "small-xxhdpi";
                    // Log.e("small 1","small-xxhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    str = "small-xxxhdpi";
                    //Log.e("small 1","small-xxxhdpi");
                    value = 20;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    str = "small-tvdpi";
                    // Log.e("small 1","small-tvdpi");
                    value = 20;
                    break;
                default:
                    str = "small-unknown";
                    value = 20;
                    break;
            }

        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_NORMAL) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "normal-ldpi";
                    // Log.e("normal-ldpi 1","normal-ldpi");
                    str_device = "normal-ldpi";
                    value = 82;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    // Log.e("normal-mdpi 1","normal-mdpi");
                    str = "normal-mdpi";
                    value = 82;
                    str_device = "normal-mdpi";
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    // Log.e("normal-hdpi 1","normal-hdpi");
                    str = "normal-hdpi";
                    str_device = "normal-hdpi";
                    value = 82;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    //Log.e("normal-xhdpi 1","normal-xhdpi");
                    str = "normal-xhdpi";
                    str_device = "normal-xhdpi";
                    value = 90;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    // Log.e("normal-xxhdpi 1","normal-xxhdpi");
                    str = "normal-xxhdpi";
                    str_device = "normal-xxhdpi";
                    value = 96;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    //Log.e("normal-xxxhdpi","normal-xxxhdpi");
                    str = "normal-xxxhdpi";
                    str_device = "normal-xxxhdpi";
                    value = 96;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("DENSITY_TV 1","normal-mdpi");
                    str = "normal-tvdpi";
                    str_device = "normal-tvmdpi";
                    value = 96;
                    break;
                default:
                    // Log.e("normal-unknown","normal-unknown");
                    str = "normal-unknown";
                    str_device = "normal-unknown";
                    value = 82;
                    break;
            }
        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    str = "large-ldpi";
                    // Log.e("large-ldpi 1","normal-ldpi");
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    str = "large-mdpi";
                    //Log.e("large-ldpi 1","normal-mdpi");
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    //Log.e("large-ldpi 1","normal-hdpi");
                    str = "large-hdpi";
                    value = 78;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    // Log.e("large-ldpi 1","normal-xhdpi");
                    str = "large-xhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    //Log.e("large-ldpi 1","normal-xxhdpi");
                    str = "large-xxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    // Log.e("large-ldpi 1","normal-xxxhdpi");
                    str = "large-xxxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("large-ldpi 1","normal-tvdpi");
                    str = "large-tvdpi";
                    value = 125;
                    break;
                default:
                    str = "large-unknown";
                    value = 78;
                    break;
            }

        } else if ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
            switch (context.getResources().getDisplayMetrics().densityDpi) {
                case DisplayMetrics.DENSITY_LOW:
                    // Log.e("large-ldpi 1","normal-ldpi");
                    str = "xlarge-ldpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_MEDIUM:
                    // Log.e("large-ldpi 1","normal-mdpi");
                    str = "xlarge-mdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_HIGH:
                    //Log.e("large-ldpi 1","normal-hdpi");
                    str = "xlarge-hdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XHIGH:
                    // Log.e("large-ldpi 1","normal-hdpi");
                    str = "xlarge-xhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXHIGH:
                    // Log.e("large-ldpi 1","normal-xxhdpi");
                    str = "xlarge-xxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_XXXHIGH:
                    // Log.e("large-ldpi 1","normal-xxxhdpi");
                    str = "xlarge-xxxhdpi";
                    value = 125;
                    break;
                case DisplayMetrics.DENSITY_TV:
                    //Log.e("large-ldpi 1","normal-tvdpi");
                    str = "xlarge-tvdpi";
                    value = 125;
                    break;
                default:
                    str = "xlarge-unknown";
                    value = 125;
                    break;
            }
        }

        return value;
    }
}
Deepshikha Puri
  • 1,844
  • 17
  • 23
1

Sometime ago I needed such feature and prepared a tiny library which uses RecyclerView with PagerSnapHelper (added in version 25.1.0 of the v7 support library) instead of classic ViewPager:

MetalRecyclerPagerView - you can find all the code along with examples there.

Mainly it consists of a single class file: MetalRecyclerViewPager.java (and two xmls: attrs.xml and ids.xml).

Hope it helps somebody :)

Alexander Bilchuk
  • 1,670
  • 10
  • 17
0

Carousel ViewPager fragment

    ViewPager viewPager = findViewById(R.id.viewPager);
    TabPagerAdapter tabPagerAdapter = new TabPagerAdapter(this,getSupportFragmentManager());
    viewPager.setAdapter(tabPagerAdapter);
    // Disable clip to padding
    viewPager.setClipToPadding(false);
    // set padding manually, the more you set the padding the more you see of prev & next page
    viewPager.setPadding(40, 0, 40, 0);
    // sets a margin b/w individual pages to ensure that there is a gap b/w them
    viewPager.setPageMargin(20);

Carousel ViewPager