Skip to content

Instantly share code, notes, and snippets.

@PiMaker
Created July 28, 2016 08:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save PiMaker/0d1572aa532ca67c39259dc5e33aac83 to your computer and use it in GitHub Desktop.
Save PiMaker/0d1572aa532ca67c39259dc5e33aac83 to your computer and use it in GitHub Desktop.
A custom ListView for Xamarin.Android that enables scrolling via moving a DragShadow to the top or bottom.
// File: XamarinScrollableListView.cs
// Created: 27.07.2016
//
// This class represents a custom View. It inherits from ListView but adds automatic Drag and Drop scrolling.
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using Java.Lang;
namespace yournamespace
{
[Register("your.package.name.here.yournamespace.XamarinScrollableListView")]
public sealed class XamarinScrollableListView : ListView
{
private bool isScrolling;
private float lastY;
private int scrollBy;
public XamarinScrollableListView(Context context, IAttributeSet attrs) : base(context, attrs)
{
this.Drag += this.OnDrag;
this.SetOnScrollListener(new OnScrollListener(this));
}
private void OnDrag(object sender, DragEventArgs args)
{
if (args.Event.Action == DragAction.Location)
{
this.lastY = args.Event.GetY();
this.ScrollUpdate(this.lastY);
}
else if (args.Event.Action == DragAction.Ended)
{
this.isScrolling = false;
}
}
private void ScrollUpdate(float y)
{
var height = this.Height;
var scrollBorder = height/3;
var position = (int) y;
this.scrollBy = 0;
if (position < scrollBorder)
{
this.scrollBy = -(scrollBorder - position);
}
else if (position > height - scrollBorder)
{
this.scrollBy = position - (height - scrollBorder);
}
if (this.scrollBy == 0)
{
this.isScrolling = false;
return;
}
// Math incoming: A quadratic function with some division eases to smooth out scroll speed
this.scrollBy = (int) (Math.Pow(this.scrollBy/7d, 2)/(scrollBorder/10d))*(this.scrollBy < 0 ? -1 : 1);
if (this.isScrolling)
{
this.SmoothScrollBy(this.scrollBy, 0);
}
this.isScrolling = true;
}
private class OnScrollListener : Object, IOnScrollListener
{
private readonly XamarinScrollableListView super;
private int prevFirstVisibleItem;
private int prevLastVisibleItem;
public OnScrollListener(XamarinScrollableListView super)
{
this.super = super;
}
public void OnScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)
{
var lastVisibleItem = firstVisibleItem + visibleItemCount - 1;
View child = null;
if (firstVisibleItem != this.prevFirstVisibleItem)
{
child = this.super.GetChildAt(0);
}
if (this.prevLastVisibleItem != lastVisibleItem)
{
child = this.super.GetChildAt(this.super.ChildCount - 1);
}
if (child != null)
{
// Workaround to set newly scrolled items as valid drop targets
child.Visibility = ViewStates.Invisible;
child.Visibility = ViewStates.Visible;
}
this.prevLastVisibleItem = lastVisibleItem;
this.prevFirstVisibleItem = firstVisibleItem;
}
public void OnScrollStateChanged(AbsListView view, ScrollState scrollState)
{
if (this.super.isScrolling)
{
this.super.SmoothScrollBy(this.super.scrollBy, 0);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment