Search code examples
c#androidscrollxamarin.android

How to cancel an in-progress scroll while the user is still scrolling?


Background

My team is using an Android tablet as the display for a vehicle system. The display features a dashboard with a "widget carousel" (implemented as a ViewPager) in which are displayed widgets that the user can swipe left or right to change which ones are currently visible on the screen. When the vehicle is stationary, the user should be able to freely scroll within the widget carousel but when the vehicle starts moving, user interactions with the display should be blocked to encourage the user to keep their hands on the vehicle controls.

Problem

If the user is scrolling and then the vehicle starts moving, the in-progress scrolling operation is not interrupted. The user is able to continue scrolling while driving until they release and touch the screen again, at which point the user interaction is properly blocked.

Question

I've browsed a fair number of Stack Overflow posts in search of a solution for this, but most solutions apply only to blocking new scrolling operations, not cancelling one that is already in-progress. Is there a way to cancel an in-progress scroll while the user is still scrolling? I have a solution which worked for my team (which I'll provide as an answer), but would appreciate other possible solutions (or improvements/critiques to the one I'll provide) in case they can help someone else in the future.


Solution

  • Intercept and, based on vehicle motion, either pass on or discard the touch events targeting the widget carousel ViewPager.

    To do this, subclass ViewPager like below and override the OnTouchEvent() method to pass on incoming touch events to any targeted child views if display interactions are currently allowed; otherwise discard the incoming touch events and report to the system that they have been handled.

    public class ViewPagerCustom : ViewPager
    {
        ...
    
        public override bool OnTouchEvent(MotionEvent e)
        {
            return displayInteractionsAllowed ? base.OnTouchEvent(e) : true;
        }
    }