Skip to content

Instantly share code, notes, and snippets.

@devunwired
Last active December 20, 2023 12:34
Star You must be signed in to star a gist
Save devunwired/8cbe094bb7a783e37ad1 to your computer and use it in GitHub Desktop.
ViewPager container implementation that allows surrounding pages be visible (although not interactive). PagerContainer holds a ViewPager (in this case centered), has clipChildren() set to false, and has some extra code to redraw itself and forward touche
<!-- Copyright (c) 2012 Wireless Designs, LLC
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-->
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.pagercontainer.PagerContainer
android:id="@+id/pager_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#CCC">
<android.support.v4.view.ViewPager
android:layout_width="150dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
</com.example.pagercontainer.PagerContainer>
</RelativeLayout>
/*
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.example.pagercontainer;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
/**
* PagerActivity: A Sample Activity for PagerContainer
*/
public class PagerActivity extends Activity {
PagerContainer mContainer;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mContainer = (PagerContainer) findViewById(R.id.pager_container);
ViewPager pager = mContainer.getViewPager();
PagerAdapter adapter = new MyPagerAdapter();
pager.setAdapter(adapter);
//Necessary or the pager will only have one extra page to show
// make this at least however many pages you can see
pager.setOffscreenPageLimit(adapter.getCount());
//A little space between pages
pager.setPageMargin(15);
//If hardware acceleration is enabled, you should also remove
// clipping on the pager for its children.
pager.setClipChildren(false);
}
//Nothing special about this adapter, just throwing up colored views for demo
private class MyPagerAdapter extends PagerAdapter {
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView view = new TextView(PagerActivity.this);
view.setText("Item "+position);
view.setGravity(Gravity.CENTER);
view.setBackgroundColor(Color.argb(255, position * 50, position * 10, position * 50));
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public int getCount() {
return 5;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
}
}
/*
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.example.pagercontainer;
import android.content.Context;
import android.graphics.Point;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* PagerContainer: A layout that displays a ViewPager with its children that are outside
* the typical pager bounds.
*/
public class PagerContainer extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager mPager;
boolean mNeedsRedraw = false;
public PagerContainer(Context context) {
super(context);
init();
}
public PagerContainer(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public PagerContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
//Disable clipping of children so non-selected pages are visible
setClipChildren(false);
//Child clipping doesn't work with hardware acceleration in Android 3.x/4.x
//You need to set this value here if using hardware acceleration in an
// application targeted at these releases.
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
@Override
protected void onFinishInflate() {
try {
mPager = (ViewPager) getChildAt(0);
mPager.setOnPageChangeListener(this);
} catch (Exception e) {
throw new IllegalStateException("The root child of PagerContainer must be a ViewPager");
}
}
public ViewPager getViewPager() {
return mPager;
}
private Point mCenter = new Point();
private Point mInitialTouch = new Point();
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenter.x = w / 2;
mCenter.y = h / 2;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
//We capture any touches not already handled by the ViewPager
// to implement scrolling from a touch outside the pager bounds.
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialTouch.x = (int)ev.getX();
mInitialTouch.y = (int)ev.getY();
default:
ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
break;
}
return mPager.dispatchTouchEvent(ev);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//Force the container to redraw on scrolling.
//Without this the outer pages render initially and then stay static
if (mNeedsRedraw) invalidate();
}
@Override
public void onPageSelected(int position) { }
@Override
public void onPageScrollStateChanged(int state) {
mNeedsRedraw = (state != ViewPager.SCROLL_STATE_IDLE);
}
}
@akhr
Copy link

akhr commented Apr 16, 2014

Hi devunwired,
Just worked as expected. Thank you very much :)

@mattsca
Copy link

mattsca commented May 1, 2014

Hi,
I have an Android Tablet Asus, 10", with Android 4.2.1 and your code doesn't work. On Samsung S3 work correctly but on tablet the PageView fit exactly the central cell and the right and the left element don't move.
Can anyone help me?
Tnx:)

@MicheleDB
Copy link

Works well as a replacement for Gallery except for determining what items is "clicked". Has anybody implemented an onClickListener? the example shows onPageSelected which is not suitable since it "fires up" on any scroll before you can decide what to click. Please supply example.

