androidandroid-viewpagerandroid-pagetransformer

Centered View Pager with next and previous preview and pagetransformer


I am implementing a ViewPager which shows next and previous items and also animates page transitions using ViewPager.PageTransformer. It has a zoom out and fade out effect. For showing next and previous, I am using negative PageMargin, padding on viewpager and clipToPadding as false. This is how it looks like

enter image description here

Which is what I want. The problem is that PageTransform uses left edge as the reference i.e. it shows maximum zoom when image touches left edge instead of when it is in center. To fix this, I used an offset in the code as follows

viewPager.setPageTransformer(false, new ViewPager.PageTransformer() {
        @Override
        public void transformPage(View page, float position) {
            if (position < -1) {
                page.setAlpha(0);
            } else if (position <= 1) {
                float scaleFactor = Math.max(0.7f, 1 - Math.abs(position - 0.14285715f));
                page.setScaleX(scaleFactor);
                page.setScaleY(scaleFactor);
                page.setAlpha(scaleFactor);
            } else {
                page.setAlpha(0);
            }
        }
    });

I logged the position value when image is in center and found the offset. But this offset only works on xxhdpi devices. On xxxhdpi, value comes out to be 0.12068965f. Also, the offset changes when I change the padding on ViewPager. Also, the preview size of next and previous changes with dpi.

Question

How can I calculate the padding, margin and especially offset to keep consistent behaviour across different dpis?

Code

Here is my layout where I am adding viewpager and its padding

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:clipToPadding="false"
        android:paddingLeft="40dp"
        android:paddingRight="40dp">

    </android.support.v4.view.ViewPager>

    <Button
        android:id="@+id/swiper"
        android:text="Animate"
        android:layout_centerInParent="true"
        android:layout_below="@id/viewPager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

Here is the viewpager initialisation code:

final ViewPager viewPager = ((ViewPager) findViewById(R.id.viewPager));
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 120, r.getDisplayMetrics());
viewPager.setPageMargin((int) (-1 * px));
viewPager.setOffscreenPageLimit(5);

Solution

  • This is what I ended up with:

    public class ZoomFadeTransformer implements ViewPager.PageTransformer {
    
        private float offset = -1;
        private float paddingLeft;
        private float minScale;
        private float minAlpha;
    
        public ZoomFadeTransformer(float paddingLeft, float minScale, float minAlpha) {
            this.paddingLeft = paddingLeft;
            this.minAlpha = minAlpha;
            this.minScale = minScale;
        }
    
        @Override
        public void transformPage(View page, float position) {
            if (offset == -1) {
                offset = paddingLeft / page.getMeasuredWidth();
            }
            if (position < -1) {
                page.setAlpha(0);
            } else if (position <= 1) {
                float scaleFactor = Math.max(minScale, 1 - Math.abs(position - offset));
                page.setScaleX(scaleFactor);
                page.setScaleY(scaleFactor);
                float alphaFactor = Math.max(minAlpha, 1 - Math.abs(position - offset));
                page.setAlpha(alphaFactor);
            } else {
                page.setAlpha(0);
            }
        }
    }
    

    and I set it as

    viewPager.setPageTransformer(false, new ZoomFadeTransformer(viewPager.getPaddingLeft(), MIN_SCALE, MIN_ALPHA));