0

I have a ViewPager modelled after this example, where I am implementing ViewPager.PageTransformer:

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {    
        if (position < -1) { // [-Infinity,-1)
            view.setAlpha(0);
        } else if (position <= 0) { // [-1,0]

            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);

        } else if (position <= 1) { // (0,1]

            view.setAlpha(1 - position);
            view.setTranslationX(view.getWidth() * -position);
            float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);

        } else { // (1,+Infinity]
            view.setAlpha(0);
        }
    }
}

and it works great, just as shown in the Depth Page Transformer animation shown in the link above.

But, the way it currently works is that views to the right are always below the views on the left. As you scroll right, the views come from below. Scrolling left has the current view going below the new view, which slides in on top.

What I'd like to do is have the current view ALWAYS be on top, and for the new view (left or right) to come in from behind/below.

I've tried adding

view.bringToFront();

to the if(position <= 0) block, but nothing changes. I've also tried adding

view.bringToFront();
view.invalidate();

and

view.bringToFront();
((View)view.getParent()).invalidate();

all to no avail. These are the same approaches taken by similar SO posts that I can find, but all of them have accepted answers and comments saying they work. But mine doesn't!

How can I have the current view ALWAYS be the top-most view, regardless of swipe direction?

EDIT 1 - ADDITIONAL DETAILS

As was mentioned in the comments, I'd need to know if swiped right or left to get the same animation going both ways.

The ViewPager only has three views, and they are presented as a VerticalViewPager, based off this post.

Within the PageTransformer, I have the following:

    @Override
    public void transformPage(View view, float position) {

        if (position < -1) { // [-Infinity,-1)
            view.setAlpha(0);
        }
        else if(position <= 0){
            if(view == first){
                view.setAlpha(1 - Math.abs(position));
                view.setTranslationX(view.getWidth() * -position);
                view.setTranslationY(0);
                float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
            }else if(view == second){
                view.setAlpha(1);
                view.setTranslationX(view.getWidth() * -position);
                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);
            }else if(view == third){
                // Never happen
            }
        }
        else if (position <= 1) { // [0,1]
            if(view == first){
                // Never happen
            }else if(view == second){
                view.setAlpha(1);
                view.setTranslationX(view.getWidth() * -position);
                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);
            }else if(view == third){
                view.setAlpha(1 - position);
                view.setTranslationX(view.getWidth() * -position);
                view.setTranslationY(0);
                float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position));
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
            }
        } else { // (1,+Infinity]
            view.setAlpha(0);
        }

    }

Where first, second and third are type View and set inside of the PagerAdapter:

@Override
public View instantiateItem(ViewGroup container, int position) {
    if(position == 0){
        ... stuff ...
        vvp.first = img;
        ... stuff ...
    }else if(position == 1){
        ... stuff ...
        vvp.second = img;
        ... stuff ...
    }else if(position == 2){
        ... stuff ...
        vvp.third = img;
        ... stuff ...
    }
}

It's a little hacked together, but works great and gets exactly the functionality I'm looking for. EXCEPT for the bringToFront thing that isn't working. But it doesn't work whether I use the transformPage code in the original post, or the more comprehensive version in this edit.

Still unsure how to make second (i.e. the middle view of the three) always on top. I even tried a generic

second.bringToFront();

at the top of the method, but still nothing.

EDIT 2

Just tried view.setZ() but met the following error:

java.lang.NoSuchMethodError: android.view.View.setZ

So that option's out as well.

EDIT 3 - SOLUTION

For my solution to this problem, please see answer below (using getChildDrawingOrder).

That being said, I still cannot get bringToFront to work, and will happily select your answer as the chosen one if you can figure it out.

Thanks again in advance.

Community
  • 1
  • 1
Birrel
  • 4,754
  • 6
  • 38
  • 74

3 Answers3

1

Got it! In case you run into the same problem...

I have a custom class:

public class VerticalViewPager extends ViewPager

This is the same class that implements the transformPage method. From within the constructor of the ViewPager, set the following:

setChildrenDrawingOrderEnabled(true);

which is required for you to override the following method:

@Override
protected int getChildDrawingOrder(int childCount, int i) {
    switch(i){
        case 0: return 1;
        case 1: return 2;
        case 2: return 0;
    }

    return super.getChildDrawingOrder(childCount, i);
}

This method lets you choose the order in which the children views of the ViewPager are drawn. Since I only have three views, I only need to worry about cases 0-2.

Whichever view you draw last (in my case, the second/middle view), will be the top view.

The method argument int i is the drawing iteration, which goes

for(i = 0; i < childCount; i++)

and returns the drawing order.

So just pick out the order you want your views to be drawn in, and have at it.

Note that this only works to put ONE view on top. If you have more than three views, or would always like the current view to be on top, you'll have to find some other way to change the top view dynamically.

EDIT - ADDITIONAL INFO

If you override getChildDrawingOrder, you may receive nullpointerexceptions if your ViewPager destroys the off-screen views. If you have a small number of views, you can add:

viewPager.setOffscreenPageLimit(2);

where the argument is the number of views you have, minus 1. This will prevent those views from being destroyed.

Birrel
  • 4,754
  • 6
  • 38
  • 74
0

For those who are looking for dynamic size, I’ve find a solution for post-KitKat version. Basically what I did is increase the elevation of the current center view. (I'm still working for pre-Lollipop solution)

Here's my code, Set an OnPageChangeListener to your ViewPager and, in OnPageSelected Method

     @Override public void onPageSelected(int position) {

      //Counter for loop
      int count = 0;
      int PAGER_LOOP_THRESHOLD = 2;
      if (position >= PAGER_LOOP_THRESHOLD) {
        count = position - PAGER_LOOP_THRESHOLD;
      }
      do {
        Fragment fragment = (Fragment) adapter.instantiateItem(pager, count);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
            && fragment.getView() != null) {

          //If it's current center position, increase view's elevation. Otherwise, we reduce elevation
          if (count == position) {
            fragment.getView().setElevation(8.0f);
          }
          else {
            fragment.getView().setElevation(0.0f);
          }
        }
        count++;
      } while (count < position + PAGER_LOOP_THRESHOLD);
    }
Vincent Paing
  • 2,308
  • 3
  • 17
  • 29
0

you can just set :

mViewPager.setPageTransformer(false, new DepthPageTransformer());