Android Question ViewPager not firing settled state fast enough

wimpie3

Well-Known Member
Licensed User
This is a very annoying problem. When flicking through a ViewPager, the "settled" state fires a second AFTER the page has settled. In other words: the animation is already finished a while before the correct state is fired...

B4X:
#Region Module Attributes
    #FullScreen: False
    #IncludeTitle: True
    #ApplicationLabel: Hello World
    #VersionCode: 1
    #VersionName:
    #SupportedOrientations: unspecified
    #CanInstallToExternalStorage: False
#End Region

Sub Process_Globals
End Sub

Sub Globals
    Private VP As ViewPager: ' https://www.b4x.com/android/forum/threads/viewpager-cleaned-up-viewpager.75342/
    Private PC As PageContainer
End Sub

Sub Activity_Create(FirstTime As Boolean)
    VP.Initialize("VP")
    Activity.AddView(VP,0,0,100%x,100%y)
    PC.Initialize
    For i = 0 To 10
        PC.AddPage("Page " & i)
    Next
    VP.PageContainer = PC
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub VP_PageCreated (Position As Int, Page As VPage)
    If Page.AlreadyCreated = False Then
        Page.Panel.Color = Colors.RGB(Rnd(0,256), Rnd(0,256), Rnd(0,256))
    End If
End Sub

Sub VP_PageScrollStateChanged (State As Int)
    If State=0 Then
        Msgbox ("Now settled, but should have already fired a second earlier","Demo")
    End If
End Sub
 

wimpie3

Well-Known Member
Licensed User
The delay is caused by the animation type that is used by the viewpager. By default it's AccelerateDecelerateInterpolator. This means the animation slows down at the end, giving the impression there is a delay before PageScrollStateChanged is fired. It's fired at the end of the animation, but since the animation goes slow at the end, it gives the impression the event is fired too late.

The solution I've found on the internet is to use a LinearInterpolator for the viewpager:
B4X:
private void setScroller() {
     try {
        Field mScroller;
        mScroller = ViewPager.class.getDeclaredField("mScroller");
        mScroller.setAccessible(true);
        FixedSpeedScroller scroller =
           new FixedSpeedScroller(this.getContext(), new LinearInterpolator());
        mScroller.set(this, scroller);
     } catch (NoSuchFieldException e) {
     } catch (IllegalArgumentException e) {
     } catch (IllegalAccessException e) {
     }
  }
B4X:
private class FixedSpeedScroller extends Scroller {

    private int mDuration = 500;

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

    public FixedSpeedScroller(Context context, Interpolator interpolator) {
        super(context, interpolator);
    }

    public FixedSpeedScroller(Context context, Interpolator interpolator, boolean flywheel) {
        super(context, interpolator, flywheel);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy, int duration) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }

    @Override
    public void startScroll(int startX, int startY, int dx, int dy) {
        // Ignore received duration, use fixed one instead
        super.startScroll(startX, startY, dx, dy, mDuration);
    }

    public void setScrollDuration(int duration) {
        mDuration = duration;
    }
}
However, I've got no idea on how to implement this.

BTW, tabstrib does not seem to have an event called PageScrollStateChanged. Only PageSelected (which is fired BEFORE the animation ends!). Plus you can't remove the tabs.
 
Last edited:
Upvote 0

wimpie3

Well-Known Member
Licensed User
I will repeat my previous post. I've tested TabStripViewPager and the PageSelected appears to be raised at the correct time.

But that is not true! PageSelected is fired a few milliseconds BEFORE THE ANIMATION ENDS.
 
Upvote 0

wimpie3

Well-Known Member
Licensed User
The problem is that I need to do some intensive calculations when PageSelected is fired. Doing so prevents the animation from finishing (there is a small hickup at the end).
 
Upvote 0

wimpie3

Well-Known Member
Licensed User
If I add sleep(200) it works, but that is kind of guesswork, that value will be different from phone to phone (and no, those calculations cannot be optimized).

My original question still remains:
- PageSelected is fired TOO SOON
- PageScrollStateChanged with the IDLE value is called a few milliseconds TOO LATE

I'm afraid the only solution that works is the one given in Java code. But I cannot implement it...
 
Upvote 0
Top