visual indication of over scroll in android
Asked Answered
A

8

10

I am trying to add some visual indication, that there are no more pages in the desired fling direction in the ViewPager. However I am struggling to find a place, where to put relevant code.

I have tried extending ViewPager class with following code, but the Toast is not displaying (ev.getOrientation() returns always 0). I have also tried the same with history points, but ev.getHistorySize() returns also 0.

What am I missing?

Class example:

public class CustomViewPager extends ViewPager {

    public CustomViewPager(Context context) {
        super(context);
    }

    public CustomViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * @see android.support.v4.view.ViewPager#onTouchEvent(android.view.MotionEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean result = super.onTouchEvent(ev);

        switch (ev.getAction() & MotionEventCompat.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE:
                if (ev.getOrientation() > 0) {
                    Toast.makeText(getContext(), "left", 0).show();
                }
        }

        return result;
    }
}
Anthropometry answered 27/5, 2012 at 11:19 Comment(0)
I
19

If you look at the v4 support library you will see there's a class used by ViewPager called EdgeEffectCompat (this provides the glow effect when you reach the beginning or end of a view pager in ICS+) If you look at the implementation in the compat library you will see that it has an if-statement to see if the build version is 14+ (ICS) or not. If it is, then it ends up eventually (if you trace long enough) using the normal EdgeEffect class that was inroduced in ICS. Otherwise it uses BaseEdgeEffectImpl which basically has nothing in it.

If you want, you can make your own custom ViewPager that uses EdgeEffect of your own. You can look at the android source code to see how they implemented EdgeEffect here which you can pretty much copy (just make sure to copy the overscroll_edge and overscroll_glow drawables in the AOSP /res/drawable directories to your own project since they are internal to android) or go ahead and create your own version.

Good luck.

(By the way, that's how they create the cool looking edge tilt effect in the launcher menu on ICS... so you can pretty much be as creative as you want with this ;)

Insomuch answered 15/7, 2012 at 3:37 Comment(0)
W
7

I was trying to get the exact same effect that was asked in this question. I struggle with it and then I read @wnafee answer (I couldn't do it with out it).

But then I struggle to implement what was sound pretty simple from the answer. I had so much trouble with implementing it, that I might didn't understand the answer correctly, but there were too many issues of inaccessible APIs since I wasn't working in the same package of the Compatibility library.

After I tried some approaches (none of them succeeded, and they were pretty complicated) I went to a slightly different direction, and now it works like a charm. I used some reflection, for the ones who never used it, don't worry it is really the basic of reflection.

I'm not sure if it's the best solution out there, but it worked for me, so if you would like to use it you are welcome. Please read Wnafee example since it explains some of the stuff that I did.

In order to accomplish this task you should just follow my three parts solution. (Will take you between 3-10 minutes)

Part I:

As Wnafee said I just made my own EdgeEffect class by copy paste the source code from here,

(just make sure to copy the overscroll_edge and overscroll_glow drawables in the AOSP /res/drawable directories to your own project since they are internal to android)

I only did 2 really small changes:

  1. I declare that the class extends EdgeEffectCompat (I called my class EdgeEffectForEarlyVersions). public class EdgeEffectForEarlyVersions extends EdgeEffectCompat. The reason for doing this change is that the mLeftEdge and mRightEdge are of the type EdgeEffectCompat.
  2. At the first line of the constructor of "my" new class I added a call to the parent constructor super(context);. Since there is no default constructor to EdgeEffectCompat you have to Explicitly call the constructor.

Part II

Besides that I wrote the another function. The purpose of the function is that in case of an early version (before ICS) we would like to use the EdgeEffectForEarlyVersions that we just copied. In order to get that purpose I used reflection.

This is the function:

private static void changeEdgeEffectCompactOnEarlyVersions(ViewPager viewPager, Context context)
{
    /* In case that the version is earlier than 14 there is only empty implementation for the edge effect, therefore we change it.
     * for more information look on the following links:
     * 1. https://mcmap.net/q/1043338/-visual-indication-of-over-scroll-in-android
     * 2. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/view/ViewPager.java#ViewPager.0mLeftEdge
     * 3. http://grepcode.com/file/repo1.maven.org/maven2/com.google.android/support-v4/r7/android/support/v4/widget/EdgeEffectCompat.java#EdgeEffectCompat
     */

    if (Build.VERSION.SDK_INT < 14)
    {
        try
        {
            Class<ViewPager> viewPagerClass = ViewPager.class;
            //Get the left edge field, since it is private we used getDeclaredField and not getDeclared 
            Field leftEdge = viewPagerClass.getDeclaredField("mLeftEdge");
            leftEdge.setAccessible(true);
            //Get the right edge field, since it is private we used getDeclaredField and not getDeclared
            Field rightEdge = viewPagerClass.getDeclaredField("mRightEdge");
            rightEdge.setAccessible(true);

            EdgeEffectForEarlyVersions leftEdgeEffect = new EdgeEffectForEarlyVersions(context);
            EdgeEffectForEarlyVersions rightEdgeEffect = new EdgeEffectForEarlyVersions(context);

            //Set the mLeftEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect  
            leftEdge.set(viewPager, leftEdgeEffect);
            //Set the mRightEdge memeber of viewPager not to be the default one, but to be "our" edgeEffect
            rightEdge.set(viewPager, rightEdgeEffect);              
        }
        catch (Exception ex)
        {
            Log.e("refelection", ex.getMessage());
        }

    }
}

Part III

Now all there is left to do, is to call that function after you have the ViewPager Instance and nothing more.

I Hope it will help someone.

Wellman answered 11/8, 2012 at 3:24 Comment(1)
This wont work on API <= 8. (in latest rev 11) using v4 compat lib. Because it uses BaseViewCompatImpl which always returns OVER_SCROLL_NEVER when asked for overscroll mode. In the onDraw() it then skips the drawing of the over scroll...Uncompromising
L
3

wnafee explained the solution well but for the lazy among us, i made an actual working implementation quite some time ago.

https://github.com/inovex/ViewPager3D

And if you just want overscroll take a look here:

https://github.com/inovex/ViewPager3D/issues/1

Lathy answered 11/12, 2012 at 15:8 Comment(0)
R
1

You have a lot of options, you can show a Toast, display a Dialog, make a TextView or image to appear over your UI, etc. Or because you know the amount of View items in the ViewPager, you could add different View at positions 0 and/or n + 1 with the message and make it bounce to the last View that actually contains your data.

You could implement:

viewPager.setOnPageChangeListener(new OnPageChangeListener() {
    public void onPageSelected(int position) {
        //TODO If position is the 0 or n item, add a view at 0 or at n+1 to indicate there is no more pages with data.
    }

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        // TODO Show a Toast, View or do anything you want when position = your first/last  item;
    }

    public void onPageScrollStateChanged(int state) {
    }
});
Romine answered 11/7, 2012 at 20:46 Comment(2)
There is a system-standard glow effect when you try to scroll past the end of a ViewPager, which is the same as when you scroll past the top of bottom of a list. I would like something like that - but it should work on all versions (the current glow is ICS-only). Your suggestions would make for a clunky UI, and if those are really the only options then I will stick with no indication.Wheelbase
I know and its enough for my App, but if @Anthropometry asked, I assume is because he needs something more. :)Romine
T
1

