ViewPager with previous and next page boundaries
Asked Answered
E

8

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 !

Embracery answered 17/12, 2012 at 12:55 Comment(1)
"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 : https://mcmap.net/q/112094/-viewpager-as-a-circular-queue-wrappingRotgut
B
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

Bruton answered 21/12, 2012 at 20:29 Comment(24)
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 sameCowboy
@Cowboy - just add an overlay image with the effect you wantAluminiferous
am try this example it works perfectly in emulator but not work in Samsung real device.it overlaps the images.Randirandie
I do the same but it disables the over-scroll effect for the last item. Any leads on that ?Unrivaled
@Swayam: I actually haven't used Dave's technique much. I tend to go with the getPageWidth() solution (see option #1), which still supports overscroll.Bruton
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?Unrivaled
@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.Bruton
@Bruton : 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?Unrivaled
@Swayam: You aren't "going wrong". That is how the first technique works.Bruton
So, what do I have to do to preserve the overscroll and also show both the previous and next page ?Unrivaled
@Swayam: I have no idea.Bruton
No problem sir. I will figure something from your blogpost. Thanks for all the help. :)Unrivaled
@Bruton 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.Stuart
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.Esteresterase
This doesn't work for first time when using custom page transformers. After a bit of scrolling, it works. Any suggestions please?Fear
Is there a way to make the next item and the preview item blurred, then when on focus remove the blur ?Peaked
@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.Bruton
@Bruton i tried that demo..but the issue is that i am not able to get my last item position.Gothart
@Bruton it works as expected but it remove corner radius of CardView? any help to fix this please?Garfield
@ThinkTwiceCodeOnce: This question does not appear to have anything to do with CardView. You should be able to call setRadius() on a CardView 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.Bruton
How can we make the off screen items respond to click events.Weig
@oziomajnr: You would do it the same way as you would anything else in a ViewPager: add click listeners to the widgets. However, for items that are fully off-screen, the user has no visible pixels of those widgets to click.Bruton
I have tried setting on click listeners to the off screen elements but because they are drawn outside the parent, they do not respond to click events.Weig
@oziomajnr: You might try switching to ViewPager2, as ViewPager itself largely is deprecated. Beyond that, consider asking a separate Stack Overflow question with a minimal reproducible example demonstrating what you are trying and explaining in detail what you are seeing.Bruton
D
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);
Detrital answered 26/5, 2014 at 12:37 Comment(5)
Good solution provided.Dally
easiest and best wayFabi
This is the beast yes beast answer for consider naming values xdNitroparaffin
side note: this wont work with a custom view pager transformerBichromate
@Bichromate any solution for transformer ?Helenhelena
B
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

Bruton answered 21/12, 2012 at 20:29 Comment(24)
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 sameCowboy
@Cowboy - just add an overlay image with the effect you wantAluminiferous
am try this example it works perfectly in emulator but not work in Samsung real device.it overlaps the images.Randirandie
I do the same but it disables the over-scroll effect for the last item. Any leads on that ?Unrivaled
@Swayam: I actually haven't used Dave's technique much. I tend to go with the getPageWidth() solution (see option #1), which still supports overscroll.Bruton
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?Unrivaled
@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.Bruton
@Bruton : 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?Unrivaled
@Swayam: You aren't "going wrong". That is how the first technique works.Bruton
So, what do I have to do to preserve the overscroll and also show both the previous and next page ?Unrivaled
@Swayam: I have no idea.Bruton
No problem sir. I will figure something from your blogpost. Thanks for all the help. :)Unrivaled
@Bruton 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.Stuart
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.Esteresterase
This doesn't work for first time when using custom page transformers. After a bit of scrolling, it works. Any suggestions please?Fear
Is there a way to make the next item and the preview item blurred, then when on focus remove the blur ?Peaked
@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.Bruton
@Bruton i tried that demo..but the issue is that i am not able to get my last item position.Gothart
@Bruton it works as expected but it remove corner radius of CardView? any help to fix this please?Garfield
@ThinkTwiceCodeOnce: This question does not appear to have anything to do with CardView. You should be able to call setRadius() on a CardView 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.Bruton
How can we make the off screen items respond to click events.Weig
@oziomajnr: You would do it the same way as you would anything else in a ViewPager: add click listeners to the widgets. However, for items that are fully off-screen, the user has no visible pixels of those widgets to click.Bruton
I have tried setting on click listeners to the off screen elements but because they are drawn outside the parent, they do not respond to click events.Weig
@oziomajnr: You might try switching to ViewPager2, as ViewPager itself largely is deprecated. Beyond that, consider asking a separate Stack Overflow question with a minimal reproducible example demonstrating what you are trying and explaining in detail what you are seeing.Bruton
O
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.

Overhaul answered 2/4, 2013 at 19:47 Comment(6)
@Sergey, I can't make this work with your solution, could you post a example? thxAltdorf
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);Gertrudegertrudis
I do the same but it disables the over-scroll effect for the last item. Any leads on that ?Unrivaled
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?Refectory
How to touch first and last item? By checking page index in OnPageListener?Declaratory
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 #52710576Dependence
H
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

Hairsplitter answered 23/6, 2014 at 15:1 Comment(3)
This should be correct answer. I think maybe this didnt work in prior versions of viewpager but it works now.Esteresterase
Its adding same margin on left side of first and right side of last page as well. Any fixUndercover
Short and more Clear answer.Punkie
B
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);

Boater answered 23/2, 2018 at 11:46 Comment(2)
Thanks. How to increase space between pages?Defrayal
mPager.setPageMargin(marging in pixels);Boater
A
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;
    }
}
Angelicaangelico answered 11/7, 2017 at 4:45 Comment(1)
This code is not working properly, its showing left side page bit bigger than right sideRipping
A
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 :)

Archaimbaud answered 8/10, 2017 at 12:29 Comment(0)
T
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

Tenpins answered 27/2, 2021 at 15:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.