I am implementing ViewPager Transformer where, Text Alpha should slowly transformed from 0.3f(Unselected Page) - 1.0f(Selected Page) as we are swiping in viewpager.
Here is the Desired output. Viewpager Transformation image
As I am new to Android, I tried following approach, basically I am confused in what formula to apply.
public class AlphaTextTransformer implements ViewPager.PageTransformer {
public void transformPage(#NonNull View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0.3 f);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(0.3 f);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(0.3 f + Math.abs(position));
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0.3 f);
}
}
}
I show 3 pages in screen at a time.Left & Right page are partially shown with Text alpha value to 0.3f.Center page text has alpha value of 1. I don't want to immediately set alpha value to 1, but rather it will be progressive as we perform sliding. Can someone please help me with correct pointer? I would really appreciate if someone can help me with Sample code.
Thanks.
Did you check the value of postion with breakpoint or log? Maybe it does not correspond to your expected value?
You should check on this library's code to see how it works, it's with pagetransformer
Related
What is the below code in the Android Developper's Official Tutorial, Using ViewPager for Screen Slides?
public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
//...
public void transformPage(View view, float position) {
//...
if (position < -1) { // **[-Infinity,-1)
//...
} else if (position <= 1) { // [-1,1]
//...
} else { // (1,+Infinity]
//...
}
}
}
The meaning of these comments is more or less:
if (position < -1) { // here go all 'position' values lesser than -1
//...
} else if (position <= 1) { // 'position' in range from -1 to 1, including both -1 and 1
//...
} else { // and then all 'position' values greater than 1 go here
//...
}
If you are looking for an "infinity" value for Float type in Android then there are Float.NEGATIVE_INFINITY and Float.POSITIVE_INFINITY values representing them.
Infinity means that the actual view is way off screen left (negative infinity) or right (positive infinity), so you don't need to apply a scale factor, just set the alpha value to 0
You have to understand that this is a code used to describe the view transition when swiping through the ViewPager fragments, so, when the view is still on screen, you apply the scaleFactor and when it's off, just set it to invisible, or alpha 0.
In the example on the site, the comment below actually explains the behaviour
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
}
What the negative infinity to -1 describes is the position parameters limits for the if block to pass (i.e. be true). In the case -Infinity < position < -1 this code will execute, which means the code below wille execute at the following points:
else if (position <= 1) { // [-1,1]
//true for when position is less than or equal to 1
//but only run when code is greater than or equal to -1
//as this is an else-if
} else { // [1,+Infinity]
//true for when position is greater than 1
}
The position variable accounts for the positioning of the fragment that is to be transformed.
So from this you can see that the limit of negative infinity and -1 means that the actual view is off screen in the left direction. It is off screen in the right direction when position is greater than one and on screen when neither of these conditions are satisfied.
Is it possible to setup different PageTransformers for different fragments in ViewPager?
I would like to use ZoomOutPageTransformer when the user swipes between (0, 1) and (n-1, n) fragments of ViewPager with n fragments and use the default transitions for all other cases.
What I tried:
Detect the current page with mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { ... }) and apply different transitions here. But, for example, if the user is on the first page - how to set ZoomOutPageTransformer only for the case when he will swipe to the left (0 page) and do it before animation is started?
I had a less similar requirement wherein I had applied Depth page transformer to all my pages by default and on tap of a button I changed it to Vertical page transformer and then back to Depth page. (A lil wierd but thats how it was)
I did the below:
Applied viewPager.setPageTransformer(true, new DepthPageTransformer()); for all the pages.
onClick I changed it to viewPager.setPageTransformer(true, new VerticalPageTransformer());. Now here's the catch in-order for this to function smoothly I had to use a handler in order to change the page a little later to ensure VerticalPager takes effect. I achieved this by calling viewPager.setCurrentPosition(viewPager.getCurrentItem() + 1)
After this inside onPageChangeListener's onPageSelected method I changed it back to viewPager.setPageTransformer(true, new DepthPageTransformer()); again inside a handler to ensure it's done after my vertical animation is completed.
Note - I had also accessed ViewPager's scroller to achieve the above to define my own speed for the page change.
Let me know if this makes sense, since you need it to be done based on page numbers. If it does I will share my code. :)
This is quite old, but wish it's not too late for somebody
Step 1:
Create custom ViewPager which extends from ViewPager.
Step 2:
Determine which Fragment positions (ViewPager page integers) you need to have a unique PageTransformer for them. Then add these indices into a list within the custom ViewPager.
Step 3:
Override onPageScrolled(int position, float offset, int offsetPixels):
Check if the position is in your list of indices, if so, then you have to create a Custom PageTransformer object; and then utilize the ViewPager setPageTransformer() to change the page transformation.
For positions other than that, then you've to set the default PageTransformer (or you can also you another custom PageTransformer object if you wish.
Code
public class CustomViewPager extends ViewPager {
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
PageTransformer mDepthPageTransformer = new PageTransformer() {
private static final float MIN_SCALE = 0.75f;
public void transformPage(#NonNull View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0f);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1f);
view.setTranslationX(0f);
view.setScaleX(1f);
view.setScaleY(1f);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0f);
}
}
};
PageTransformer mDefaultPageTransformer = new PageTransformer() {
#Override
public void transformPage(#NonNull View page, float position) {
}
};
#Override
protected void onPageScrolled(int position, float offset, int offsetPixels) {
List<Integer> pos = new ArrayList<>();
pos.add(0);
pos.add(n - 1); // n is the no. of pages as in the question
if (pos.contains(position)) {
this.setPageTransformer(true, mDepthPageTransformer);
} else {
this.setPageTransformer(true, mDefaultPageTransformer);
}
super.onPageScrolled(position, offset, offsetPixels);
}
}
Documentation
Slide between fragments using ViewPager
setPageTransformer() method
Note: mDepthPageTransformer used in my example is taken from the above mentioned documentation.
Inspite Of using mViewPager.setOnPageChangeListener , a single PageTransformer with different animations can help.
Have a look at first paragraph here ,
http://developer.android.com/training/animation/screen-slide.html#pagetransformer
Not sure what is going on here but I have implemented the Zoom features on an image-gallery in a ViewPager, ImagePagerAdapter using TouchImageView. On all my other phones and tablets the code works perfect, for zooming and page flipping. On 3 of our Motorola Razrs the Zooming acts funny on the first image. By funny I mean that when you try and zoom nothing happens on picture #1. But if you pan to the next image the zooming from image 1 has affect image 2. If you flip back to image 1 it still doesn't zoom. If you pan to image #3 then back to #1 the first image will zoom just fine. Not sure how to tackle this issue or even debug it. I have added custom animation, DepthPageTransformer, from the examples in API. This is the culprit, but not sure what in the code is causing this behavior.
import android.annotation.SuppressLint;
import android.support.v4.view.ViewPager.PageTransformer;
import android.view.View;
public class DepthPageTransformer implements PageTransformer {
private static final float MIN_SCALE = 0.55f;
// suppress compiler warning...lower Api use standard slide in but
// APIs higher than 10 will use the transform page function
#SuppressLint("NewApi")
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
view.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
}
}
}
Apparently the animation was causing this weird behavior. Not sure why but until I figure it out I had to remove the setting of this animation.
galleryViewPager.setClickable(true);
//galleryViewPager.setAnimation(new DepthPageTransformer());
galleryViewPager.setOnClickListener(new OnClickListener() {...
Now the fling animation is using the standard. Must be the way the Motorola Razr implements the adapter fling animation that messes up the listener order.
I have a ViewPager in which the pages contain ListViews.
Everything works fine and my viewPAger as well as ListViews work as expected : it is possible to swipe from page to page, and the listviews scroll vertically as they should.
Now I wanted to add a PageTransformer to smooth out paging anbd I used the ZoomOutPageTransformer offered in the google docs.
Now I have a nice animation when swiping between views but the Lists are not scrollable anymore.
Here's the code :
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
LayoutInflater inflater = LayoutInflater.from(getActivity());
viewPager = (ViewPager) view.findViewById(R.id.bookMenuPager);
viewPager.setPageTransformer(false, new ZoomOutPageTransformer());
pagerAdapter = new MenuPagerAdapter();
viewPager.setAdapter(pagerAdapter);
}
class MenuPagerAdapter extends PagerAdapter{
#Override
public int getCount() {
return 3; //change this as needed
}
#Override
public boolean isViewFromObject(View view, Object o) {
return view.equals( o );
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
LayoutInflater inflater = (LayoutInflater) collection.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(position == 0){
if(!rootMenuAdded){
viewPager.addView(rootMenucont, 0);
rootMenuAdded = true;
}
return rootMenucont;
}else if(position == 1){
if(!level1MenuAdded){
viewPager.addView(level1MenuCont, 0);
level1MenuAdded = true;
}
return level1MenuCont;
}else if(position == 2){
if(!level2MenuAdded){
viewPager.addView(level2MenuCont, 0);
level2MenuAdded = true;
}
return level2MenuCont;
}
//we got a problem houston
return null;
}
}
and the layout for a page :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/level1MenuCont"
android:layout_height="match_parent"
android:layout_width="match_parent"
>
<ListView
android:id="#+id/level1Menu"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#f02bb6"
>
</ListView>
</RelativeLayout>
What can I do to have my lists scrolling as expected ? What does the PageTransformer break in my ListView so that it wont scroll anymore?
Is this a known bug?
Thanks for any help :)
I think I have found a workaround for this issue.
After some investigation, I think this only happens if you apply a PageTransformer that changes the coordinates of the Views so they are all on top of each other (the two example transformers do exactly this).
When you swipe in the direction such as the NEW VIEW has a Z-index LOWER than the OLD VIEW (normally a swipe backwards), what happens with those transformers is that the OLD VIEW is on top of the NEW VIEW, with Alpha==0, and is the one that later on gets the "ghost" touches.
Unfortunately, the solution by #ngatyrauks bringToFront() didn't work for me (although it definitely should).
However, I have tweaked the transformer so invisible views are changed its visibility to "GONE". And this does the trick.
I have yet to investigate if this Visibility change has any side effects (A GONE view will return null and zeros in layout etc, so maybe this breaks other things inside ViewPager), but so far it's working perfect.
I post here a tweaked DepthPageTransformer (the same in the docs) with these changes. Hope it helps anybody!
package com.regaliz.gui.fx;
import android.util.Log;
import android.view.View;
import android.support.v4.view.ViewPager;
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static final String TAG="DepthTransformer";
private static float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
Log.d(TAG, "VIew "+view+" Position: "+position);
if (position <= -1) { // [-Infinity,-1) ] ***
// RLP> I Changed to include "-1" as well: When position is -1, the view is not visible
// This page is way off-screen to the left.
view.setAlpha(0);
Log.d(TAG, "VIew "+view+" Position: "+position+", way left");
view.setVisibility(View.GONE);
} else if (position <= 0) { // [ (-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
if (position==0) {
Log.d(TAG, "View "+view+" focused now?");
}
if (view.getVisibility()!=View.VISIBLE)
view.setVisibility(View.VISIBLE);
} else if (position <= 1) { // (0,1]
// Fade the page out.
view.setAlpha(1 - position);
// Counteract the default slide transition
// I THINK THIS IS WHAT BREAKS EVERYTHING
// ViewPager normally has the views one after another, but this makes all views on top
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
if (position==1) {
Log.d(TAG, "View "+view+" invisible now?");
view.setVisibility(View.GONE);
// we totally hide the view. This seems to solve focus issue
} else {
if (view.getVisibility()!=View.VISIBLE)
view.setVisibility(View.VISIBLE);
}
} else { // (1,+Infinity]
// This page is way off-screen to the right.
view.setAlpha(0);
// we totally hide the view. This seems to solve focus issue
// I have to check for strange side-effects, but so far I found none :)
view.setVisibility(View.GONE);
Log.d(TAG, "VIew "+view+" Position: "+position+", way right");
}
}
}
Here is the detail of the reason
after 4.1 that the framework respects a custom child drawing order as implied Z-ordering for dispatching touch events. If your views overlap after this page transformation they may not receive touch events in the expected order on older platform versions. Check which view is receiving the touch events to be certain.
If this is what you are seeing you have a few options:
Enforce the desired ordering as you add/remove child views in your PagerAdapter
Remove the X translation applied by the PageTransformer when a page is no longer fully visible - i.e. the "position" parameter reports a full -1 or 1.
And here is my solution
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
if (position <= -1 || position >= 1) { // [-Infinity,-1) ] ***
// [-Infinity,-1] or [1,+Infinity]
// This page is way off-screen to the left or way off-screen to the right.
view.setAlpha(0);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position <= 0) { // [ (-1,0]
// Use the default slide transition when moving to the left page
view.setAlpha(1);
view.setTranslationX(0);
view.setScaleX(1);
view.setScaleY(1);
} else if (position < 1) {
// (0,1)
// Fade the page out.
view.setAlpha(1 - position);
view.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
view.setScaleX(scaleFactor);
view.setScaleY(scaleFactor);
}
}
ref link: https://code.google.com/p/android/issues/detail?id=58918
I don't know if you got this working, but I have the same issue, with a PageDepthTransformer. I'm using a gridview though, the scrolling works, however my subsequent fragments seem to have focus and my top level Fragment doesn't register the correct onClick() events.
My work around for this was to add global layout listener to viewPager and bring the current view to the front
viewPager.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
View view = viewPager.getChildAt(currentFragmentPosition);
if (view != null) {
view.bringToFront();
}
}
)};
This seems like hackery to me, and I haven't quite got this working on rotation. But hopefully it might help you.
friends....
I got a problem with animation in betwen versions...So i will give description about the requirements of my app and problem im facing
1.My consists of animation which consists viewpager.
2.And this animation must work in lower versions such as 2.2.
3.So for this i have found a awsome library ninoldandroid.
4.I have used animation proxy for my animation.
5.It waa working fine in 4.2.
6.But when coming to 2.2 the animation was not working and the viewpager was moving with its default feature.
My code for animation is...
public class DepthPageTransformer implements ViewPager.PageTransformer {
private static final float MIN_SCALE = 0.75f;
public void transformPage(View view, float position) {
int pageWidth = view.getWidth();
AnimatorProxy proxy = AnimatorProxy.wrap(view);
if (position < -1) { // [-Infinity,-1)
// This page is way off-screen to the left.
proxy.setAlpha(0);
} else if (position <= 0) { // [-1,0]
// Use the default slide transition when moving to the left page
proxy.setAlpha(1);
proxy.setTranslationX(0);
proxy.setScaleX(1);
proxy.setScaleY(1);
} else if (position <= 1) { // (0,1]
// Fade the page out.
proxy.setAlpha(1 - position);
// Counteract the default slide transition
proxy.setTranslationX(pageWidth * -position);
// Scale the page down (between MIN_SCALE and 1)
float scaleFactor = MIN_SCALE
+ (1 - MIN_SCALE) * (1 - Math.abs(position));
proxy.setScaleX(scaleFactor);
proxy.setScaleY(scaleFactor);
} else { // (1,+Infinity]
// This page is way off-screen to the right.
proxy.setAlpha(0);
}
}
}
Can u tell what i have to implement to make this anim ation work in 2.2.
If u feel that the question is insuffient please let me know..Thnaq
There is a Animated view pager by jfeinstein JazzyViewPager which is used by nineoldandroid. which work from 2.2.
(Sorry for late replay)