just to complement @goBeepit dev answer when you create your own edgeffect class and you extend from EdgeEffectCompat some methods requires to be boolean. you can change those methods to boolean type and make then return true in any case, this way everything works fine

Traditional answered 4/9, 2012 at 16:59 Comment(0)
F
0

You can overload the setUserVisibleHint(boolean) function in your fragments. Pseudo code:

    void setUserVisibleHint(boolean isVisibleToUser) {
        // If this fragment is becoming visible
        if (isVisibleToUser == true) {
             // Check if it is the last fragment in the viewpager
             if (indexOfThis == getActivity().indexOfLast) {
                // Display right limit reached
                Toast(..., "No more Frags to right",...)
                }
             // Check if it is the first fragment in the viewpager
             else if (indexOfThis == getActivity().indexOfFirst) {
                // Display Left Limit reached
                Toast(..., "No more Frags to left",...)
             }
        }
    }

I have not used this function for this purpose, but have used it for other reasons and it does fire appropriately. Hope this helps...

Friulian answered 13/7, 2012 at 10:14 Comment(1)
This has the same UI implications as Oscar's answer, except that it overrides a method that is not meant to be overridden instead of using a listener, which seems dangerous.Wheelbase
Q
0

I've implemented a bounce back effect based on Renard's ViewPager3D: https://mcmap.net/q/1161096/-android-viewpager-how-to-achieve-the-bound-effect

Quantify answered 2/7, 2013 at 12:22 Comment(0)
A
-1

Usually with ViewPager, one uses a PagerAdapter such as FragmentPagerAdapter or FragmentStatePagerAdapter to flood the ViewPager with contents(your content are going to be views).

Now, when you use a PagerAdapter, you have one method called getCount(), http://developer.android.com/reference/android/support/v4/view/PagerAdapter.html#getCount%28%29 ,which will give you the size of the content.

Since you now, know the size you can easily display a message with an if control statement.

Try this code : http://developer.android.com/reference/android/support/v4/view/ViewPager.html

Note: I dont think you need a custom ViewPager. You will also need to understand Fragments for ViewPager. Look at samples in ApiDemos. Its a great source.

Angelika answered 15/6, 2012 at 9:42 Comment(1)
That was not what he asked. He wanted to know specifically how to do an overscroll-effect like the rubber-band on iOS, which is missing in ViewPager at least until ICS.Pompom

© 2022 - 2024 — McMap. All rights reserved.