@gavelez
Copy link

gavelez commented Jun 5, 2014

@nessusu did you find any solution to your issue? It will be very helpful if you did it.

@ggilrong
Copy link

Thanks you!!

@malinkang
Copy link

Thanks you!!

@marcospaulo
Copy link

I still have a problem with setOnClickListener for the pages. I want to be able to click on a page and drag the other view pager. But it's not working, it's always calling the setOnClickListener from the right. page. Anyone with the same problem? Did someone find a solution?

I saw some old comments like the one from @frankjoshua and @marckaraujo with the same problem, but I didnt see any solution for it.

Thank you in advance.

@traninho
Copy link

traninho commented Nov 4, 2014

Has anyone solved on click events on the edge items?

@psparago
Copy link

First off, thank you very much for posting this code, Dave.

Without going into detail about my ViewPager implementation, the PagerContainer works as expected on my KitKat (4.4.2) tablet, but not on my other 2 Jelly Bean tablets (4.2.2). On Jelly Bean, I'm getting the behavior where the "outer" pages stay static. This is the behavior that the invalidate() call in onPageScrolled is supposed to fix. Has anyone else experienced this? Is there a workaround?

@scottmeng
Copy link

I used a simple trick to block on click event from getting fired up when tapping on edge items.

Below is the helper function I added to PagerContainer class

    private boolean isInNonTappableRegion(float oldX, float newX) {
        int tappableWidth = mPager.getWidth();
        int totalWidth = getWidth();

        if (oldX < (totalWidth - tappableWidth) / 2 && newX < (totalWidth - tappableWidth) / 2) {
            return true;
        }
        if (oldX > (totalWidth + tappableWidth) / 2 && newX > (totalWidth + tappableWidth) / 2) {
            return true;
        }
        return false;
    }

I also modified its onTouchEvent handler:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //We capture any touches not already handled by the ViewPager
        // to implement scrolling from a touch outside the pager bounds.
        switch (ev.getAction()) {
            // if tapping happens on edge items
            case MotionEvent.ACTION_UP:
                if (isInNonTappableRegion(mInitialTouch.x, ev.getX())) {
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                }
                ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
                break;
            case MotionEvent.ACTION_DOWN:
                mInitialTouch.x = (int)ev.getX();
                mInitialTouch.y = (int)ev.getY();
            default:
                ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
                break;
        }

        return mPager.dispatchTouchEvent(ev);
    }

@bblackbelt
Copy link

Hi all, and thanks for sharing your code. Did somebody managed to make the onClick behave like the gallery?, making the clicked item move to the central position?

@worawees
Copy link

The modified version of @scottmeng, which will return the delta item position should be changed from the current item of the pager.
deltaPosition == 0 click on tappable area
deltaPosition != 0 click on non-tappable area

We can use this value to do 2 things

  • set currentItem to the new position (pager.setCurrentItem(pager.getCurrentItem() + daltaPosition)
  • activate on item click action, with the right data item index
    private int isInNonTappableRegion(float oldX, float newX) {
        int tappableWidth = mPager.getWidth();
        int totalWidth = getWidth();
        int nonTappableWidth = (totalWidth - tappableWidth) / 2;
        if (oldX < nonTappableWidth && newX < nonTappableWidth) {
            return -(int) Math.ceil((nonTappableWidth - newX) / (float) tappableWidth);
        }
        nonTappableWidth = (totalWidth + tappableWidth) / 2;
        if (oldX > nonTappableWidth && newX > nonTappableWidth) {
            return (int) Math.ceil((newX - nonTappableWidth) / (float) tappableWidth);
        }
        return 0;
    }

@MitchDroid
Copy link

Hi @worawees, I saw your modified version of @scottmeng, but could you tell me how or where I can implement this method isInNonTappableRegion()? in @scottmeng method it returns a boolean but in your methos it return an int and I don´t know how to use it.

Thanks for your help.

Regards.

@FaizanMubasher
Copy link

Is there any way to implement it Vertically? I have to show it in Landscape mod but it shouldn't take whole screen.

@lemartva
Copy link

Thank you!

@forsubhi
Copy link

click left item cause clicking the right one of nexus 7, why?

I saw some old comments like the one from @frankjoshua,@marcospauloand and @marckaraujo with the same problem, but I didnt see any solution for it.

@lysrt
Copy link

lysrt commented Aug 1, 2015

Hi @MitchDroid,

You can use @worawees code this way:

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_UP:
                int delta = isInNonTappableRegion(mInitialTouch.x, ev.getX());
                if (delta != 0) {
                    ev.setAction(MotionEvent.ACTION_CANCEL);
                    mPager.setCurrentItem(mPager.getCurrentItem() + delta );
                }
                ev.offsetLocation(mCenter.x - mInitialTouch.x, mCenter.y - mInitialTouch.y);
                break;
                ...

A click on a side item will bring it to the center, and it works like a charm :)

