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