Android ViewPager - Show preview of page on left and right
Asked Answered
A

17

151

I'm using Android's ViewPager. What I want to do is to show a preview of the page on both the left and the right. I've seen where I can use a negative pageMargin to show a preview of the right side.

setPageMargin(-100);

Is there anyway that I can show a preview of the left side aswell? Its basically something similar to the gallery widget that I am looking for.

Aniline answered 10/4, 2012 at 23:47 Comment(6)
Good question! I have recently checked the source and I feel there is no better way if you don't want to modify the src code. Does the setPageMargin(-100) work correctly?Shari
the GalleryView will do the job better, unless if you are using the ViewPger with fragments.Bosun
I was able to do this using the gallery view. same as viewpager in any way. don't know why gallery is deprecatedMiun
This question has since been extensively covered in the [post here][1] [1]: #13915109Exult
i am using Vierpager with fragments.So can i used same approach ?Donovan
if you want implement with ViewPager2 then check this answer https://mcmap.net/q/57488/-view-pager-with-previous-and-next-item-smaller-in-size-with-infinite-scrollIrretentive
G
220

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

  1. viewpager.setClipToPadding(false);
  2. viewpager.setPadding(left,0,right,0);

If you need space between two pages in the viewpager then add

viewpager.setPageMargin(int);
Garlen answered 29/4, 2014 at 5:14 Comment(3)
Yes I agree with you Jiju Induchoodan.. For more info please refer blog.neteril.org/blog/2013/10/14/… and #13915109Quechuan
Cool, it's so simple :) Is there possible to resize previews on right and left sizes on switch pages of view pager? For example - if fragment out of focus - it's scale will be ~0.8, but when we drag it to center - scale will increase to 1, and previous fragment from centre view will change scale to 0.8 when going out of screen?Dupleix
This is working! But 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 #52710576Tit
M
200

Solution with the new ViewPager2

Nowadays you should consider using ViewPager2 which "replaces ViewPager, addressing most of its predecessor’s pain-points":

  • Based on RecyclerView
  • RTL (right-to-left) layout support
  • Vertical orientation support
  • Reliable Fragment support (including handling changes to the underlying Fragment collection)
  • Dataset change animations (including DiffUtil support)

The result

enter image description here

The code

In your Activity/Fragment, setup the ViewPager2:

// MyRecyclerViewAdapter is an standard RecyclerView.Adapter :)
viewPager2.adapter = MyRecyclerViewAdapter() 

// You need to retain one page on each side so that the next and previous items are visible
viewPager2.offscreenPageLimit = 1

// Add a PageTransformer that translates the next and previous items horizontally
// towards the center of the screen, which makes them visible
val nextItemVisiblePx = resources.getDimension(R.dimen.viewpager_next_item_visible)
val currentItemHorizontalMarginPx = resources.getDimension(R.dimen.viewpager_current_item_horizontal_margin)
val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
    page.translationX = -pageTranslationX * position
    // Next line scales the item's height. You can remove it if you don't want this effect
    page.scaleY = 1 - (0.25f * abs(position))
    // If you want a fading effect uncomment the next line:
    // page.alpha = 0.25f + (1 - abs(position))
}
viewPager2.setPageTransformer(pageTransformer)

// The ItemDecoration gives the current (centered) item horizontal margin so that
// it doesn't occupy the whole screen width. Without it the items overlap
val itemDecoration = HorizontalMarginItemDecoration(
    context,
    R.dimen.viewpager_current_item_horizontal_margin
)
viewPager2.addItemDecoration(itemDecoration)

Add the HorizontalMarginItemDecoration, which is a trivial ItemDecoration:

/**
 * Adds margin to the left and right sides of the RecyclerView item.
 * Adapted from https://mcmap.net/q/27078/-how-to-add-dividers-and-spaces-between-items-in-recyclerview
 * @param horizontalMarginInDp the margin resource, in dp.
 */
class HorizontalMarginItemDecoration(context: Context, @DimenRes horizontalMarginInDp: Int) :
    RecyclerView.ItemDecoration() {

    private val horizontalMarginInPx: Int =
        context.resources.getDimension(horizontalMarginInDp).toInt()

    override fun getItemOffsets(
        outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
    ) {
        outRect.right = horizontalMarginInPx
        outRect.left = horizontalMarginInPx
    }

}

Add the dimensions that control how much of the previous/next item is visible, and current item horizontal margin:

<dimen name="viewpager_next_item_visible">26dp</dimen>
<dimen name="viewpager_current_item_horizontal_margin">42dp</dimen>

enter image description here

Finally add the ViewPager2 to your layout:

<androidx.viewpager2.widget.ViewPager2
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

One important thing: a ViewPager2 item must have layout_height="match_parent" (otherwise it throws an IllegalStateException), so you should do something like:

