Unformat CarouselViewRenderer, recombine files; no code changes
authorChris King <kingces95@gmail.com>
Thu, 31 Mar 2016 19:07:41 +0000 (12:07 -0700)
committerChris King <kingces95@gmail.com>
Fri, 1 Apr 2016 23:46:32 +0000 (16:46 -0700)
/*privat*/ -> #region

Unformat CarouselView.cs

More unformat work

Remove offending /*privates*/

Xamarin.Forms.Core/CarouselView.cs
Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs [deleted file]
Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/IntVector.cs [deleted file]
Xamarin.Forms.Platform.Android/Renderers/ItemViewAdapter.cs [deleted file]
Xamarin.Forms.Platform.Android/Renderers/MeasureSpecification.cs [deleted file]
Xamarin.Forms.Platform.Android/Renderers/MeasureSpecificationType.cs [deleted file]
Xamarin.Forms.Platform.Android/Renderers/PhysicalLayoutManager.cs [deleted file]
Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj

index 62e9393..b0be847 100644 (file)
@@ -6,13 +6,28 @@ namespace Xamarin.Forms
        [RenderWith(typeof(_CarouselViewRenderer))]
        public class CarouselView : ItemsView, ICarouselViewController
        {
-               public static readonly BindableProperty PositionProperty = BindableProperty.Create(nameof(Position), typeof(int), typeof(CarouselView), 0, BindingMode.TwoWay);
-
-               public static readonly BindableProperty ItemProperty = BindableProperty.Create(nameof(Item), typeof(object), typeof(CarouselView), 0, BindingMode.TwoWay);
-
+               public static readonly BindableProperty PositionProperty = 
+                       BindableProperty.Create(
+                               propertyName: nameof(Position), 
+                               returnType: typeof(int), 
+                               declaringType: typeof(CarouselView), 
+                               defaultValue: 0, 
+                               defaultBindingMode: BindingMode.TwoWay
+                       );
+
+               public static readonly BindableProperty ItemProperty = 
+                       BindableProperty.Create(
+                               propertyName: nameof(Item), 
+                               returnType: typeof(object), 
+                               declaringType: typeof(CarouselView), 
+                               defaultValue: null, 
+                               defaultBindingMode: BindingMode.TwoWay
+                       );
+
+               #region Fields
                object _lastItem;
-
                int _lastPosition;
+               #endregion
 
                public CarouselView()
                {
@@ -22,27 +37,46 @@ namespace Xamarin.Forms
                        HorizontalOptions = LayoutOptions.FillAndExpand;
                }
 
-               public int Item
+               #region Private Members
+               object GetItem(int position)
                {
-                       get { return (int)GetValue(ItemProperty); }
+                       var controller = (IItemViewController)this;
+                       object item = controller.GetItem(position);
+                       return item;
                }
+               #endregion
+
+               // non-public bc unable to implement on iOS
+               internal event EventHandler<ItemVisibilityEventArgs> ItemAppearing;
+               internal event EventHandler<ItemVisibilityEventArgs> ItemDisappearing;
 
                public int Position
                {
                        get { return (int)GetValue(PositionProperty); }
                        set { SetValue(PositionProperty, value); }
                }
+               public int Item
+               {
+                       get { return (int)GetValue(ItemProperty); }
+               }
+
+               public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
+               public event EventHandler<SelectedPositionChangedEventArgs> PositionSelected;
+
+               protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
+               {
+                       var minimumSize = new Size(40, 40);
+                       return new SizeRequest(minimumSize, minimumSize);
+               }
 
                void ICarouselViewController.SendPositionAppearing(int position)
                {
                        ItemAppearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position)));
                }
-
                void ICarouselViewController.SendPositionDisappearing(int position)
                {
                        ItemDisappearing?.Invoke(this, new ItemVisibilityEventArgs(GetItem(position)));
                }
-
                void ICarouselViewController.SendSelectedItemChanged(object item)
                {
                        if (item.Equals(_lastItem))
@@ -51,7 +85,6 @@ namespace Xamarin.Forms
                        ItemSelected?.Invoke(this, new SelectedItemChangedEventArgs(item));
                        _lastItem = item;
                }
-
                void ICarouselViewController.SendSelectedPositionChanged(int position)
                {
                        if (_lastPosition == position)
@@ -60,27 +93,5 @@ namespace Xamarin.Forms
                        _lastPosition = position;
                        PositionSelected?.Invoke(this, new SelectedPositionChangedEventArgs(position));
                }
-
-               public event EventHandler<SelectedItemChangedEventArgs> ItemSelected;
-
-               public event EventHandler<SelectedPositionChangedEventArgs> PositionSelected;
-
-               protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
-               {
-                       var minimumSize = new Size(40, 40);
-                       return new SizeRequest(minimumSize, minimumSize);
-               }
-
-               // non-public bc unable to implement on iOS
-               internal event EventHandler<ItemVisibilityEventArgs> ItemAppearing;
-
-               internal event EventHandler<ItemVisibilityEventArgs> ItemDisappearing;
-
-               object GetItem(int position)
-               {
-                       var controller = (IItemViewController)this;
-                       object item = controller.GetItem(position);
-                       return item;
-               }
        }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs b/Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs
deleted file mode 100644 (file)
index 6349b2b..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Linq;
-using Android.Content;
-using Android.Graphics;
-
-namespace Xamarin.Forms.Platform.Android
-{
-       internal static class CarouselViewExtensions
-       {
-               internal static int Area(this System.Drawing.Rectangle rectangle)
-               {
-                       return rectangle.Width * rectangle.Height;
-               }
-
-               internal static IntVector BoundTranslation(this System.Drawing.Rectangle viewport, IntVector delta, System.Drawing.Rectangle bound)
-               {
-                       // TODO: generalize the math
-                       Debug.Assert(delta.X == 0 || delta.Y == 0);
-
-                       IntVector start = viewport.LeadingCorner(delta);
-                       IntVector end = start + delta;
-                       IntVector clampedEnd = end.Clamp(bound);
-                       IntVector clampedDelta = clampedEnd - start;
-                       return clampedDelta;
-               }
-
-               internal static IntVector Center(this System.Drawing.Rectangle rectangle)
-               {
-                       return (IntVector)rectangle.Location + (IntVector)rectangle.Size / 2;
-               }
-
-               internal static IntVector Clamp(this IntVector position, System.Drawing.Rectangle bound)
-               {
-                       return new IntVector(position.X.Clamp(bound.Left, bound.Right), position.Y.Clamp(bound.Top, bound.Bottom));
-               }
-
-               internal static IntVector LeadingCorner(this System.Drawing.Rectangle rectangle, IntVector delta)
-               {
-                       return new IntVector(delta.X < 0 ? rectangle.Left : rectangle.Right, delta.Y < 0 ? rectangle.Top : rectangle.Bottom);
-               }
-
-               internal static bool LexicographicallyLess(this System.Drawing.Point source, System.Drawing.Point target)
-               {
-                       if (source.X < target.X)
-                               return true;
-
-                       if (source.X > target.X)
-                               return false;
-
-                       return source.Y < target.Y;
-               }
-
-               internal static Rect ToAndroidRectangle(this System.Drawing.Rectangle rectangle)
-               {
-                       return new Rect(rectangle.Left, right: rectangle.Right, top: rectangle.Top, bottom: rectangle.Bottom);
-               }
-
-               internal static Rectangle ToFormsRectangle(this System.Drawing.Rectangle rectangle, Context context)
-               {
-                       return new Rectangle(context.FromPixels(rectangle.Left), context.FromPixels(rectangle.Top), context.FromPixels(rectangle.Width), context.FromPixels(rectangle.Height));
-               }
-
-               internal static int[] ToRange(this Tuple<int, int> startAndCount)
-               {
-                       return Enumerable.Range(startAndCount.Item1, startAndCount.Item2).ToArray();
-               }
-       }
-}
\ No newline at end of file
index f63b6fd..d563b7f 100644 (file)
 using System;
 using System.Collections.Specialized;
 using System.ComponentModel;
+using System.Collections.Generic;
 using System.Linq;
+using Android.Content;
+using Android.Graphics;
+using Android.Views;
 using Android.Support.V7.Widget;
-using AndroidListView = Android.Widget.ListView;
 using static System.Diagnostics.Debug;
-using Observer = Android.Support.V7.Widget.RecyclerView.AdapterDataObserver;
-using BclDebug = System.Diagnostics.Debug;
 using IntRectangle = System.Drawing.Rectangle;
 using IntSize = System.Drawing.Size;
+using IntPoint = System.Drawing.Point;
+using AndroidView = Android.Views.View;
+using Adapter = Android.Support.V7.Widget.RecyclerView.Adapter;
+using Recycler = Android.Support.V7.Widget.RecyclerView.Recycler;
+using State = Android.Support.V7.Widget.RecyclerView.State;
+using ViewHolder = Android.Support.V7.Widget.RecyclerView.ViewHolder;
+using Observer = Android.Support.V7.Widget.RecyclerView.AdapterDataObserver;
+using LayoutManager = Android.Support.V7.Widget.RecyclerView.LayoutManager;
+using LayoutParams = Android.Support.V7.Widget.RecyclerView.LayoutParams;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       public class CarouselViewRenderer : ViewRenderer<CarouselView, RecyclerView>
+       internal static class CarouselViewExtensions
        {
-               PhysicalLayoutManager _physicalLayout;
-               int _position;
+               internal static IntVector BoundTranslation(this IntRectangle viewport, IntVector delta, IntRectangle bound)
+               {
+                       // TODO: generalize the math
+                       Assert(delta.X == 0 || delta.Y == 0);
+
+                       IntVector start = viewport.LeadingCorner(delta);
+                       IntVector end = start + delta;
+                       IntVector clampedEnd = end.Clamp(bound);
+                       IntVector clampedDelta = clampedEnd - start;
+                       return clampedDelta;
+               }
+               internal static IntVector Clamp(this IntVector position, IntRectangle bound)
+               {
+                       return new IntVector(
+                               x: position.X.Clamp(bound.Left, bound.Right), 
+                               y: position.Y.Clamp(bound.Top, bound.Bottom)
+                       );
+               }
+               internal static IntVector LeadingCorner(this IntRectangle rectangle, IntVector delta)
+               {
+                       return new IntVector(
+                               x: delta.X < 0 ? rectangle.Left : rectangle.Right, 
+                               y: delta.Y < 0 ? rectangle.Top : rectangle.Bottom
+                       );
+               }
+               internal static IntVector Center(this IntRectangle rectangle)
+               {
+                       return (IntVector)rectangle.Location + (IntVector)rectangle.Size / 2;
+               }
+               internal static int Area(this IntRectangle rectangle)
+               {
+                       return rectangle.Width * rectangle.Height;
+               }
 
-               public CarouselViewRenderer()
+               internal static Rectangle ToFormsRectangle(this IntRectangle rectangle, Context context)
                {
-                       AutoPackage = false;
+                       return new Rectangle(
+                               x: context.FromPixels(rectangle.Left),
+                               y: context.FromPixels(rectangle.Top),
+                               width: context.FromPixels(rectangle.Width), 
+                               height: context.FromPixels(rectangle.Height)
+                       );
+               }
+               internal static Rect ToAndroidRectangle(this IntRectangle rectangle)
+               {
+                       return new Rect(
+                               left: rectangle.Left, 
+                               right: rectangle.Right, 
+                               top: rectangle.Top, 
+                               bottom: rectangle.Bottom
+                       );
                }
 
-               ItemViewAdapter Adapter
+               internal static bool LexicographicallyLess(this IntPoint source, IntPoint target)
+               {
+                       if (source.X < target.X)
+                               return true;
+
+                       if (source.X > target.X)
+                               return false;
+
+                       return source.Y < target.Y;
+               }
+               internal static int[] ToRange(this Tuple<int, int> startAndCount)
                {
-                       get { return (ItemViewAdapter)Control.GetAdapter(); }
+                       return Enumerable.Range(startAndCount.Item1, startAndCount.Item2).ToArray();
                }
+       }
 
