Xamarin android C# ScrollView OnScrollChanged event

4k views Asked by At

In Xamarin Android how can we extend the ScrollView in order to use the protected OnScrollChanged event?

Specifically, How do we extend the ScrollView to allow an EventHandler to be registered to the OnScrollChanged event? What other methods of ScrollView need to be implemented in a class that extends ScrollView?

Reasons:

The Android ScrollView does not have a way to listen for a scroll event. There have been various questions regarding how to extend ScrollView in native Android Java, however, there is not a question and answer addressing how to apply this to Xamarin.

2

There are 2 answers

0
Joel Anderson On BEST ANSWER

In order to extend ScrollView in this way we should implement 3 constructors

public UsefulScrollView(Context context)
public UsefulScrollView(Context context, IAttributeSet attrs)
public UsefulScrollView(Context context, IAttributeSet attrs, int defStyle)

We also need to override the OnDraw method

protected override void OnDraw(Android.Graphics.Canvas canvas)

To achieve the functionality of an event that we can respond to when the user scrolls we need to override the OnScrollChanged method.

protected override void OnScrollChanged(int l, int t, int oldl, int oldt)

There are multiple ways to allow event listening and handling, but in order to be consistent with Xamarin we can add a public EventHandler property to our class.

public EventHandler<T> ScrollEventHandler { get; set; }

We will want to pass along the values from OnScrollChanged to the EventHandler, so let's extend EventArgs

public class UsefulScrollEventArgs : EventArgs{
    public int l { get; set; }
    public int t { get; set; }
    public int oldl { get; set; }
    public int oldt { get; set; }
}

Finally, don't forget to initialize our handler in each of our constructors

ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};

Put it all together and it might look something like this

Extended EventArgs class

public class UsefulScrollEventArgs : EventArgs{
    public int l { get; set; }
    public int t { get; set; }
    public int oldl { get; set; }
    public int oldt { get; set; }
}

Extended ScrollView class

public class UsefulScrollView : ScrollView
{
    public EventHandler<UsefulScrollEventArgs> ScrollEventHandler { get; set; }
    public UsefulScrollView (Context context)
    : base(context)
    {
        ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};
    }
    public UsefulScrollView (Context context, IAttributeSet attrs)
    : base(context, attrs) {
        ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};

    }
    public UsefulScrollView(Context context, IAttributeSet attrs, int defStyle)
    : base(context, attrs, defStyle) {
        ScrollEventHandler = (object sender, UsefulScrollEventArgs e) => {};

    }
    protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
    {
        ScrollEventHandler (this, 
                   new UsefulScrollEventArgs ()
                   {l=l,t=t,oldl=oldl,oldt=oldt});
        base.OnScrollChanged (l, t, oldl, oldt);
    }
    protected override void OnDraw(Android.Graphics.Canvas canvas)
    {

    }
}

This Q&A was helpful in figuring this problem out: Scrollview listener is not working in Xamarin for Android?

0
Steven L On

I extended ScrollView but used EventHandlers. One for ScrollChanged, one for ReachedBottom.

This goes in your extended class:

    public delegate void ScrollChangedEventHandler(object sender, EventArgs e);
    public event ScrollChangedEventHandler ScrollChanged;

    public delegate void ScrollReachedBottomEventHandler(object sender, EventArgs e);
    public event ScrollReachedBottomEventHandler ScrollReachedBottom;

    protected override void OnScrollChanged(int l, int t, int oldl, int oldt)
    {
        base.OnScrollChanged(l, t, oldl, oldt);

        if (ScrollChanged != null)
        {
            ScrollChanged.Invoke(this, new EventArgs());
        }

        // Grab the last child placed in the ScrollView, we need it to determinate the bottom position.
        var view = GetChildAt(ChildCount - 1);

        // Calculate the scrolldiff
        var diff = (view.Bottom - (Height + ScrollY));

        // if diff is zero, then the bottom has been reached
        if (diff == 0 && ScrollReachedBottom != null)
        {
            ScrollReachedBottom.Invoke(this, new EventArgs());
        }
    }

You would then simply call

myScrollView.ScrollChanged += delegate { /* your code here */ }

and

myScrollView.ScrollReachedBottom += delegate { /* your code here */ }

enjoy.