<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" <-- this!
    app:cardCornerRadius="8dp"
    app:cardUseCompatPadding="true">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="280dp">

        <!-- ... -->

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

PageTransformer examples

Google has added a guide on ViewPager2 that has 2 PageTransformer implementations that you can use as an inspiration: https://developer.android.com/training/animation/screen-slide-2

About the new ViewPager2

Microscope answered 24/9, 2019 at 21:30 Comment(9)
Same problem on oreo devices, padding start and end works as expected on other devices but oreo after ViewPager2. PageTransformer resolved the issue, paddings are still same and I had to add viewpager.setPageTransformer { page, position -> page.translationX = -1 * position }Basion
I have to slightly move the view pager's page to get this effectCaviness
never mind, I forgot to add this line setOffscreenPageLimit(1) :)Caviness
This is the best solution I ever find. It works perfectly but yes DO NOT FORGOT to set ViewPager2 's Item width, height to MATCH_PARENT.Rossetti
Do you have the source code/Github repo for the GIF you posted?Arber
@akubi Unfortunately I cannot give you the code because it belongs to a private company. But note that you have all the code you need here.Microscope
This is the best solution, better than other tutorials i found online. Thanks for providing detailed commentsAldine
Not show left right page in rtl applicationWaylin
@AlbertVilaCalvo What about vericle viewPager? I am using verticle viewPager and wants to preview upper and lower items?Tentage
D
83

Update :- ViewPager 2.0

Please use the following

viewPager2.setPageTransformer(new MarginPageTransformer(1500));

Also see, Android ViewPager2 setPageMargin unresolved

Old Answer

The answer by @JijuInduchoodan is perfect and working. However, since I am relatively new to Android, it took me a while to understand & set it properly. So, I am posting this answer for future reference and help anyone else who is in same shoes as me.

if (viewPager == null) 
        {
            
            // Initializing view pager
            viewPager = (ViewPager) findViewById(R.id.vpLookBook);
            
            // 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);
        }

There's no need to set any width to the ViewPager's page in the adapter. There no additional code required to see previous & next page in ViewPager. However, if you want to add blank space at the top & bottom of the each page, you can set the following code to ViewPager's child page`s parent layout.

android:paddingTop="20dp"
android:paddingBottom="20dp"

Final ViewPager

This will be the final look of the ViewPager.

Depriest answered 11/12, 2015 at 6:52 Comment(2)
i can not find viewPager.setPageMargin(20); with viewpager2. .Denary
@MahmoudMabrok checkout this question #56114930Depriest
V
29

In 2017 such behaviour additionally can be easily achieved by using RecyclerView with PagerSnapHelper (added in version 25.1.0 of v7 support library): 

enter image description here

Sometime ago I needed such viewpager-like feature and prepared a tiny library:

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 and will save some hours :)

Velour answered 9/8, 2017 at 19:37 Comment(2)
Off topic, because sometimes only pager is needed but Nice to know! Thanks for sharing!Fitzpatrick
This is awesome. I already had tons of RecyclerView helper code. Just adding a simple new PagerSnapHelper().attachToRecyclerView(recyclerView) and I have page snapping.Noetic
A
16

You can do this in xml file, just use below code:

 android:clipToPadding="false"
 android:paddingLeft="XX"
 android:paddingRight="XX"

For example:

<androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:paddingLeft="30dp"
        android:paddingRight="30dp" />

Note: If you need space between pages, set padding/margin to child fragments

Armet answered 28/5, 2019 at 6:6 Comment(1)
I downvoted. sorry bro. It worked this time!! Upvoted againLumen
V
14

Kotlin + ViewPager2

enter image description here

I am late to the party but the above answers didn't work for me. So I am here to help others who are still looking for the answer.

  1. Your XML:

         <androidx.viewpager2.widget.ViewPager2
         android:id="@+id/newsHomeViewPager"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
    
  2. Your item Layout root view should be like this:

         <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:paddingStart="@dimen/dp_10"
     xmlns:tools="http://schemas.android.com/tools"
     android:orientation="vertical" />
    
  3. Your Activity/Fragment:

add this before setting your viewpager adapter

        newsHomeViewPager.apply {
        clipToPadding = false   // allow full width shown with padding
        clipChildren = false    // allow left/right item is not clipped
        offscreenPageLimit = 2  // make sure left/right item is rendered
    }

    //increase this offset to show more of left/right
    val offsetPx =
        resources.getDimension(R.dimen.dp_30).toInt().dpToPx(resources.displayMetrics)
    newsHomeViewPager.setPadding(0, 0, offsetPx, 0)

    //increase this offset to increase distance between 2 items
    val pageMarginPx =
        resources.getDimension(R.dimen.dp_5).toInt().dpToPx(resources.displayMetrics)
    val marginTransformer = MarginPageTransformer(pageMarginPx)
    newsHomeViewPager.setPageTransformer(marginTransformer)
  1. dp to pixel function:

    fun Int.dpToPx(displayMetrics: DisplayMetrics): Int = (this * displayMetrics.density).toInt()

General Notes:

  1. I've to show the next page only, so in setPadding method, I've kept 0 for left and offsetPx for right. So add offsetPx to the right side also in the setPadding method if you wanted to show the right-side item too.

  2. I've used dp from the dimension file, you can use your own.

Thank you, happy coding.

Vaduz answered 24/7, 2021 at 14:27 Comment(1)
The last item has unnecessary padding. How to get rid of it?Hodometer
A
9

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);

Allege answered 23/2, 2018 at 11:37 Comment(0)
G
5

Solution with Kotlin Extention for ViewPager2


  1. Single Line Implementation in your Activity or Fragment.

    binding.viewpager2.setPreviewBothSide(R.dimen._20sdp,R.dimen._35sdp)
    
  2. Add Below Code in HorizontalMarginItemDecoration.kt file in your Project

    import android.content.Context
    import android.graphics.Rect
    import android.view.View
    import androidx.annotation.DimenRes
    import androidx.recyclerview.widget.RecyclerView
    import androidx.viewpager2.widget.ViewPager2
    
    class HorizontalMarginItemDecoration(context: Context, @DimenRes horizontalMarginInDp: Int) :
        RecyclerView.ItemDecoration() {
        private val horizontalMarginInPx: Int =
            context.resources.getDimension(horizontalMarginInDp).toInt()
    
        override fun getItemOffsets(
            outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State
        ) {
            outRect.right = horizontalMarginInPx
            outRect.left = horizontalMarginInPx
        }
    }
    
    fun ViewPager2.setPreviewBothSide(@DimenRes nextItemVisibleSize: Int,@DimenRes currentItemHorizontalMargin: Int) {
        this.offscreenPageLimit = 1
        val nextItemVisiblePx = resources.getDimension(nextItemVisibleSize)
        val currentItemHorizontalMarginPx = resources.getDimension(currentItemHorizontalMargin)
        val pageTranslationX = nextItemVisiblePx + currentItemHorizontalMarginPx
        val pageTransformer = ViewPager2.PageTransformer { page: View, position: Float ->
            page.translationX = -pageTranslationX * position
            page.scaleY = 1 - (0.25f * kotlin.math.abs(position))
        }
        this.setPageTransformer(pageTransformer)
    
        val itemDecoration = HorizontalMarginItemDecoration(
            context,
            currentItemHorizontalMargin
        )
        this.addItemDecoration(itemDecoration)
    }
    
  3. And Yes Here you got this Amazing Output

    enter image description here

This Answer Optimized version of Albert Vila Calvo's Answer

Gunnysack answered 18/8, 2022 at 5:38 Comment(2)
What about vericle viewPager? I am using verticle viewPager and wants to preview upper and lower items?Tentage
@krupaparekh you can try to modify the above answer (I give you a hint) if there is mentioned width you can replace it with height, and left-right replace with top-bottom and modify x to y and y to x in the above answer.Gunnysack
D
4

For those struggling to have this on different screens,

mPager.setClipToPadding(false);
DisplayMetrics displayMetrics = new DisplayMetrics();
self.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int width = displayMetrics.widthPixels;
int paddingToSet = width/4; //set this ratio according to how much of the next and previos screen you want to show.
mPager.setPadding(paddingToSet,0,paddingToSet,0);

Diatomaceous answered 7/2, 2019 at 7:28 Comment(0)
M
2

Thanks to the author of this article. It's the best solution I found.

Just create a function eg. in your Helpers class like:

fun ViewPager2.showHorizontalPreview(offsetDpLeft : Int, offsetDpRight : Int, marginBtwItems : Int){
        this.apply {
            clipToPadding = false   // allow full width shown with padding
            clipChildren = false    // allow left/right item is not clipped
            offscreenPageLimit = 2  // make sure left/right item is rendered
        }

        // increase this offset to show more of left/right
        val offsetPxLeft = offsetDpLeft.toPx()
        val offsetPxRight = offsetDpRight.toPx()
        this.setPadding(offsetPxLeft, 0, offsetPxRight, 0)

        // increase this offset to increase distance between 2 items
        val pageMarginPx = marginBtwItems.toPx()
        val marginTransformer = MarginPageTransformer(pageMarginPx)
        this.setPageTransformer(marginTransformer)
    }

and call it like:

mViewPager.showHorizontalPreview(40,40,5)

in your Activity/Fragment

Mindi answered 16/12, 2021 at 9:50 Comment(0)
B
0

Note that when setting none zero paddings to a viewpager, the viewpager's edgeeffect placement is messed up, this is a bug of the viewpager, see here:viewpager edgeeffect bug