@amitprabhudesai
Copy link

@nessusu The blank area is because the ViewPager gravity is set as center_horizontal. To fix this, you will need to set the gravity to start for the first page, end for the last page. To do this you can update the layout params for the ViewPager in the onPageSelected event.
Something like so:

            @Override
            public void onPageSelected(int position) {
                FrameLayout.LayoutParams viewPagerLayoutParams
                        = (FrameLayout.LayoutParams) viewPager.getLayoutParams();
                if (0 == position) {
                    viewPagerLayoutParams.gravity = Gravity.START;
                } else if (getNumItemsInScrollableContainer()-1 == position) {
                    viewPagerLayoutParams.gravity = Gravity.END;
                } else {
                    viewPagerLayoutParams.gravity = Gravity.CENTER_HORIZONTAL;
                }
                viewPager.requestLayout();
            }

HTH.

@francieli-lima
Copy link

It's working just great! Thanks.

@yarolegovich
Copy link

Hi, when I use this solution with PagerTransformer - views inside viewpager are not updated until page is scrolled. Any advices on how can I solve it?

@openkwaky
Copy link

Same problem as yarolegovich. Any clue ?

@alin-rosu
Copy link

How can I change the text of the selected page?
I added a OnPageChangeListener, but how can I access the textview from my selection, and also the others?

Copy link

ghost commented Mar 9, 2016

thank you for your code but there is problem in PagerContainer

protected void onFinishInflate() needs Overriding method should call super

can anyone explain this?

@MrThiago
Copy link

Thanks guys for this.

Is there a way to make the next item and the preview item blurred, then when on focus remove the blur ?
thanks

@gSrikar
Copy link

gSrikar commented Jul 25, 2016

@boshraViva Did you called super.onFinishInflate()? and did you figured out What it does?

@MrThiago
Copy link

@amitprabhudesai

Which method is this getNumItemsInScrollableContainer() ?

@MrThiago
Copy link

Guys, I have a problem.

I have 3 items visible.
If I select the 3rd item it will return item on 2nd position.
Do you guys have this problem?

@therahulkumar
Copy link

Guys, how are you taking care of the starting and end page of the pager, as in the facebook's carousel ads

screenshot from 2016-11-24 18 54 36

screenshot from 2016-11-24 18 55 03

screenshot from 2016-11-24 18 55 30

@adityasd
Copy link

adityasd commented Jan 5, 2017

I used two viewpager..in first pager i have only images of product. now if user swipe image from pager 1 then in pager 2 i want to set its different prices..how to do that can any one help?
http://stackoverflow.com/questions/41217237/viewpager-not-getting-last-item

@jimitpatel
Copy link

jimitpatel commented Jan 11, 2017

@PrinceRK I am also wondering same. Was checking this code, and surprisingly it is not working for me at all.

@devunwired is there any updates on this code you made. Code seems workable but still it's not working in my scenario. I have ViewPager inside RecyclerView. So I have to use wrap_content for ViewPager. And have made customization in onMeasure to get wrapped content. Then I used your piece of code for getting preview of previous and next view. Unfortunately I am not getting it. I don't know what I am missing. I will give CustomViewPager code over here. In that there is also vertical swipe related code, but that is what I am not using in the discussed scenario. Just wrapping the height. isWrap is true in below code

/**
 * Created by Jimit Patel on 29/07/15.
 */
public class CustomViewPager extends ViewPager {