-               new RecyclerView Control
+       internal struct IntVector
+       {
+               public static explicit operator IntVector(IntSize size)
                {
-                       get
-                       {
-                               Initialize();
-                               return base.Control;
-                       }
+                       return new IntVector(size.Width, size.Height);
+               }
+               public static explicit operator IntVector(IntPoint point)
+               {
+                       return new IntVector(point.X, point.Y);
+               }
+               public static implicit operator IntPoint(IntVector vector)
+               {
+                       return new IntPoint(vector.X, vector.Y);
+               }
+               public static implicit operator IntSize(IntVector vector)
+               {
+                       return new IntSize(vector.X, vector.Y);
                }
 
-               ICarouselViewController Controller => Element;
+               public static bool operator ==(IntVector lhs, IntVector rhs)
+               {
+                       return lhs.X == rhs.X && lhs.Y == rhs.Y;
+               }
+               public static bool operator !=(IntVector lhs, IntVector rhs)
+               {
+                       return !(lhs == rhs);
+               }
+               public static IntRectangle operator -(IntRectangle source, IntVector vector) => source + -vector;
+               public static IntRectangle operator +(IntRectangle source, IntVector vector) => 
+                       new IntRectangle(source.Location + vector, source.Size);
+
+               public static IntVector operator -(IntVector vector, IntVector other) => vector + -other;
+               public static IntVector operator +(IntVector vector, IntVector other) => 
+                       new IntVector(
+                               x: vector.X + other.X, 
+                               y: vector.Y + other.Y
+                       );
+
+               public static IntPoint operator -(IntPoint point, IntVector delta) => point + -delta;
+               public static IntPoint operator +(IntPoint point, IntVector delta) => 
+                       new IntPoint(
+                               x: point.X + delta.X, 
+                               y: point.Y + delta.Y
+                       );
+
+               public static IntVector operator -(IntVector vector) => vector * -1;
+               public static IntVector operator *(IntVector vector, int scaler) => 
+                       new IntVector(
+                               x: vector.X * scaler, 
+                               y: vector.Y * scaler
+                       );
+               public static IntVector operator /(IntVector vector, int scaler) => 
+                       new IntVector(
+                               x: vector.X / scaler, 
+                               y: vector.Y / scaler
+                       );
+
+               public static IntVector operator *(IntVector vector, double scaler) => 
+                       new IntVector(
+                               x: (int)(vector.X * scaler),
+                               y:  (int)(vector.Y * scaler)
+                       );
+               public static IntVector operator /(IntVector vector, double scaler) => vector * (1 / scaler);
+
+               internal static IntVector Origin = new IntVector(0, 0);
+               internal static IntVector XUnit = new IntVector(1, 0);
+               internal static IntVector YUnit = new IntVector(0, 1);
+
+               #region Fields
+               readonly int _x;
+               readonly int _y;
+               #endregion
+
+               internal IntVector(int x, int y)
+               {
+                       _x = x;
+                       _y = y;
+               }
 
-               PhysicalLayoutManager LayoutManager
+               internal int X => _x;
+               internal int Y => _y;
+
+               public override bool Equals(object obj)
+               {
+                       return base.Equals(obj);
+               }
+               public override int GetHashCode()
+               {
+                       return base.GetHashCode();
+               }
+               public override string ToString()
                {
-                       get { return (PhysicalLayoutManager)Control.GetLayoutManager(); }
+                       return $"{X},{Y}";
                }
+       }
 
-               protected override Size MinimumSize()
+       [Flags]
+       internal enum MeasureSpecificationType
+       {
+               Unspecified = 0,
+               Exactly = 0x1 << 31,
+               AtMost = 0x1 << 32,
+               Mask = Exactly | AtMost
+       }
+       internal struct MeasureSpecification
+       {
+               public static explicit operator MeasureSpecification(int measureSpecification)
                {
-                       return new Size(40, 40);
+                       return new MeasureSpecification(measureSpecification);
+               }
+               public static implicit operator int(MeasureSpecification measureSpecification)
+               {
+                       return measureSpecification.Encode();
                }
 
-               protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
+               #region Fields
+               readonly int _value;
+               readonly MeasureSpecificationType _type;
+               #endregion
+
+               internal MeasureSpecification(int measureSpecification)
                {
-                       CarouselView oldElement = e.OldElement;
-                       if (oldElement != null)
-                               e.OldElement.CollectionChanged -= OnCollectionChanged;
+                       _value = measureSpecification & (int)~MeasureSpecificationType.Mask;
+                       _type = (MeasureSpecificationType)(measureSpecification & (int)MeasureSpecificationType.Mask);
+               }
+               internal MeasureSpecification(int value, MeasureSpecificationType measureSpecification)
+               {
+                       _value = value;
+                       _type = measureSpecification;
+               }
 
-                       base.OnElementChanged(e);
-                       Initialize();
+               internal int Value => _value;
+               internal MeasureSpecificationType Type => _type;
+               internal int Encode() => Value | (int)Type;
+
+               public override string ToString()
+               {
+                       return string.Format("{0} {1}", Value, Type);
                }
+       }
 
-               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+       public class CarouselViewRenderer : ViewRenderer<CarouselView, RecyclerView>
+       {
+               // http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
+               // http://developer.android.com/training/material/lists-cards.html
+               // http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/
+
+               internal class VirtualLayoutManager : PhysicalLayoutManager.VirtualLayoutManager
                {
-                       if (e.PropertyName == "Position" && _position != Element.Position)
-                               _physicalLayout.ScrollToPosition(Element.Position);
+                       #region Fields
+                       const int Columns = 1;
+                       IntSize _itemSize;
+                       #endregion
 
-                       base.OnElementPropertyChanged(sender, e);
+                       #region Private Members
+                       int GetPosition(int itemCount, int positionOrigin, int x, bool exclusive = false)
+                       {
+                               int position = x / _itemSize.Width + positionOrigin;
+                               bool hasRemainder = x % _itemSize.Width != 0;
+
+                               if (hasRemainder && x < 0)
+                                       position--;
+
+                               if (!hasRemainder && exclusive)
+                                       position--;
+
+                               position = position.Clamp(0, itemCount - 1);
+                               return position;
+                       }
+                       #endregion
+
+                       internal override bool CanScrollHorizontally => true;
+                       internal override bool CanScrollVertically => false;
+
+                       internal override IntRectangle GetBounds(int originPosition, RecyclerView.State state) => 
+                               new IntRectangle(
+                                       LayoutItem(originPosition, 0).Location, 
+                                       new IntSize(_itemSize.Width * state.ItemCount, _itemSize.Height)
+                               );
+
+                       internal override Tuple<int, int> GetPositions(
+                               int positionOrigin, 
+                               int itemCount, 
+                               IntRectangle viewport, 
+                               bool includeBuffer)
+                       {
+                               // returns one item off-screen in either direction. 
+                               int buffer = includeBuffer ? 1 : 0;
+                               int left = GetPosition(itemCount, positionOrigin - buffer, viewport.Left);
+                               int right = GetPosition(itemCount, positionOrigin + buffer, viewport.Right, exclusive: true);
+
+                               int start = left;
+                               int count = right - left + 1;
+                               return new Tuple<int, int>(start, count);
+                       }
+                       internal override void Layout(int positionOffset, IntSize viewportSize, ref IntVector offset)
+                       {
+                               int width = viewportSize.Width / Columns;
+                               int height = viewportSize.Height;
+
+                               if (_itemSize.Width != 0)
+                                       offset *= (double)width / _itemSize.Width;
+
+                               _itemSize = new IntSize(width, height);
+                       }
+                       internal override IntRectangle LayoutItem(int positionOffset, int position)
+                       {
+                               // measure
+                               IntSize size = _itemSize;
+
+                               // layout
+                               var location = new IntVector((position - positionOffset) * size.Width, 0);
+
+                               // allocate
+                               return new IntRectangle(location, size);
+                       }
+
+                       public override string ToString()
+                       {
+                               return $"itemSize={_itemSize}";
+                       }
                }
 
-               protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
+               #region Private Definitions
+               class OnScrollListener : RecyclerView.OnScrollListener
                {
-                       int width = right - left;
-                       int height = bottom - top;
+                       enum ScrollState
+                       {
+                               Idle,
+                               Dragging,
+                               Settling
+                       }
 
-                       LayoutManager.Layout(width, height);
+                       readonly Action _onDragEnd;
+                       readonly Action _onDragStart;
+                       ScrollState _lastScrollState;
 
-                       base.OnLayout(changed, left, top, right, bottom);
+                       internal OnScrollListener(
+                               Action onDragEnd,
+                               Action onDragStart)
+                       {
+                               _onDragEnd = onDragEnd;
+                               _onDragStart = onDragStart;
+                       }
+
+                       public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
+                       {
+                               var state = (ScrollState)newState;
+                               if (_lastScrollState != ScrollState.Dragging && state == ScrollState.Dragging)
+                                       _onDragStart();
 
-                       Control.Measure(new MeasureSpecification(width, MeasureSpecificationType.Exactly), new MeasureSpecification(height, MeasureSpecificationType.Exactly));
+                               if (_lastScrollState == ScrollState.Dragging && state != ScrollState.Dragging)
+                                       _onDragEnd();
 
-                       Control.Layout(0, 0, width, height);
+                               _lastScrollState = state;
+                               base.OnScrollStateChanged(recyclerView, newState);
+                       }
+               }
+               class PositionUpdater : Observer
+               {
+                       #region Fields
+                       readonly CarouselViewRenderer _carouselView;
+                       #endregion
+
+                       internal PositionUpdater(CarouselViewRenderer carouselView)
+                       {
+                               _carouselView = carouselView;
+                       }
+
+                       public override void OnItemRangeInserted(int positionStart, int itemCount)
+                       {
+                               // removal after the current position won't change current position
+                               if (positionStart > _carouselView._position)
+                                       ;
+
+                               // raise position changed
+                               else
+                               {
+                                       _carouselView._position += itemCount;
+                                       _carouselView.OnPositionChanged();
+                               }
+
+                               base.OnItemRangeInserted(positionStart, itemCount);
+                       }
+                       public override void OnItemRangeRemoved(int positionStart, int itemCount)
+                       {
+                               Assert(itemCount == 1);
+
+                               // removal after the current position won't change current position
+                               if (positionStart > _carouselView._position)
+                                       ;
+
+                               // raise item changed
+                               else if (positionStart == _carouselView._position &&
+                                       positionStart != _carouselView.Adapter.ItemCount)
+                               {
+                                       _carouselView.OnItemChanged();
+                                       return;
+                               }
+
+                               // raise position changed
+                               else
+                               {
+                                       _carouselView._position -= itemCount;
+                                       _carouselView.OnPositionChanged();
+                               }
+
+                               base.OnItemRangeRemoved(positionStart, itemCount);
+                       }
+                       public override void OnItemRangeMoved(int fromPosition, int toPosition, int itemCount)
+                       {
+                               base.OnItemRangeMoved(fromPosition, toPosition, itemCount);
+                       }
+               }
+               #endregion
+
+               #region Fields
+               PhysicalLayoutManager _physicalLayout;
+               int _position;
+               #endregion
+
+               public CarouselViewRenderer()
+               {
+                       AutoPackage = false;
                }
 
+               #region Private Members
                void Initialize()
                {
                        // cache hit? Check if the view page is already created
@@ -92,18 +432,31 @@ namespace Xamarin.Forms.Platform.Android
                        SetNativeControl(recyclerView);
 
                        // layoutManager
-                       recyclerView.SetLayoutManager(_physicalLayout = new PhysicalLayoutManager(Context, new VirtualLayoutManager(), Element.Position));
+                       recyclerView.SetLayoutManager(
+                               layout: _physicalLayout = new PhysicalLayoutManager(
+                                       context: Context, 
+                                       virtualLayout: new VirtualLayoutManager(), 
+                                       positionOrigin: Element.Position
+                               )
+                       );
 
                        // swiping
                        var dragging = false;
-                       recyclerView.AddOnScrollListener(new OnScrollListener(onDragStart: () => dragging = true, onDragEnd: () =>
-                       {
-                               dragging = false;
-                               IntVector velocity = _physicalLayout.Velocity;
-
-                               int target = velocity.X > 0 ? _physicalLayout.VisiblePositions().Max() : _physicalLayout.VisiblePositions().Min();
-                               _physicalLayout.ScrollToPosition(target);
-                       }));
+                       recyclerView.AddOnScrollListener(
+                               new OnScrollListener(
+                                       onDragStart: () => dragging = true, 
+                                       onDragEnd: () => 
+                                       {
+                                               dragging = false;
+                                               var velocity = _physicalLayout.Velocity;
+
+                                               var target = velocity.X > 0 ? 
+                                                       _physicalLayout.VisiblePositions().Max() : 
+                                                       _physicalLayout.VisiblePositions().Min();
+                                               _physicalLayout.ScrollToPosition(target);
+                                       }
+                               )
+                       );
 
                        // scrolling
                        var scrolling = false;
@@ -111,7 +464,10 @@ namespace Xamarin.Forms.Platform.Android
                        _physicalLayout.OnEndScroll += position => scrolling = false;
 
                        // appearing
-                       _physicalLayout.OnAppearing += appearingPosition => { Controller.SendPositionAppearing(appearingPosition); };
+                       _physicalLayout.OnAppearing += appearingPosition => 
+                       {
+                               Controller.SendPositionAppearing(appearingPosition);
+                       };
 
                        // disappearing
                        _physicalLayout.OnDisappearing += disappearingPosition =>
@@ -140,28 +496,51 @@ namespace Xamarin.Forms.Platform.Android
                        Element.CollectionChanged += OnCollectionChanged;
                }
 
+               ItemViewAdapter Adapter => (ItemViewAdapter)Control.GetAdapter();
+               PhysicalLayoutManager LayoutManager => (PhysicalLayoutManager)Control.GetLayoutManager();
+               new RecyclerView Control
+               {
+                       get
+                       {
+                               Initialize();
+                               return base.Control;
+                       }
+               }
+
                void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
                {
                        switch (e.Action)
                        {
                                case NotifyCollectionChangedAction.Add:
-                                       Adapter.NotifyItemRangeInserted(e.NewStartingIndex, e.NewItems.Count);
+                                       Adapter.NotifyItemRangeInserted(
+                                               positionStart: e.NewStartingIndex, 
+                                               itemCount: e.NewItems.Count
+                                       );
                                        break;
 
                                case NotifyCollectionChangedAction.Move:
                                        for (var i = 0; i < e.NewItems.Count; i++)
-                                               Adapter.NotifyItemMoved(e.OldStartingIndex + i, e.NewStartingIndex + i);
+                                               Adapter.NotifyItemMoved(
+                                                       fromPosition: e.OldStartingIndex + i, 
+                                                       toPosition: e.NewStartingIndex + i
+                                               );
                                        break;
 
                                case NotifyCollectionChangedAction.Remove:
                                        if (Element.Count == 0)
                                                throw new InvalidOperationException("CarouselView must retain a least one item.");
 
-                                       Adapter.NotifyItemRangeRemoved(e.OldStartingIndex, e.OldItems.Count);
+                                       Adapter.NotifyItemRangeRemoved(
+                                               positionStart: e.OldStartingIndex, 
+                                               itemCount: e.OldItems.Count
+                                       );
                                        break;
 
                                case NotifyCollectionChangedAction.Replace:
-                                       Adapter.NotifyItemRangeChanged(e.OldStartingIndex, e.OldItems.Count);
+                                       Adapter.NotifyItemRangeChanged(
+                                               positionStart: e.OldStartingIndex, 
+                                               itemCount: e.OldItems.Count
+                                       );
                                        break;
 
                                case NotifyCollectionChangedAction.Reset:
@@ -172,182 +551,674 @@ namespace Xamarin.Forms.Platform.Android
                                        throw new Exception($"Enum value '{(int)e.Action}' is not a member of NotifyCollectionChangedAction enumeration.");
                        }
                }
-
+               ICarouselViewController Controller => Element;
+               void OnPositionChanged()
+               {
+                       Element.Position = _position;
+                       Controller.SendSelectedPositionChanged(_position);
+               }
                void OnItemChanged()
                {
                        object item = ((IItemViewController)Element).GetItem(_position);
                        Controller.SendSelectedItemChanged(item);
                }
+               #endregion
 
-               void OnPositionChanged()
+               protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
                {
-                       Element.Position = _position;
-                       Controller.SendSelectedPositionChanged(_position);
+                       CarouselView oldElement = e.OldElement;
+                       if (oldElement != null)
+                               e.OldElement.CollectionChanged -= OnCollectionChanged;
+
+                       base.OnElementChanged(e);
+                       Initialize();
                }
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == "Position" && _position != Element.Position)
+                               _physicalLayout.ScrollToPosition(Element.Position);
 
-               // http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
-               // http://developer.android.com/training/material/lists-cards.html
-               // http://wiresareobsolete.com/2014/09/building-a-recyclerview-layoutmanager-part-1/
+                       base.OnElementPropertyChanged(sender, e);
+               }
+               protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
+               {
+                       int width = right - left;
+                       int height = bottom - top;
 
-               class OnScrollListener : RecyclerView.OnScrollListener
+                       LayoutManager.Layout(width, height);
+
+                       base.OnLayout(changed, left, top, right, bottom);
+
+                       Control.Measure(
+                               widthMeasureSpec: new MeasureSpecification(width, MeasureSpecificationType.Exactly),
+                               heightMeasureSpec: new MeasureSpecification(height, MeasureSpecificationType.Exactly)
+                       );
+
+                       Control.Layout(0, 0, width, height);
+               }
+               protected override Size MinimumSize()
                {
-                       readonly Action _onDragEnd;
-                       readonly Action _onDragStart;
-                       ScrollState _lastScrollState;
+                       return new Size(40, 40);
+               }
+       }
+
+       // RecyclerView virtualizes indexes (adapter position <-> viewGroup child index) 
+       // PhysicalLayoutManager virtualizes location (regular layout <-> screen)
+       internal class PhysicalLayoutManager : LayoutManager
+       {
+               // ObservableCollection is our public entryway to this method and it only supports single item removal
+               internal const int MaxItemsRemoved = 1;
 
-                       internal OnScrollListener(Action onDragEnd, Action onDragStart)
+               internal struct DecoratedView
+               {
+                       public static implicit operator AndroidView(DecoratedView view)
                        {
-                               _onDragEnd = onDragEnd;
-                               _onDragStart = onDragStart;
+                               return view._view;
                        }
 
-                       public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
+                       #region Fields
+                       readonly PhysicalLayoutManager _layout;
+                       readonly AndroidView _view;
+                       #endregion
+
+                       internal DecoratedView(
+                               PhysicalLayoutManager layout, 
+                               AndroidView view)
                        {
-                               var state = (ScrollState)newState;
-                               if (_lastScrollState != ScrollState.Dragging && state == ScrollState.Dragging)
-                                       _onDragStart();
+                               _layout = layout;
+                               _view = view;
+                       }
 
-                               if (_lastScrollState == ScrollState.Dragging && state != ScrollState.Dragging)
-                                       _onDragEnd();
+                       internal int Left => _layout.GetDecoratedLeft(_view);
+                       internal int Top => _layout.GetDecoratedTop(_view);
+                       internal int Bottom => _layout.GetDecoratedBottom(_view);
+                       internal int Right => _layout.GetDecoratedRight(_view);
+                       internal int Width => Right - Left;
+                       internal int Height => Bottom - Top;
+                       internal IntRectangle Rectangle => new IntRectangle(Left, Top, Width, Height);
 
-                               _lastScrollState = state;
-                               base.OnScrollStateChanged(recyclerView, newState);
+                       internal void Measure(int widthUsed, int heightUsed)
+                       {
+                               _layout.MeasureChild(_view, widthUsed, heightUsed);
                        }
-
-                       enum ScrollState
+                       internal void MeasureWithMargins(int widthUsed, int heightUsed)
                        {
-                               Idle,
-                               Dragging,
-                               Settling
+                               _layout.MeasureChildWithMargins(_view, widthUsed, heightUsed);
+                       }
+                       internal void Layout(IntRectangle position)
+                       {
+                               var renderer = _view as IVisualElementRenderer;
+                               renderer.Element.Layout(position.ToFormsRectangle(_layout._context));
+
+                               _layout.LayoutDecorated(_view, 
+                                       left: position.Left, 
+                                       top: position.Top, 
+                                       right: position.Right, 
+                                       bottom: position.Bottom
+                               );
+                       }
+                       internal void Add()
+                       {
+                               _layout.AddView(_view);
                        }
+                       internal void DetachAndScrap(Recycler recycler)
+                       {
+                               _layout.DetachAndScrapView(_view, recycler);
+                       }
+               }
+               internal abstract class VirtualLayoutManager
+               {
+                       internal abstract Tuple<int, int> GetPositions(
+                               int positionOrigin, 
+                               int itemCount, 
+                               IntRectangle viewport, 
+                               bool isPreLayout
+                       );
+
+                       internal abstract IntRectangle LayoutItem(int positionOrigin, int position);
+                       internal abstract bool CanScrollHorizontally { get; }
+                       internal abstract bool CanScrollVertically { get; }
+
+                       internal abstract void Layout(int positionOrigin, IntSize viewportSize, ref IntVector offset);
+                       internal abstract IntRectangle GetBounds(int positionOrigin, State state);
                }
 
-               class PositionUpdater : Observer
+               #region Private Defintions
+               enum AdapterChangeType
                {
-                       readonly CarouselViewRenderer _carouselView;
+                       Removed = 1,
+                       Added,
+                       Moved,
+                       Updated,
+                       Changed
+               }
+               enum SnapPreference
+               {
+                       None = 0,
+                       Begin = 1,
+                       End = -1
+               }
+               sealed class SeekAndSnapScroller : LinearSmoothScroller
+               {
+                       #region Fields
+                       readonly SnapPreference _snapPreference;
+                       readonly Func<int, IntVector> _vectorToPosition;
+                       #endregion
+
+                       internal SeekAndSnapScroller(
+                               Context context, 
+                               Func<int, IntVector> vectorToPosition, 
+                               SnapPreference snapPreference = SnapPreference.None) 
+                               : base(context)
+                       {
+                               _vectorToPosition = vectorToPosition;
+                               _snapPreference = snapPreference;
+                       }
 
-                       internal PositionUpdater(CarouselViewRenderer carouselView)
+                       protected override int HorizontalSnapPreference => (int)_snapPreference;
+                       protected override void OnStart()
                        {
-                               _carouselView = carouselView;
+                               OnBeginScroll?.Invoke(TargetPosition);
+                               base.OnStart();
+                       }
+                       protected override void OnStop()
+                       {
+                               // expected this to be triggered with the animation stops but it
+                               // actually seems to be triggered when the target is found
+                               OnEndScroll?.Invoke(TargetPosition);
+                               base.OnStop();
                        }
 
-                       public override void OnItemRangeInserted(int positionStart, int itemCount)
+                       public event Action<int> OnBeginScroll;
+                       public event Action<int> OnEndScroll;
+
+                       public override PointF ComputeScrollVectorForPosition(int targetPosition)
                        {
-                               // removal after the current position won't change current position
-                               if (positionStart > _carouselView._position)
-                                       ;
+                               var vector = _vectorToPosition(targetPosition);
+                               return new PointF(vector.X, vector.Y);
+                       }
 
-                               // raise position changed
-                               else
-                               {
-                                       _carouselView._position += itemCount;
-                                       _carouselView.OnPositionChanged();
+               }
+               #endregion
+
+               #region Static Fields
+               readonly static int s_samplesCount = 5;
+               readonly static Func<int, int> s_fixPosition = o => o;
+               #endregion
+
+               #region Fields
+               readonly Context _context;
+               readonly VirtualLayoutManager _virtualLayout;
+               readonly Queue<Action<Recycler, State>> _deferredLayout;
+               readonly Dictionary<int, AndroidView> _viewByAdaptorPosition;
+               readonly HashSet<int> _visibleAdapterPosition;
+               readonly SeekAndSnapScroller _scroller;
+
+               int _positionOrigin; // coordinates are relative to the upper left corner of this element
+               IntVector _locationOffset; // upper left corner of screen is positionOrigin + locationOffset
+               List<IntVector> _samples;
+               AdapterChangeType _adapterChangeType;
+               #endregion
+
+               public PhysicalLayoutManager(
+                       Context context, 
+                       VirtualLayoutManager virtualLayout, 
+                       int positionOrigin)
+               {
+                       _positionOrigin = positionOrigin;
+                       _context = context;
+                       _virtualLayout = virtualLayout;
+                       _viewByAdaptorPosition = new Dictionary<int, AndroidView>();
+                       _visibleAdapterPosition = new HashSet<int>();
+                       _samples = Enumerable.Repeat(IntVector.Origin, s_samplesCount).ToList();
+                       _deferredLayout = new Queue<Action<Recycler, State>>();
+                       _scroller = new SeekAndSnapScroller(
+                               context: context, 
+                               vectorToPosition: adapterPosition => {
+                                       var end = virtualLayout.LayoutItem(positionOrigin, adapterPosition).Center();
+                                       var begin = Viewport.Center();
+                                       return end - begin;
                                }
+                       );
 
-                               base.OnItemRangeInserted(positionStart, itemCount);
-                       }
+                       _scroller.OnBeginScroll += adapterPosition => OnBeginScroll?.Invoke(adapterPosition);
+                       _scroller.OnEndScroll += adapterPosition => OnEndScroll?.Invoke(adapterPosition);
+               }
 
-                       public override void OnItemRangeMoved(int fromPosition, int toPosition, int itemCount)
+               #region Private Members
+               // helpers to deal with locations as IntRectangles and IntVectors
+               IntRectangle Rectangle => new IntRectangle(0, 0, Width, Height);
+               void OffsetChildren(IntVector delta)
+               {
+                       OffsetChildrenHorizontal(-delta.X);
+                       OffsetChildrenVertical(-delta.Y);
+               }
+               void ScrollBy(ref IntVector delta, Recycler recycler, State state)
+               {
+                       _adapterChangeType = default(AdapterChangeType);
+
+                       delta = Viewport.BoundTranslation(
+                               delta: delta, 
+                               bound: _virtualLayout.GetBounds(_positionOrigin, state)
+                       );
+
+                       _locationOffset += delta;
+                       _samples.Insert(0, delta);
+                       _samples.RemoveAt(_samples.Count - 1);
+
+                       OffsetChildren(delta);
+                       OnLayoutChildren(recycler, state);
+               }
+               void OnAppearingOrDisappearing(int position, bool isAppearing)
+               {
+                       if (isAppearing)
                        {
-                               base.OnItemRangeMoved(fromPosition, toPosition, itemCount);
+                               if (!_visibleAdapterPosition.Contains(position))
+                               {
+                                       _visibleAdapterPosition.Add(position);
+                                       OnAppearing?.Invoke(position);
+                               }
                        }
-
-                       public override void OnItemRangeRemoved(int positionStart, int itemCount)
+                       else
                        {
-                               Assert(itemCount == 1);
+                               if (_visibleAdapterPosition.Contains(position))
+                               {
+                                       _visibleAdapterPosition.Remove(position);
+                                       OnDisappearing?.Invoke(position);
+                               }
+                       }
+               }
+               #endregion
 
-                               // removal after the current position won't change current position
-                               if (positionStart > _carouselView._position)
-                                       ;
+               protected override void Dispose(bool disposing)
+               {
+                       base.Dispose(disposing);
+               }
 
-                               // raise item changed
-                               else if (positionStart == _carouselView._position && positionStart != _carouselView.Adapter.ItemCount)
+               public event Action<int> OnAppearing;
+               public event Action<int> OnBeginScroll;
+               public event Action<int> OnDisappearing;
+               public event Action<int> OnEndScroll;
+
+               public IntVector Velocity => _samples.Aggregate((o, a) => o + a) / _samples.Count;
+               public void Layout(int width, int height)
+               {
+                       // e.g. when rotated the width and height are updated the virtual layout will 
+                       // need to resize and provide a new viewport offset given the current one.
+                       _virtualLayout.Layout(_positionOrigin, new IntSize(width, height), ref _locationOffset);
+               }
+               public IntRectangle Viewport => Rectangle + _locationOffset;
+               public IEnumerable<int> VisiblePositions()
+               {
+                       return _visibleAdapterPosition;
+               }
+               public IEnumerable<AndroidView> Views()
+               {
+                       return _viewByAdaptorPosition.Values;
+               }
+
+               public override void OnAdapterChanged(Adapter oldAdapter, Adapter newAdapter)
+               {
+                       RemoveAllViews();
+               }
+               public override void OnItemsChanged(RecyclerView recyclerView)
+               {
+                       _adapterChangeType = AdapterChangeType.Changed;
+
+                       // low-fidelity change event; assume everything has changed. If adapter reports it has "stable IDs" then 
+                       // RecyclerView will attempt to synthesize high-fidelity change events: added, removed, moved, updated.
+                       base.OnItemsChanged(recyclerView);
+               }
+               public override void OnItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)
+               {
+                       _adapterChangeType = AdapterChangeType.Added;
+
+                       _deferredLayout.Enqueue((recycler, state) => {
+
+                               var viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray();
+                               _viewByAdaptorPosition.Clear();
+                               foreach (KeyValuePair<int, AndroidView> pair in viewByAdaptorPositionCopy)
                                {
-                                       _carouselView.OnItemChanged();
-                                       return;
+                                       var view = pair.Value;
+                                       var position = pair.Key;
+
+                                       // position unchanged
+                                       if (position < positionStart)
+                                               _viewByAdaptorPosition[position] = view;
+
+                                       // position changed
+                                       else
+                                               _viewByAdaptorPosition[position + itemCount] = view;
                                }
 
-                               // raise position changed
-                               else
+                               if (_positionOrigin >= positionStart)
+                                       _positionOrigin += itemCount;
+                       });
+                       base.OnItemsAdded(recyclerView, positionStart, itemCount);
+               }
+               public override void OnItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)
+               {
+                       Assert(itemCount == MaxItemsRemoved);
+                       _adapterChangeType = AdapterChangeType.Removed;
+
+                       var positionEnd = positionStart + itemCount;
+
+                       _deferredLayout.Enqueue((recycler, state) => {
+                               if (state.ItemCount == 0)
+                                       throw new InvalidOperationException("Cannot delete all items.");
+
+                               // re-map views to their new positions
+                               var viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray();
+                               _viewByAdaptorPosition.Clear();
+                               foreach (var pair in viewByAdaptorPositionCopy)
                                {
-                                       _carouselView._position -= itemCount;
-                                       _carouselView.OnPositionChanged();
+                                       var view = pair.Value;
+                                       var position = pair.Key;
+
+                                       // position unchanged
+                                       if (position < positionStart)
+                                               _viewByAdaptorPosition[position] = view;
+
+                                       // position changed
+                                       else if (position >= positionEnd)
+                                               _viewByAdaptorPosition[position - itemCount] = view;
+
+                                       // removed
+                                       else
+                                       {
+                                               _viewByAdaptorPosition[-1] = view;
+                                               if (_visibleAdapterPosition.Contains(position))
+                                                       _visibleAdapterPosition.Remove(position);
+                                       }
                                }
 
-                               base.OnItemRangeRemoved(positionStart, itemCount);
-                       }
+                               // if removed origin then shift origin to first removed position
+                               if (_positionOrigin >= positionStart && _positionOrigin < positionEnd)
+                               {
+                                       _positionOrigin = positionStart;
+
+                                       // if no items to right of removed origin then set origin to item prior to removed set
+                                       if (_positionOrigin >= state.ItemCount)
+                                       {
+                                               _positionOrigin = state.ItemCount - 1;
+
+                                               if (!_viewByAdaptorPosition.ContainsKey(_positionOrigin))
+                                                       throw new InvalidOperationException(
+                                                               "VirtualLayoutManager must add items to the left and right of the origin"
+                                                       );
+                                       }
+                               }
+
+                               // if removed before origin then shift origin left
+                               else if (_positionOrigin >= positionEnd)
+                                       _positionOrigin -= itemCount;
+                       });
+
+                       base.OnItemsRemoved(recyclerView, positionStart, itemCount);
                }
+               public override void OnItemsMoved(RecyclerView recyclerView, int from, int toValue, int itemCount)
+               {
+                       _adapterChangeType = AdapterChangeType.Moved;
+                       base.OnItemsMoved(recyclerView, from, toValue, itemCount);
+               }
+               public override void OnItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)
+               {
+                       _adapterChangeType = AdapterChangeType.Updated;
 
-               internal class VirtualLayoutManager : PhysicalLayoutManager.VirtualLayoutManager
+                       // rebind rendered updated elements
+                       _deferredLayout.Enqueue((recycler, state) => {
+                               for (var i = 0; i < itemCount; i++)
+                               {
+                                       var position = positionStart + i;
+
+                                       AndroidView view;
+                                       if (!_viewByAdaptorPosition.TryGetValue(position, out view))
+                                               continue;
+
+                                       recycler.BindViewToPosition(view, position);
+                               }
+                       });
+
+                       base.OnItemsUpdated(recyclerView, positionStart, itemCount);
+               }
+
+               public override LayoutParams GenerateDefaultLayoutParams()
                {
-                       const int Columns = 1;
+                       return new LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
+               }
+               public override AndroidView FindViewByPosition(int adapterPosition)
+               {
+                       // Used by SmoothScrollToPosition to know when the view 
+                       // for the targeted adapterPosition has been attached.
 
-                       IntSize _itemSize;
+                       AndroidView view;
+                       if (!_viewByAdaptorPosition.TryGetValue(adapterPosition, out view))
+                               return null;
+                       return view;
+               }
 
-                       internal override bool CanScrollHorizontally => true;
+               public override void ScrollToPosition(int adapterPosition)
+               {
+                       if (adapterPosition < 0 || adapterPosition >= ItemCount)
+                               throw new ArgumentException(nameof(adapterPosition));
 
-                       internal override bool CanScrollVertically => false;
+                       _scroller.TargetPosition = adapterPosition;
+                       StartSmoothScroll(_scroller);
+               }
+               public override void SmoothScrollToPosition(RecyclerView recyclerView, State state, int adapterPosition)
+               {
+                       ScrollToPosition(adapterPosition);
+               }
+               public override bool CanScrollHorizontally() => _virtualLayout.CanScrollHorizontally;
+               public override bool CanScrollVertically() => _virtualLayout.CanScrollVertically;
 
-                       public override string ToString()
+               // entry points
+               public override bool SupportsPredictiveItemAnimations() => true;
+               public override void OnLayoutChildren(Recycler recycler, State state)
+               {
+                       var adapterChangeType = _adapterChangeType;
+                       if (state.IsPreLayout)
+                               adapterChangeType = default(AdapterChangeType);
+
+                       // adapter updates
+                       if (!state.IsPreLayout)
                        {
-                               return $"itemSize={_itemSize}";
+                               while (_deferredLayout.Count > 0)
+                                       _deferredLayout.Dequeue()(recycler, state);
                        }
 
-                       internal override IntRectangle GetBounds(int originPosition, RecyclerView.State state)
-                               => new IntRectangle(LayoutItem(originPosition, 0).Location, new IntSize(_itemSize.Width * state.ItemCount, _itemSize.Height));
+                       // get visible items
+                       var positions = _virtualLayout.GetPositions(
+                               positionOrigin: _positionOrigin,
+                               itemCount: state.ItemCount,
+                               viewport: Viewport,
+                               // IsPreLayout => some type of data update of yet unknown type. Must assume update 
+                               // could be remove so virtualLayout must +1 off-screen left in case origin is 
+                               // removed and +n off-screen right to slide onscreen if a big item is removed
+                               isPreLayout: state.IsPreLayout || adapterChangeType == AdapterChangeType.Removed
+                       ).ToRange();
 
-                       internal override Tuple<int, int> GetPositions(int positionOrigin, int itemCount, IntRectangle viewport, bool includeBuffer)
-                       {
-                               // returns one item off-screen in either direction. 
-                               int buffer = includeBuffer ? 1 : 0;
-                               int left = GetPosition(itemCount, positionOrigin - buffer, viewport.Left);
-                               int right = GetPosition(itemCount, positionOrigin + buffer, viewport.Right, true);
+                       // disappearing
+                       var disappearing = _viewByAdaptorPosition.Keys.Except(positions).ToList();
 
-                               int start = left;
-                               int count = right - left + 1;
-                               return new Tuple<int, int>(start, count);
+                       // defer cleanup of displaced items and lay them out off-screen so they animate off-screen
+                       if (adapterChangeType == AdapterChangeType.Added)
+                       {
+                               positions = positions.Concat(disappearing).OrderBy(o => o).ToArray();
+                               disappearing.Clear();
                        }
 
-                       internal override void Layout(int positionOffset, IntSize viewportSize, ref IntVector offset)
+                       // recycle
+                       foreach (var position in disappearing)
                        {
-                               int width = viewportSize.Width / Columns;
-                               int height = viewportSize.Height;
+                               var view = _viewByAdaptorPosition[position];
 
-                               if (_itemSize.Width != 0)
-                                       offset *= (double)width / _itemSize.Width;
+                               // remove
+                               _viewByAdaptorPosition.Remove(position);
+                               OnAppearingOrDisappearing(position, false);
 
-                               _itemSize = new IntSize(width, height);
+                               // scrap
+                               new DecoratedView(this, view).DetachAndScrap(recycler);
                        }
 
-                       internal override IntRectangle LayoutItem(int positionOffset, int position)
+                       // TODO: Generalize
+                       if (adapterChangeType == AdapterChangeType.Removed && _positionOrigin == state.ItemCount - 1)
                        {
-                               // measure
-                               IntSize size = _itemSize;
+                               var vlayout = _virtualLayout.LayoutItem(_positionOrigin, _positionOrigin);
+                               _locationOffset = new IntVector(vlayout.Width - Width, _locationOffset.Y);
+                       }
+
+                       var nextLocationOffset = new IntPoint(int.MaxValue, int.MaxValue);
+                       var nextPositionOrigin = int.MaxValue;
+                       foreach (var position in positions)
+                       {
+                               // attach
+                               AndroidView view;
+                               if (!_viewByAdaptorPosition.TryGetValue(position, out view))
+                                       AddView(_viewByAdaptorPosition[position] = view = recycler.GetViewForPosition(position));
 
                                // layout
-                               var location = new IntVector((position - positionOffset) * size.Width, 0);
+                               var decoratedView = new DecoratedView(this, view);
+                               var layout = _virtualLayout.LayoutItem(_positionOrigin, position);
+                               var physicalLayout = layout - _locationOffset;
+                               decoratedView.Layout(physicalLayout);
 
-                               // allocate
-                               return new IntRectangle(location, size);
+                               var isVisible = Viewport.IntersectsWith(layout);
+                               if (isVisible)
+                                       OnAppearingOrDisappearing(position, true);
+
+                               // update offsets
+                               if (isVisible && position < nextPositionOrigin)
+                               {
+                                       nextLocationOffset = layout.Location;
+                                       nextPositionOrigin = position;
+                               }
                        }
 
-                       int GetPosition(int itemCount, int positionOrigin, int x, bool exclusive = false)
+                       // update origin
+                       if (nextPositionOrigin != int.MaxValue)
                        {
-                               int position = x / _itemSize.Width + positionOrigin;
-                               bool hasRemainder = x % _itemSize.Width != 0;
+                               _positionOrigin = nextPositionOrigin;
+                               _locationOffset -= (IntVector)nextLocationOffset;
+                       }
 
-                               if (hasRemainder && x < 0)
-                                       position--;
+                       // scrapped views not re-attached must be recycled (why isn't this done by Android, I dunno)
+                       foreach (var viewHolder in recycler.ScrapList.ToArray())
+                               recycler.RecycleView(viewHolder.ItemView);
+               }
 
-                               if (!hasRemainder && exclusive)
-                                       position--;
+               public override int ScrollHorizontallyBy(int dx, Recycler recycler, State state)
+               {
+                       var delta = new IntVector(dx, 0);
+                       ScrollBy(ref delta, recycler, state);
+                       return delta.X;
+               }
+               public override int ScrollVerticallyBy(int dy, Recycler recycler, State state)
+               {
+                       var delta = new IntVector(0, dy);
+                       ScrollBy(ref delta, recycler, state);
+                       return delta.Y;
+               }
 
-                               position = position.Clamp(0, itemCount - 1);
-                               return position;
-                       }
+               public override string ToString()
+               {
+                       return $"offset={_locationOffset}";
                }
        }
 
-       // RecyclerView virtualizes indexes (adapter position <-> viewGroup child index) 
-       // PhysicalLayoutManager virtualizes location (regular layout <-> screen)
+       internal class ItemViewAdapter : Adapter
+       {
+               #region Private Definitions
+               class CarouselViewHolder : ViewHolder
+               {
+                       #region Fields
+                       readonly View _view;
+                       readonly IVisualElementRenderer _visualElementRenderer;
+                       #endregion
+
+                       public CarouselViewHolder(View view, IVisualElementRenderer renderer) 
+                               : base(renderer.ViewGroup)
+                       {
+                               _visualElementRenderer = renderer;
+                               _view = view;
+                       }
+
+                       public View View => _view;
+                       public IVisualElementRenderer VisualElementRenderer => _visualElementRenderer;
+               }
+               #endregion
+
+               #region Fields
+               readonly IVisualElementRenderer _renderer;
+               readonly Dictionary<int, object> _typeByTypeId;
+               readonly Dictionary<object, int> _typeIdByType;
+               int _nextItemTypeId;
+               #endregion
+
+               public ItemViewAdapter(IVisualElementRenderer carouselRenderer)
+               {
+                       _renderer = carouselRenderer;
+                       _typeByTypeId = new Dictionary<int, object>();
+                       _typeIdByType = new Dictionary<object, int>();
+                       _nextItemTypeId = 0;
+               }
+
+               #region Private Members
+               ItemsView Element
+               {
+                       get {
+                               return (ItemsView)_renderer.Element;
+                       }
+               }
+               IItemViewController Controller
+               {
+                       get {
+                               return Element;
+                       }
+               }
+               #endregion
+
+               public override int ItemCount
+               {
+                       get {
+                               return Element.Count;
+                       }
+               }
+               public override int GetItemViewType(int position)
+               {
+                       // get item and type from ItemSource and ItemTemplate
+                       object item = Controller.GetItem(position);
+                       object type = Controller.GetItemType(item);
+
+                       // map type as DataTemplate to type as Id
+                       int id = default(int);
+                       if (!_typeIdByType.TryGetValue(type, out id))
+                       {
+                               id = _nextItemTypeId++;
+                               _typeByTypeId[id] = type;
+                               _typeIdByType[type] = id;
+                       }
+                       return id;
+               }
+               public override ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
+               {
+                       // create view from type
+                       var type = _typeByTypeId[viewType];
+                       var view = Controller.CreateView(type);
+
+                       // create renderer for view
+                       var renderer = Platform.CreateRenderer(view);
+                       Platform.SetRenderer(view, renderer);
+
+                       // package renderer + view
+                       return new CarouselViewHolder(view, renderer);
+               }
+               public override void OnBindViewHolder(ViewHolder holder, int position)
+               {
+                       var carouselHolder = (CarouselViewHolder)holder;
+
+                       var item = Controller.GetItem(position);
+                       Controller.BindView(carouselHolder.View, item);
+               }
+       }
 }
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/IntVector.cs b/Xamarin.Forms.Platform.Android/Renderers/IntVector.cs
deleted file mode 100644 (file)
index e5e0986..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-namespace Xamarin.Forms.Platform.Android
-{
-       internal struct IntVector
-       {
-               public static explicit operator IntVector(System.Drawing.Size size)
-               {
-                       return new IntVector(size.Width, size.Height);
-               }
-
-               public static explicit operator IntVector(System.Drawing.Point point)
-               {
-                       return new IntVector(point.X, point.Y);
-               }
-
-               public static implicit operator System.Drawing.Point(IntVector vector)
-               {
-                       return new System.Drawing.Point(vector.X, vector.Y);
-               }
-
-               public static implicit operator System.Drawing.Size(IntVector vector)
-               {
-                       return new System.Drawing.Size(vector.X, vector.Y);
-               }
-
-               public static bool operator ==(IntVector lhs, IntVector rhs)
-               {
-                       return lhs.X == rhs.X && lhs.Y == rhs.Y;
-               }
-
-               public static bool operator !=(IntVector lhs, IntVector rhs)
-               {
-                       return !(lhs == rhs);
-               }
-
-               public static System.Drawing.Rectangle operator -(System.Drawing.Rectangle source, IntVector vector) => source + -vector;
-
-               public static System.Drawing.Rectangle operator +(System.Drawing.Rectangle source, IntVector vector) => new System.Drawing.Rectangle(source.Location + vector, source.Size);
-
-               public static System.Drawing.Point operator -(System.Drawing.Point point, IntVector delta) => point + -delta;
-
-               public static System.Drawing.Point operator +(System.Drawing.Point point, IntVector delta) => new System.Drawing.Point(point.X + delta.X, point.Y + delta.Y);
-
-               public static IntVector operator -(IntVector vector, IntVector other) => vector + -other;
-
-               public static IntVector operator +(IntVector vector, IntVector other) => new IntVector(vector.X + other.X, vector.Y + other.Y);
-
-               public static IntVector operator -(IntVector vector) => vector * -1;
-
-               public static IntVector operator *(IntVector vector, int scaler) => new IntVector(vector.X * scaler, vector.Y * scaler);
-
-               public static IntVector operator /(IntVector vector, int scaler) => new IntVector(vector.X / scaler, vector.Y / scaler);
-
-               public static IntVector operator *(IntVector vector, double scaler) => new IntVector((int)(vector.X * scaler), (int)(vector.Y * scaler));
-
-               public static IntVector operator /(IntVector vector, double scaler) => vector * (1 / scaler);
-
-               internal static IntVector Origin = new IntVector(0, 0);
-               internal static IntVector XUnit = new IntVector(1, 0);
-               internal static IntVector YUnit = new IntVector(0, 1);
-
-               internal IntVector(int x, int y)
-               {
-                       X = x;
-                       Y = y;
-               }
-
-               internal int X { get; }
-
-               internal int Y { get; }
-
-               public override bool Equals(object obj)
-               {
-                       return base.Equals(obj);
-               }
-
-               public override int GetHashCode()
-               {
-                       return base.GetHashCode();
-               }
-
-               public override string ToString()
-               {
-                       return $"{X},{Y}";
-               }
-       }
-}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/ItemViewAdapter.cs b/Xamarin.Forms.Platform.Android/Renderers/ItemViewAdapter.cs
deleted file mode 100644 (file)
index 84dfa17..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-using System.Collections.Generic;
-using Android.Support.V7.Widget;
-using Android.Views;
-
-namespace Xamarin.Forms.Platform.Android
-{
-       internal class ItemViewAdapter : RecyclerView.Adapter
-       {
-               readonly IVisualElementRenderer _renderer;
-               readonly Dictionary<int, object> _typeByTypeId;
-               readonly Dictionary<object, int> _typeIdByType;
-               int _nextItemTypeId;
-
-               public ItemViewAdapter(IVisualElementRenderer carouselRenderer)
-               {
-                       _renderer = carouselRenderer;
-                       _typeByTypeId = new Dictionary<int, object>();
-                       _typeIdByType = new Dictionary<object, int>();
-                       _nextItemTypeId = 0;
-               }
-
-               public override int ItemCount
-               {
-                       get { return Element.Count; }
-               }
-
-               IItemViewController Controller
-               {
-                       get { return Element; }
-               }
-
-               ItemsView Element
-               {
-                       get { return (ItemsView)_renderer.Element; }
-               }
-
-               public override int GetItemViewType(int position)
-               {
-                       // get item and type from ItemSource and ItemTemplate
-                       object item = Controller.GetItem(position);
-                       object type = Controller.GetItemType(item);
-
-                       // map type as DataTemplate to type as Id
-                       int id = default(int);
-                       if (!_typeIdByType.TryGetValue(type, out id))
-                       {
-                               id = _nextItemTypeId++;
-                               _typeByTypeId[id] = type;
-                               _typeIdByType[type] = id;
-                       }
-                       return id;
-               }
-
-               public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
-               {
-                       var carouselHolder = (CarouselViewHolder)holder;
-
-                       object item = Controller.GetItem(position);
-                       Controller.BindView(carouselHolder.View, item);
-               }
-
-               public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
-               {
-                       // create view from type
-                       object type = _typeByTypeId[viewType];
-                       View view = Controller.CreateView(type);
-
-                       // create renderer for view
-                       IVisualElementRenderer renderer = Platform.CreateRenderer(view);
-                       Platform.SetRenderer(view, renderer);
-
-                       // package renderer + view
-                       return new CarouselViewHolder(view, renderer);
-               }
-
-               class CarouselViewHolder : RecyclerView.ViewHolder
-               {
-                       public CarouselViewHolder(View view, IVisualElementRenderer renderer) : base(renderer.ViewGroup)
-                       {
-                               VisualElementRenderer = renderer;
-                               View = view;
-                       }
-
-                       public View View { get; }
-
-                       public IVisualElementRenderer VisualElementRenderer { get; }
-               }
-       }
-}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecification.cs b/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecification.cs
deleted file mode 100644 (file)
index fe38a56..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-namespace Xamarin.Forms.Platform.Android
-{
-       internal struct MeasureSpecification
-       {
-               public static explicit operator MeasureSpecification(int measureSpecification)
-               {
-                       return new MeasureSpecification(measureSpecification);
-               }
-
-               public static implicit operator int(MeasureSpecification measureSpecification)
-               {
-                       return measureSpecification.Encode();
-               }
-
-               internal MeasureSpecification(int measureSpecification)
-               {
-                       Value = measureSpecification & (int)~MeasureSpecificationType.Mask;
-                       Type = (MeasureSpecificationType)(measureSpecification & (int)MeasureSpecificationType.Mask);
-               }
-
-               internal MeasureSpecification(int value, MeasureSpecificationType measureSpecification)
-               {
-                       Value = value;
-                       Type = measureSpecification;
-               }
-
-               internal int Value { get; }
-
-               internal MeasureSpecificationType Type { get; }
-
-               internal int Encode()
-               {
-                       return Value | (int)Type;
-               }
-
-               public override string ToString()
-               {
-                       return string.Format("{0} {1}", Value, Type);
-               }
-       }
-}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecificationType.cs b/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecificationType.cs
deleted file mode 100644 (file)
index bb61d84..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace Xamarin.Forms.Platform.Android
-{
-       [Flags]
-       internal enum MeasureSpecificationType
-       {
-               Unspecified = 0,
-               Exactly = 0x1 << 31,
-               AtMost = 0x1 << 32,
-               Mask = Exactly | AtMost
-       }
-}
\ No newline at end of file
diff --git a/Xamarin.Forms.Platform.Android/Renderers/PhysicalLayoutManager.cs b/Xamarin.Forms.Platform.Android/Renderers/PhysicalLayoutManager.cs
deleted file mode 100644 (file)
index 64e859d..0000000
+++ /dev/null
@@ -1,531 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using Android.Content;
-using Android.Graphics;
-using Android.Support.V7.Widget;
-using Android.Views;
-
-namespace Xamarin.Forms.Platform.Android
-{
-       internal class PhysicalLayoutManager : RecyclerView.LayoutManager
-       {
-               // ObservableCollection is our public entryway to this method and it only supports single item removal
-               internal const int MaxItemsRemoved = 1;
-
-               static readonly int s_samplesCount = 5;
-               static Func<int, int> s_fixPosition = o => o;
-
-               readonly Context _context;
-               readonly Queue<Action<RecyclerView.Recycler, RecyclerView.State>> _deferredLayout;
-               readonly List<IntVector> _samples;
-               readonly SeekAndSnapScroller _scroller;
-               readonly Dictionary<int, global::Android.Views.View> _viewByAdaptorPosition;
-               readonly VirtualLayoutManager _virtualLayout;
-               readonly HashSet<int> _visibleAdapterPosition;
-               AdapterChangeType _adapterChangeType;
-               IntVector _locationOffset; // upper left corner of screen is positionOrigin + locationOffset
-               int _positionOrigin; // coordinates are relative to the upper left corner of this element
-
-               public PhysicalLayoutManager(Context context, VirtualLayoutManager virtualLayout, int positionOrigin)
-               {
-                       _positionOrigin = positionOrigin;
-                       _context = context;
-                       _virtualLayout = virtualLayout;
-                       _viewByAdaptorPosition = new Dictionary<int, global::Android.Views.View>();
-                       _visibleAdapterPosition = new HashSet<int>();
-                       _samples = Enumerable.Repeat(IntVector.Origin, s_samplesCount).ToList();
-                       _deferredLayout = new Queue<Action<RecyclerView.Recycler, RecyclerView.State>>();
-                       _scroller = new SeekAndSnapScroller(context, adapterPosition =>
-                       {
-                               IntVector end = virtualLayout.LayoutItem(positionOrigin, adapterPosition).Center();
-                               IntVector begin = Viewport.Center();
-                               return end - begin;
-                       });
-
-                       _scroller.OnBeginScroll += adapterPosition => OnBeginScroll?.Invoke(adapterPosition);
-                       _scroller.OnEndScroll += adapterPosition => OnEndScroll?.Invoke(adapterPosition);
-               }
-
-               public IntVector Velocity => _samples.Aggregate((o, a) => o + a) / _samples.Count;
-
-               public System.Drawing.Rectangle Viewport => Rectangle + _locationOffset;
-
-               // helpers to deal with locations as IntRectangles and IntVectors
-               System.Drawing.Rectangle Rectangle => new System.Drawing.Rectangle(0, 0, Width, Height);
-
-               public override bool CanScrollHorizontally() => _virtualLayout.CanScrollHorizontally;
-
-               public override bool CanScrollVertically() => _virtualLayout.CanScrollVertically;
-
-               public override global::Android.Views.View FindViewByPosition(int adapterPosition)
-               {
-                       // Used by SmoothScrollToPosition to know when the view 
-                       // for the targeted adapterPosition has been attached.
-
-                       global::Android.Views.View view;
-                       if (!_viewByAdaptorPosition.TryGetValue(adapterPosition, out view))
-                               return null;
-                       return view;
-               }
-
-               public override RecyclerView.LayoutParams GenerateDefaultLayoutParams()
-               {
-                       return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent);
-               }
-
-               public void Layout(int width, int height)
-               {
-                       // e.g. when rotated the width and height are updated the virtual layout will 
-                       // need to resize and provide a new viewport offset given the current one.
-                       _virtualLayout.Layout(_positionOrigin, new System.Drawing.Size(width, height), ref _locationOffset);
-               }
-
-               public override void OnAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter)
-               {
-                       RemoveAllViews();
-               }
-
-               public event Action<int> OnAppearing;
-
-               public event Action<int> OnBeginScroll;
-
-               public event Action<int> OnDisappearing;
-
-               public event Action<int> OnEndScroll;
-
-               public override void OnItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount)
-               {
-                       _adapterChangeType = AdapterChangeType.Added;
-
-                       _deferredLayout.Enqueue((recycler, state) =>
-                       {
-                               KeyValuePair<int, global::Android.Views.View>[] viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray();
-                               _viewByAdaptorPosition.Clear();
-                               foreach (KeyValuePair<int, global::Android.Views.View> pair in viewByAdaptorPositionCopy)
-                               {
-                                       global::Android.Views.View view = pair.Value;
-                                       int position = pair.Key;
-
-                                       // position unchanged
-                                       if (position < positionStart)
-                                               _viewByAdaptorPosition[position] = view;
-
-                                       // position changed
-                                       else
-                                               _viewByAdaptorPosition[position + itemCount] = view;
-                               }
-
-                               if (_positionOrigin >= positionStart)
-                                       _positionOrigin += itemCount;
-                       });
-                       base.OnItemsAdded(recyclerView, positionStart, itemCount);
-               }
-
-               public override void OnItemsChanged(RecyclerView recyclerView)
-               {
-                       _adapterChangeType = AdapterChangeType.Changed;
-
-                       // low-fidelity change event; assume everything has changed. If adapter reports it has "stable IDs" then 
-                       // RecyclerView will attempt to synthesize high-fidelity change events: added, removed, moved, updated.
-                       base.OnItemsChanged(recyclerView);
-               }
-
-               public override void OnItemsMoved(RecyclerView recyclerView, int from, int toValue, int itemCount)
-               {
-                       _adapterChangeType = AdapterChangeType.Moved;
-                       base.OnItemsMoved(recyclerView, from, toValue, itemCount);
-               }
-
-               public override void OnItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount)
-               {
-                       Debug.Assert(itemCount == MaxItemsRemoved);
-                       _adapterChangeType = AdapterChangeType.Removed;
-
-                       int positionEnd = positionStart + itemCount;
-
-                       _deferredLayout.Enqueue((recycler, state) =>
-                       {
-                               if (state.ItemCount == 0)
-                                       throw new InvalidOperationException("Cannot delete all items.");
-
-                               // re-map views to their new positions
-                               KeyValuePair<int, global::Android.Views.View>[] viewByAdaptorPositionCopy = _viewByAdaptorPosition.ToArray();
-                               _viewByAdaptorPosition.Clear();
-                               foreach (KeyValuePair<int, global::Android.Views.View> pair in viewByAdaptorPositionCopy)
-                               {
-                                       global::Android.Views.View view = pair.Value;
-                                       int position = pair.Key;
-
-                                       // position unchanged
-                                       if (position < positionStart)
-                                               _viewByAdaptorPosition[position] = view;
-
-                                       // position changed
-                                       else if (position >= positionEnd)
-                                               _viewByAdaptorPosition[position - itemCount] = view;
-
-                                       // removed
-                                       else
-                                       {
-                                               _viewByAdaptorPosition[-1] = view;
-                                               if (_visibleAdapterPosition.Contains(position))
-                                                       _visibleAdapterPosition.Remove(position);
-                                       }
-                               }
-
-                               // if removed origin then shift origin to first removed position
-                               if (_positionOrigin >= positionStart && _positionOrigin < positionEnd)
-                               {
-                                       _positionOrigin = positionStart;
-
-                                       // if no items to right of removed origin then set origin to item prior to removed set
-                                       if (_positionOrigin >= state.ItemCount)
-                                       {
-                                               _positionOrigin = state.ItemCount - 1;
-
-                                               if (!_viewByAdaptorPosition.ContainsKey(_positionOrigin))
-                                                       throw new InvalidOperationException("VirtualLayoutManager must add items to the left and right of the origin");
-                                       }
-                               }
-
-                               // if removed before origin then shift origin left
-                               else if (_positionOrigin >= positionEnd)
-                                       _positionOrigin -= itemCount;
-                       });
-
-                       base.OnItemsRemoved(recyclerView, positionStart, itemCount);
-               }
-
-               public override void OnItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount)
-               {
-                       _adapterChangeType = AdapterChangeType.Updated;
-
-                       // rebind rendered updated elements
-                       _deferredLayout.Enqueue((recycler, state) =>
-                       {
-                               for (var i = 0; i < itemCount; i++)
-                               {
-                                       int position = positionStart + i;
-
-                                       global::Android.Views.View view;
-                                       if (!_viewByAdaptorPosition.TryGetValue(position, out view))
-                                               continue;
-
-                                       recycler.BindViewToPosition(view, position);
-                               }
-                       });
-
-                       base.OnItemsUpdated(recyclerView, positionStart, itemCount);
-               }
-
-               public override void OnLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
-               {
-                       AdapterChangeType adapterChangeType = _adapterChangeType;
-                       if (state.IsPreLayout)
-                               adapterChangeType = default(AdapterChangeType);
-
-                       // adapter updates
-                       if (!state.IsPreLayout)
-                       {
-                               while (_deferredLayout.Count > 0)
-                                       _deferredLayout.Dequeue()(recycler, state);
-                       }
-
-                       // get visible items
-                       int[] positions = _virtualLayout.GetPositions(_positionOrigin, state.ItemCount, Viewport,
-                               // IsPreLayout => some type of data update of yet unknown type. Must assume update 
-                               // could be remove so virtualLayout must +1 off-screen left in case origin is 
-                               // removed and +n off-screen right to slide onscreen if a big item is removed
-                               state.IsPreLayout || adapterChangeType == AdapterChangeType.Removed).ToRange();
-
-                       // disappearing
-                       List<int> disappearing = _viewByAdaptorPosition.Keys.Except(positions).ToList();
-
-                       // defer cleanup of displaced items and lay them out off-screen so they animate off-screen
-                       if (adapterChangeType == AdapterChangeType.Added)
-                       {
-                               positions = positions.Concat(disappearing).OrderBy(o => o).ToArray();
-                               disappearing.Clear();
-                       }
-
-                       // recycle
-                       foreach (int position in disappearing)
-                       {
-                               global::Android.Views.View view = _viewByAdaptorPosition[position];
-
-                               // remove
-                               _viewByAdaptorPosition.Remove(position);
-                               OnAppearingOrDisappearing(position, false);
-
-                               // scrap
-                               new DecoratedView(this, view).DetachAndScrap(recycler);
-                       }
-
-                       // TODO: Generalize
-                       if (adapterChangeType == AdapterChangeType.Removed && _positionOrigin == state.ItemCount - 1)
-                       {
-                               System.Drawing.Rectangle vlayout = _virtualLayout.LayoutItem(_positionOrigin, _positionOrigin);
-                               _locationOffset = new IntVector(vlayout.Width - Width, _locationOffset.Y);
-                       }
-
-                       var nextLocationOffset = new System.Drawing.Point(int.MaxValue, int.MaxValue);
-                       int nextPositionOrigin = int.MaxValue;
-                       foreach (int position in positions)
-                       {
-                               // attach
-                               global::Android.Views.View view;
-                               if (!_viewByAdaptorPosition.TryGetValue(position, out view))
-                                       AddView(_viewByAdaptorPosition[position] = view = recycler.GetViewForPosition(position));
-
-                               // layout
-                               var decoratedView = new DecoratedView(this, view);
-                               System.Drawing.Rectangle layout = _virtualLayout.LayoutItem(_positionOrigin, position);
-                               System.Drawing.Rectangle physicalLayout = layout - _locationOffset;
-                               decoratedView.Layout(physicalLayout);
-
-                               bool isVisible = Viewport.IntersectsWith(layout);
-                               if (isVisible)
-                                       OnAppearingOrDisappearing(position, true);
-
-                               // update offsets
-                               if (isVisible && position < nextPositionOrigin)
-                               {
-                                       nextLocationOffset = layout.Location;
-                                       nextPositionOrigin = position;
-                               }
-                       }
-
-                       // update origin
-                       if (nextPositionOrigin != int.MaxValue)
-                       {
-                               _positionOrigin = nextPositionOrigin;
-                               _locationOffset -= (IntVector)nextLocationOffset;
-                       }
-
-                       // scrapped views not re-attached must be recycled (why isn't this done by Android, I dunno)
-                       foreach (RecyclerView.ViewHolder viewHolder in recycler.ScrapList.ToArray())
-                               recycler.RecycleView(viewHolder.ItemView);
-               }
-
-               public override int ScrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state)
-               {
-                       var delta = new IntVector(dx, 0);
-                       ScrollBy(ref delta, recycler, state);
-                       return delta.X;
-               }
-
-               public override void ScrollToPosition(int adapterPosition)
-               {
-                       if (adapterPosition < 0 || adapterPosition >= ItemCount)
-                               throw new ArgumentException(nameof(adapterPosition));
-
-                       _scroller.TargetPosition = adapterPosition;
-                       StartSmoothScroll(_scroller);
-               }
-
-               public override int ScrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state)
-               {
-                       var delta = new IntVector(0, dy);
-                       ScrollBy(ref delta, recycler, state);
-                       return delta.Y;
-               }
-
-               public override void SmoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int adapterPosition)
-               {
-                       ScrollToPosition(adapterPosition);
-               }
-
-               // entry points
-               public override bool SupportsPredictiveItemAnimations() => true;
-
-               public override string ToString()
-               {
-                       return $"offset={_locationOffset}";
-               }
-
-               public IEnumerable<global::Android.Views.View> Views()
-               {
-                       return _viewByAdaptorPosition.Values;
-               }
-
-               public IEnumerable<int> VisiblePositions()
-               {
-                       return _visibleAdapterPosition;
-               }
-
-               protected override void Dispose(bool disposing)
-               {
-                       base.Dispose(disposing);
-               }
-
-               void OffsetChildren(IntVector delta)
-               {
-                       OffsetChildrenHorizontal(-delta.X);
-                       OffsetChildrenVertical(-delta.Y);
-               }
-
-               void OnAppearingOrDisappearing(int position, bool isAppearing)
-               {
-                       if (isAppearing)
-                       {
-                               if (!_visibleAdapterPosition.Contains(position))
-                               {
-                                       _visibleAdapterPosition.Add(position);
-                                       OnAppearing?.Invoke(position);
-                               }
-                       }
-                       else
-                       {
-                               if (_visibleAdapterPosition.Contains(position))
-                               {
-                                       _visibleAdapterPosition.Remove(position);
-                                       OnDisappearing?.Invoke(position);
-                               }
-                       }
-               }
-
-               void ScrollBy(ref IntVector delta, RecyclerView.Recycler recycler, RecyclerView.State state)
-               {
-                       _adapterChangeType = default(AdapterChangeType);
-
-                       delta = Viewport.BoundTranslation(delta, _virtualLayout.GetBounds(_positionOrigin, state));
-
-                       _locationOffset += delta;
-                       _samples.Insert(0, delta);
-                       _samples.RemoveAt(_samples.Count - 1);
-
-                       OffsetChildren(delta);
-                       OnLayoutChildren(recycler, state);
-               }
-
-               enum AdapterChangeType
-               {
-                       Removed = 1,
-                       Added,
-                       Moved,
-                       Updated,
-                       Changed
-               }
-
-               internal struct DecoratedView
-               {
-                       public static implicit operator global::Android.Views.View(DecoratedView view)
-                       {
-                               return view._view;
-                       }
-
-                       readonly PhysicalLayoutManager _layout;
-                       readonly global::Android.Views.View _view;
-
-                       internal DecoratedView(PhysicalLayoutManager layout, global::Android.Views.View view)
-                       {
-                               _layout = layout;
-                               _view = view;
-                       }
-
-                       internal int Left => _layout.GetDecoratedLeft(_view);
-
-                       internal int Top => _layout.GetDecoratedTop(_view);
-
-                       internal int Bottom => _layout.GetDecoratedBottom(_view);
-
-                       internal int Right => _layout.GetDecoratedRight(_view);
-
-                       internal int Width => Right - Left;
-
-                       internal int Height => Bottom - Top;
-
-                       internal System.Drawing.Rectangle Rectangle => new System.Drawing.Rectangle(Left, Top, Width, Height);
-
-                       internal void Measure(int widthUsed, int heightUsed)
-                       {
-                               _layout.MeasureChild(_view, widthUsed, heightUsed);
-                       }
-
-                       internal void MeasureWithMargins(int widthUsed, int heightUsed)
-                       {
-                               _layout.MeasureChildWithMargins(_view, widthUsed, heightUsed);
-                       }
-
-                       internal void Layout(System.Drawing.Rectangle position)
-                       {
-                               var renderer = _view as IVisualElementRenderer;
-                               renderer.Element.Layout(position.ToFormsRectangle(_layout._context));
-
-                               _layout.LayoutDecorated(_view, position.Left, position.Top, position.Right, position.Bottom);
-                       }
-
-                       internal void Add()
-                       {
-                               _layout.AddView(_view);
-                       }
-
-                       internal void DetachAndScrap(RecyclerView.Recycler recycler)
-                       {
-                               _layout.DetachAndScrapView(_view, recycler);
-                       }
-               }
-
-               internal abstract class VirtualLayoutManager
-               {
-                       internal abstract bool CanScrollHorizontally { get; }
-
-                       internal abstract bool CanScrollVertically { get; }
-
-                       internal abstract System.Drawing.Rectangle GetBounds(int positionOrigin, RecyclerView.State state);
-
-                       internal abstract Tuple<int, int> GetPositions(int positionOrigin, int itemCount, System.Drawing.Rectangle viewport, bool isPreLayout);
-
-                       internal abstract void Layout(int positionOrigin, System.Drawing.Size viewportSize, ref IntVector offset);
-
-                       internal abstract System.Drawing.Rectangle LayoutItem(int positionOrigin, int position);
-               }
-
-               enum SnapPreference
-               {
-                       None = 0,
-                       Begin = 1,
-                       End = -1
-               }
-
-               sealed class SeekAndSnapScroller : LinearSmoothScroller
-               {
-                       readonly SnapPreference _snapPreference;
-                       readonly Func<int, IntVector> _vectorToPosition;
-
-                       internal SeekAndSnapScroller(Context context, Func<int, IntVector> vectorToPosition, SnapPreference snapPreference = SnapPreference.None) : base(context)
-                       {
-                               _vectorToPosition = vectorToPosition;
-                               _snapPreference = snapPreference;
-                       }
-
-                       protected override int HorizontalSnapPreference => (int)_snapPreference;
-
-                       public override PointF ComputeScrollVectorForPosition(int targetPosition)
-                       {
-                               IntVector vector = _vectorToPosition(targetPosition);
-                               return new PointF(vector.X, vector.Y);
-                       }
-
-                       public event Action<int> OnBeginScroll;
-
-                       public event Action<int> OnEndScroll;
-
-                       protected override void OnStart()
-                       {
-                               OnBeginScroll?.Invoke(TargetPosition);
-                               base.OnStart();
-                       }
-
-                       protected override void OnStop()
-                       {
-                               // expected this to be triggered with the animation stops but it
-                               // actually seems to be triggered when the target is found
-                               OnEndScroll?.Invoke(TargetPosition);
-                               base.OnStop();
-                       }
-               }
-       }
-}
\ No newline at end of file
index 05fee22..a9894e3 100644 (file)
     <Compile Include="LayoutExtensions.cs" />
     <Compile Include="Renderers\AHorizontalScrollView.cs" />
     <Compile Include="Renderers\ButtonDrawable.cs" />
-    <Compile Include="Renderers\CarouselViewExtensions.cs" />
     <Compile Include="Renderers\CarouselViewRenderer.cs" />
     <Compile Include="Renderers\AlignmentExtensions.cs" />
     <Compile Include="IStartActivityForResult.cs" />
     <Compile Include="Renderers\IImageSourceHandler.cs" />
     <Compile Include="Renderers\ImageExtensions.cs" />
     <Compile Include="Renderers\ImageLoaderSourceHandler.cs" />
-    <Compile Include="Renderers\IntVector.cs" />
-    <Compile Include="Renderers\ItemViewAdapter.cs" />
     <Compile Include="Renderers\IToolbarButton.cs" />
     <Compile Include="Renderers\KeyboardExtensions.cs" />
     <Compile Include="AppCompat\PickerRenderer.cs" />
     <Compile Include="AppCompat\ViewRenderer.cs" />
     <Compile Include="Renderers\MasterDetailContainer.cs" />
-    <Compile Include="Renderers\MeasureSpecification.cs" />
-    <Compile Include="Renderers\MeasureSpecificationType.cs" />
     <Compile Include="Renderers\PageContainer.cs" />
-    <Compile Include="Renderers\PhysicalLayoutManager.cs" />
     <Compile Include="Renderers\ScrollViewContainer.cs" />
     <Compile Include="Renderers\StreamImagesourceHandler.cs" />
     <Compile Include="Renderers\ToolbarButton.cs" />