You can either disable the edge effect by setting android:overScrollMode="never" to the viewpager, or you can fix the bug with a slightly modified version of the EdgeEffect class:ViewPagerEdgeEffect fix

Billfold answered 21/6, 2019 at 8:33 Comment(0)
E
0
 override fun getPageWidth(position: Int): Float {
    return 0.7f
}

Add this in adapter class file, This worked for me

Erv answered 11/9, 2020 at 15:1 Comment(0)
S
0

Based on Jiju's answer, if you want to set margin between pages for VIEWPAGER 2, please use the code like this:

viewpager.setPageTransformer(new MarginPageTransformer(14));
Spindling answered 16/7, 2021 at 4:5 Comment(0)
P
0

In the XML file you should set paddings like so: enter image description here

in the fragment:

int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 20*2,     getResources().getDisplayMetrics());

and to have it all set, use this

myViewPager.setPageMargin(-margin);

should do the trick :)

Punctual answered 4/11, 2021 at 19:11 Comment(0)
G
0

Tried and tested using ViewPager2. Use below code to achieve what you want with few lines of code.

    val SPACE_BETWEEN_CARD = 40
    account_details_viewpager.setClipToPadding(false)
    account_details_viewpager.setClipChildren(false)
    account_details_viewpager.setOffscreenPageLimit(3)
    account_details_viewpager.getChildAt(0).setOverScrollMode(RecyclerView.OVER_SCROLL_NEVER);
    val compositePageTransformer = CompositePageTransformer()
    compositePageTransformer.addTransformer(MarginPageTransformer(SPACE_BETWEEN_CARD))
    compositePageTransformer.addTransformer(ViewPager2.PageTransformer() {
            page: View, position: Float ->
        var r :Float = 1f-Math.abs(position)
        page.setScaleY(0.85f + r * 0.15f)
    })
    account_details_viewpager.setPageTransformer(compositePageTransformer)

Here "account_details_viewpager" is view of ViewPager2

Gailgaile answered 19/1, 2023 at 5:12 Comment(1)
Additionally, if you want alpha on back pages of viewpager then use this line page.alpha = 0.25f + (1 - abs(position))Gailgaile
A
0

After searching a lot, I found two working ways for the view pager2
1.

         val pageMargin= 90;
         val pageOffset = 3;
   val transformer = CompositePageTransformer()
        transformer.addTransformer { page, position ->
            val ratio = position * -abs(2 * pageOffset + pageMargin)
            val scaleFactor =
                0.7f.coerceAtLeast(1 - abs(position - 0.14f))
            page.translationX = ratio
            page.scaleY = scaleFactor
        }
        binding.viewPager.setPageTransformer(transformer)

  1. You can do it for each position
         val pageMargin= 70;
         val pageOffset = 3;
         binding.viewPager.setPageTransformer { page, position ->
             val myOffset = (position * -(2 * pageOffset + pageMargin)).toFloat()
             if (position < -1) {
                 page.translationX = -myOffset
             } else if (position <= 1) {
                 val scaleFactor =
                     0.7f.coerceAtLeast(1 - abs(position - 0.14285715f))
                 page.translationX = myOffset
                 page.scaleY = scaleFactor
                 page.alpha = scaleFactor
             } else {
                 page.alpha = 0F
                 page.translationX = myOffset
             }
         }
Averill answered 18/4, 2023 at 11:55 Comment(0)
G
-13

Use this fragment adapter and class to view the viewpager in left and right scroll.add necessary classes to scroll to view next pages.

package com.rmn.viewpager;

import java.util.List;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

/**
 * The <code>PagerAdapter</code> serves the fragments when paging.
 * @author mwho
 */
public class PagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragments;
    /**
     * @param fm
     * @param fragments
     */
    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        this.fragments = fragments;
    }
    /* (non-Javadoc)
     * @see android.support.v4.app.FragmentPagerAdapter#getItem(int)
     */
    @Override
    public Fragment getItem(int position) {
        return this.fragments.get(position);
    }

    /* (non-Javadoc)
     * @see android.support.v4.view.PagerAdapter#getCount()
     */
    @Override
    public int getCount() {
        return this.fragments.size();
    }
}



package com.manishkpr.viewpager;


import android.content.Context;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;

public class ViewPagerAdapter extends FragmentPagerAdapter {
    private Context _context;

    public ViewPagerAdapter(Context context, FragmentManager fm) {
        super(fm);  
        _context=context;

        }
    @Override
    public Fragment getItem(int position) {
        Fragment f = new Fragment();
        switch(position){
        case 0:
            f=LayoutOne.newInstance(_context);  
            break;
        case 1:
            f=LayoutTwo.newInstance(_context);  
            break;
        }
        return f;
    }
    @Override
    public int getCount() {
        return 2;
    }

}
Grethel answered 5/11, 2012 at 7:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.