    private static final String TAG = CustomViewPager.class.getSimpleName();

    public static final int HORIZONTAL = 0;
    public static final int VERTICAL = 1;

    public static final int NONE = 0;
    public static final int HEIGHT = 1;
    public static final int WIDTH = 2;

    private boolean isPagingEnabled, isSwipeable, isSquare, isWrap;
    private int mSwipeOrientation;
    private ScrollerCustomDuration mScroller = null;
    private double heightRatio, widthRatio;
    private int mCalculationType;

    private View mCurrentView;

    public CustomViewPager(Context context) {
        super(context);
        this.isPagingEnabled = true;
        this.isSwipeable = true;
        isSquare = false;
        mCalculationType = NONE;
        heightRatio = 0;
        widthRatio = 0;
        mSwipeOrientation = HORIZONTAL;
        postInitViewPager();
    }

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        setIsSquare(context, attrs);
        setDimensionRatio(context, attrs);
        setSwipeOrientation(context, attrs);
        this.isPagingEnabled = true;
        this.isSwipeable = true;
        postInitViewPager();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (!this.isPagingEnabled || !this.isSwipeable)
            return false;
        return super.onTouchEvent(mSwipeOrientation == VERTICAL ? swapXY(event) : event);
    }

    @Override
    public boolean onInterceptHoverEvent(MotionEvent event) {
//        return super.onInterceptHoverEvent(event);
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (!this.isPagingEnabled || !this.isSwipeable)
            return false;
        else if (mSwipeOrientation == VERTICAL) {
            boolean intercepted = super.onInterceptHoverEvent(swapXY(event));
            swapXY(event);
            return intercepted;
        }
        return super.onInterceptTouchEvent(event);
    }

    public void setPagingEnabled(boolean isPagingEnabled) {
        this.isPagingEnabled = isPagingEnabled;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        boolean isDefault;
        int width, height;
        switch (mCalculationType) {
            case WIDTH: {
                isDefault = false;
                height = MeasureSpec.getSize(heightMeasureSpec);
                width = (int) ((float) (((float) (heightRatio > 0 ? heightRatio : 1) / (float) (widthRatio > 0 ? widthRatio : 1)) * height));
                break;
            }
            case HEIGHT: {
                isDefault = false;
                width = MeasureSpec.getSize(widthMeasureSpec);
                height = (int) ((float) (((float) (widthRatio > 0 ? widthRatio : 1) / (float) (heightRatio > 0 ? heightRatio : 1)) * width));
                break;
            }
            default: {
                isDefault = true;
                width = widthMeasureSpec;
                height = heightMeasureSpec;
                break;
            }
        }

        if (!isDefault) {
            setMeasuredDimension(width, height);
            measureChildren(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
            super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), (isSquare
                    ? MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY) : MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)));
        } else {
            if (!isSquare && isWrap) {
                if (mCurrentView == null) {
                    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                    return;
                }
                height = 0;
                mCurrentView.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
                int h = mCurrentView.getMeasuredHeight();
                if (h > height) height = h;
                heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            } else {
                super.onMeasure(widthMeasureSpec, isSquare ? widthMeasureSpec : heightMeasureSpec);
            }
        }
    }

    public void measureCurrentView(View currentView) {
        mCurrentView = currentView;
        requestLayout();
    }

    public void setSwipeOrientation(int swipeOrientation) {
        if (swipeOrientation == HORIZONTAL || swipeOrientation == VERTICAL)
            mSwipeOrientation = swipeOrientation;
        else
            throw new IllegalStateException("Swipe Orientation can be either CustomViewPager.HORIZONTAL" +
                    " or CustomViewPager.VERTICAL");
        initSwipeMethods();
    }

    private void setIsSquare(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SquareViewPager);
        boolean isSquare = typedArray.getBoolean(R.styleable.SquareViewPager_square_dimens, false);
        setIsSquare(isSquare);
        typedArray.recycle();
    }

    private void setSwipeOrientation(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomViewPager);
        mSwipeOrientation = typedArray.getInteger(R.styleable.CustomViewPager_swipe_orientation, 0);
        typedArray.recycle();
        initSwipeMethods();
    }

    private void setDimensionRatio(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SquareViewPager);
        int ratioWidth = typedArray.getInteger(R.styleable.SquareViewPager_width_ratio, 0);
        int ratioHeight = typedArray.getInteger(R.styleable.SquareViewPager_height_ratio, 0);
        int calculationType = typedArray.getInteger(R.styleable.SquareViewPager_calculation, 0);
        setDimensionRatio(ratioWidth, ratioHeight, calculationType);
        typedArray.recycle();
    }

    private void setDimensionRatio(int ratioWidth, int ratioHeight, int calculationType) {
        this.widthRatio = ratioWidth;
        this.heightRatio = ratioHeight;
        this.mCalculationType = calculationType;
    }

    public void setIsSquare(boolean isSquare) {
        this.isSquare = isSquare;
    }

    public void setHeightRatio(double heightRatio) {
        this.heightRatio = heightRatio;
    }

    public void setWidthRatio(double widthRatio) {
        this.widthRatio = widthRatio;
    }

    public boolean isSquare() {
        return this.isSquare;
    }

    public boolean isWrap() {
        return isWrap;
    }

    public void setWrap(boolean wrap) {
        isWrap = wrap;
    }

    public void setSwipeEnabled(boolean isSwipeable) {
        this.isSwipeable = isSwipeable;
    }

    /**
     * Override the Scroller instance with our own class so we can change the
     * duration
     */
    private void postInitViewPager() {
        try {
            Field scroller = ViewPager.class.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            Field interpolator = ViewPager.class.getDeclaredField("sInterpolator");
            interpolator.setAccessible(true);

            mScroller = new ScrollerCustomDuration(getContext(),
                    (Interpolator) interpolator.get(null));
            scroller.set(this, mScroller);
        } catch (Exception e) {
        }
    }

    private void initSwipeMethods() {
        if (mSwipeOrientation == VERTICAL) {
            // The majority of the work is done over here
            setPageTransformer(true, new VerticalPageTransformer());
            // The easiest way to get rid of the overscroll drawing that happens on the left and right
            setOverScrollMode(OVER_SCROLL_NEVER);
        }
    }

    /**
     * Set the factor by which the duration will change
     */
    public void setScrollDurationFactor(double scrollFactor) {
        mScroller.setScrollDurationFactor(scrollFactor);
    }

    public int measureFragment(View view) {
        if (view == null)
            return 0;

        view.measure(0, 0);
        return view.getMeasuredHeight();
    }

    /**
     * Determines the height of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @param view        the base view with already measured height
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureHeight(int measureSpec, View view) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            // set the height from the base view if available
            if (view != null) {
                result = view.getMeasuredHeight();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    /**
     * Determines the width of this view
     *
     * @param measureSpec A measureSpec packed into an int
     * @param view        the base view with already measured width
     * @return The height of the view, honoring constraints from measureSpec
     */
    private int measureWidth(int measureSpec, View view) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            // set the height from the base view if available
            if (view != null) {
                result = view.getMeasuredWidth();
            }
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private MotionEvent swapXY(MotionEvent event) {
        float width = getWidth();
        float height = getHeight();

        float newX = (event.getY() / height) * width;
        float newY = (event.getX() / width) * height;

        event.setLocation(newX, newY);
        return event;
    }

    private class VerticalPageTransformer implements ViewPager.PageTransformer {

        @Override
        public void transformPage(View page, float position) {
            if (position < -1) {
                // This page is way off-screen to the left
                page.setAlpha(0);
            } else if (position <= 1) {
                page.setAlpha(1);

                // Counteract the default slide transition
                page.setTranslationX(page.getWidth() * -position);

                // set Y position to swipe in from top
                float yPosition = position * page.getHeight();
                page.setTranslationY(yPosition);
            } else {
                // This page is way off screen to the right
                page.setAlpha(0);
            }
        }
    }
}

And in PagerAdapter, I am using this piece of code to get measurement

@Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);

            if (position != mCurrentPosition) {
                View view = (View) object;
                CustomViewPager pager = (CustomViewPager) container;
                if (null != view) {
                    mCurrentPosition = position;
                    pager.measureCurrentView(view);
                }
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment