diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.Android/Renderers/ListViewRenderer.cs | |
download | xamarin-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.cs | 355 |
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 |