summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs355
1 files changed, 355 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs
new file mode 100644
index 00000000..06a9554c
--- /dev/null
+++ b/Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs
@@ -0,0 +1,355 @@
+using System.ComponentModel;
+using Android.App;
+using Android.Content;
+using Android.Support.V4.Widget;
+using Android.Views;
+using AListView = Android.Widget.ListView;
+using AView = Android.Views.View;
+
+namespace Xamarin.Forms.Platform.Android
+{
+ public class ListViewRenderer : ViewRenderer<ListView, AListView>, SwipeRefreshLayout.IOnRefreshListener
+ {
+ ListViewAdapter _adapter;
+ IVisualElementRenderer _headerRenderer;
+ IVisualElementRenderer _footerRenderer;
+ Container _headerView;
+ Container _footerView;
+ bool _isAttached;
+ ScrollToRequestedEventArgs _pendingScrollTo;
+
+ SwipeRefreshLayout _refresh;
+
+ public ListViewRenderer()
+ {
+ AutoPackage = false;
+ }
+
+ void SwipeRefreshLayout.IOnRefreshListener.OnRefresh()
+ {
+ IListViewController controller = Element;
+ controller.SendRefreshing();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_headerView == null)
+ return;
+
+ if (_headerRenderer != null)
+ {
+ _headerRenderer.ViewGroup.RemoveAllViews();
+ _headerRenderer.Dispose();
+ _headerRenderer = null;
+ }
+
+ if (_footerRenderer != null)
+ {
+ _footerRenderer.ViewGroup.RemoveAllViews();
+ _footerRenderer.Dispose();
+ _footerRenderer = null;
+ }
+
+ _headerView.Dispose();
+ _headerView = null;
+
+ _footerView.Dispose();
+ _footerView = null;
+
+ if (_adapter != null)
+ {
+ _adapter.Dispose();
+ _adapter = null;
+ }
+
+ Element.ScrollToRequested -= OnScrollToRequested;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected override Size MinimumSize()
+ {
+ return new Size(40, 40);
+ }
+
+ protected override void OnAttachedToWindow()
+ {
+ base.OnAttachedToWindow();
+
+ _isAttached = true;
+ _adapter.IsAttachedToWindow = _isAttached;
+ }
+
+ protected override void OnDetachedFromWindow()
+ {
+ base.OnDetachedFromWindow();
+
+ _isAttached = false;
+ _adapter.IsAttachedToWindow = _isAttached;
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
+ {
+ base.OnElementChanged(e);
+
+ if (e.OldElement != null)
+ {
+ e.OldElement.ScrollToRequested -= OnScrollToRequested;
+
+ if (_adapter != null)
+ {
+ _adapter.Dispose();
+ _adapter = null;
+ }
+ }
+
+ if (e.NewElement != null)
+ {
+ AListView nativeListView = Control;
+ if (nativeListView == null)
+ {
+ var ctx = (Activity)Context;
+ nativeListView = new AListView(ctx);
+ _refresh = new SwipeRefreshLayout(ctx);
+ _refresh.SetOnRefreshListener(this);
+ _refresh.AddView(nativeListView, LayoutParams.MatchParent);
+ SetNativeControl(nativeListView, _refresh);
+
+ _headerView = new Container(ctx);
+ nativeListView.AddHeaderView(_headerView, null, false);
+ _footerView = new Container(ctx);
+ nativeListView.AddFooterView(_footerView, null, false);
+ }
+
+ e.NewElement.ScrollToRequested += OnScrollToRequested;
+
+ nativeListView.DividerHeight = 0;
+ nativeListView.Focusable = false;
+ nativeListView.DescendantFocusability = DescendantFocusability.AfterDescendants;
+ nativeListView.OnFocusChangeListener = this;
+ nativeListView.Adapter = _adapter = new ListViewAdapter(Context, nativeListView, e.NewElement);
+ _adapter.HeaderView = _headerView;
+ _adapter.FooterView = _footerView;
+ _adapter.IsAttachedToWindow = _isAttached;
+
+ UpdateHeader();
+ UpdateFooter();
+ UpdateIsSwipeToRefreshEnabled();
+ UpdateIsRefreshing();
+ }
+ }
+
+ protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ base.OnElementPropertyChanged(sender, e);
+
+ if (e.PropertyName == "HeaderElement")
+ UpdateHeader();
+ else if (e.PropertyName == "FooterElement")
+ UpdateFooter();
+ else if (e.PropertyName == "RefreshAllowed")
+ UpdateIsSwipeToRefreshEnabled();
+ else if (e.PropertyName == ListView.IsPullToRefreshEnabledProperty.PropertyName)
+ UpdateIsSwipeToRefreshEnabled();
+ else if (e.PropertyName == ListView.IsRefreshingProperty.PropertyName)
+ UpdateIsRefreshing();
+ else if (e.PropertyName == ListView.SeparatorColorProperty.PropertyName || e.PropertyName == ListView.SeparatorVisibilityProperty.PropertyName)
+ _adapter.NotifyDataSetChanged();
+ }
+
+ protected override void OnLayout(bool changed, int l, int t, int r, int b)
+ {
+ base.OnLayout(changed, l, t, r, b);
+
+ if (_pendingScrollTo != null)
+ {
+ OnScrollToRequested(this, _pendingScrollTo);
+ _pendingScrollTo = null;
+ }
+ }
+
+ void OnScrollToRequested(object sender, ScrollToRequestedEventArgs e)
+ {
+ if (!_isAttached)
+ {
+ _pendingScrollTo = e;
+ return;
+ }
+
+ Cell cell;
+ int position;
+
+ if (Element.IsGroupingEnabled)
+ {
+ var results = Element.TemplatedItems.GetGroupAndIndexOfItem(e.Group, e.Item);
+ if (results.Item1 == -1 || results.Item2 == -1)
+ return;
+
+ TemplatedItemsList<ItemsView<Cell>, Cell> group = Element.TemplatedItems.GetGroup(results.Item1);
+ cell = group[results.Item2];
+
+ position = Element.TemplatedItems.GetGlobalIndexForGroup(group) + results.Item2 + 1;
+ }
+ else
+ {
+ position = Element.TemplatedItems.GetGlobalIndexOfItem(e.Item);
+ cell = Element.TemplatedItems[position];
+ }
+
+ //Android offsets position of cells when using header
+ int realPositionWithHeader = position + 1;
+
+ if (e.Position == ScrollToPosition.MakeVisible)
+ {
+ if (e.ShouldAnimate)
+ Control.SmoothScrollToPosition(realPositionWithHeader);
+ else
+ Control.SetSelection(realPositionWithHeader);
+ return;
+ }
+
+ int height = Control.Height;
+ var cellHeight = (int)cell.RenderHeight;
+ if (cellHeight == -1)
+ {
+ int first = Control.FirstVisiblePosition;
+ if (first <= position && position <= Control.LastVisiblePosition)
+ cellHeight = Control.GetChildAt(position - first).Height;
+ else
+ {
+ AView view = _adapter.GetView(position, null, null);
+ view.Measure(MeasureSpecFactory.MakeMeasureSpec(Control.Width, MeasureSpecMode.AtMost), MeasureSpecFactory.MakeMeasureSpec(0, MeasureSpecMode.Unspecified));
+ cellHeight = view.MeasuredHeight;
+ }
+ }
+
+ var y = 0;
+
+ if (e.Position == ScrollToPosition.Center)
+ y = height / 2 - cellHeight / 2;
+ else if (e.Position == ScrollToPosition.End)
+ y = height - cellHeight;
+
+ if (e.ShouldAnimate)
+ Control.SmoothScrollToPositionFromTop(realPositionWithHeader, y);
+ else
+ Control.SetSelectionFromTop(realPositionWithHeader, y);
+ }
+
+ void UpdateFooter()
+ {
+ var footer = (VisualElement)((IListViewController)Element).FooterElement;
+ if (_footerRenderer != null && (footer == null || Registrar.Registered.GetHandlerType(footer.GetType()) != _footerRenderer.GetType()))
+ {
+ _footerView.Child = null;
+ _footerRenderer.Dispose();
+ _footerRenderer = null;
+ }
+
+ if (footer == null)
+ return;
+
+ if (_footerRenderer != null)
+ _footerRenderer.SetElement(footer);
+ else
+ {
+ _footerRenderer = Platform.CreateRenderer(footer);
+ _footerView.Child = _footerRenderer;
+ }
+
+ Platform.SetRenderer(footer, _footerRenderer);
+ }
+
+ void UpdateHeader()
+ {
+ var header = (VisualElement)((IListViewController)Element).HeaderElement;
+ if (_headerRenderer != null && (header == null || Registrar.Registered.GetHandlerType(header.GetType()) != _headerRenderer.GetType()))
+ {
+ _headerView.Child = null;
+ _headerRenderer.Dispose();
+ _headerRenderer = null;
+ }
+
+ if (header == null)
+ return;
+
+ if (_headerRenderer != null)
+ _headerRenderer.SetElement(header);
+ else
+ {
+ _headerRenderer = Platform.CreateRenderer(header);
+ _headerView.Child = _headerRenderer;
+ }
+
+ Platform.SetRenderer(header, _headerRenderer);
+ }
+
+ void UpdateIsRefreshing()
+ {
+ _refresh.Refreshing = Element.IsRefreshing;
+ }
+
+ void UpdateIsSwipeToRefreshEnabled()
+ {
+ _refresh.Enabled = Element.IsPullToRefreshEnabled && (Element as IListViewController).RefreshAllowed;
+ }
+
+ internal class Container : ViewGroup
+ {
+ IVisualElementRenderer _child;
+
+ public Container(Context context) : base(context)
+ {
+ }
+
+ public IVisualElementRenderer Child
+ {
+ set
+ {
+ if (_child != null)
+ RemoveView(_child.ViewGroup);
+
+ _child = value;
+
+ if (value != null)
+ AddView(value.ViewGroup);
+ }
+ }
+
+ protected override void OnLayout(bool changed, int l, int t, int r, int b)
+ {
+ if (_child == null)
+ return;
+
+ _child.UpdateLayout();
+ }
+
+ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ if (_child == null)
+ {
+ SetMeasuredDimension(0, 0);
+ return;
+ }
+
+ VisualElement element = _child.Element;
+
+ Context ctx = Context;
+
+ var width = (int)ctx.FromPixels(MeasureSpecFactory.GetSize(widthMeasureSpec));
+
+ SizeRequest request = _child.Element.Measure(width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
+ Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(_child.Element, new Rectangle(0, 0, width, request.Request.Height));
+
+ int widthSpec = MeasureSpecFactory.MakeMeasureSpec((int)ctx.ToPixels(width), MeasureSpecMode.Exactly);
+ int heightSpec = MeasureSpecFactory.MakeMeasureSpec((int)ctx.ToPixels(request.Request.Height), MeasureSpecMode.Exactly);
+
+ _child.ViewGroup.Measure(widthMeasureSpec, heightMeasureSpec);
+ SetMeasuredDimension(widthSpec, heightSpec);
+ }
+ }
+ }
+} \ No newline at end of file