summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Android
diff options
context:
space:
mode:
authorkingces95 <kingces95@users.noreply.github.com>2016-04-04 09:37:32 -0700
committerkingces95 <kingces95@users.noreply.github.com>2016-04-04 09:37:32 -0700
commitbcf7067324214e2e6ae7453e9411d698e72cf1a0 (patch)
tree7352a5459d797b4e4ac5fe0305b7b32f80858f10 /Xamarin.Forms.Platform.Android
parent98f83de04b97def3a4379406101af4507a90b7e1 (diff)
parenta470dd255d3049e3f535db6cf77903c1bd80af7c (diff)
downloadxamarin-forms-bcf7067324214e2e6ae7453e9411d698e72cf1a0.tar.gz
xamarin-forms-bcf7067324214e2e6ae7453e9411d698e72cf1a0.tar.bz2
xamarin-forms-bcf7067324214e2e6ae7453e9411d698e72cf1a0.zip
Merge pull request #39 from xamarin/unformat
Unformat CarouselViewRenderer, recombine files; no code changes
Diffstat (limited to 'Xamarin.Forms.Platform.Android')
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs69
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs1179
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/IntVector.cs86
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/ItemViewAdapter.cs89
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/MeasureSpecification.cs41
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/MeasureSpecificationType.cs13
-rw-r--r--Xamarin.Forms.Platform.Android/Renderers/PhysicalLayoutManager.cs531
-rw-r--r--Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj6
8 files changed, 1025 insertions, 989 deletions
diff --git a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs b/Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs
deleted file mode 100644
index 6349b2b7..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewExtensions.cs
+++ /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
diff --git a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs
index f63b6fd9..d563b7ff 100644
--- a/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs
+++ b/Xamarin.Forms.Platform.Android/Renderers/CarouselViewRenderer.cs
@@ -1,85 +1,425 @@
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
index e5e0986c..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/IntVector.cs
+++ /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
index 84dfa171..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/ItemViewAdapter.cs
+++ /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
index fe38a560..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecification.cs
+++ /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
index bb61d841..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/MeasureSpecificationType.cs
+++ /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
index 64e859d7..00000000
--- a/Xamarin.Forms.Platform.Android/Renderers/PhysicalLayoutManager.cs
+++ /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
diff --git a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
index 05fee22d..a9894e3e 100644
--- a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
+++ b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj
@@ -140,7 +140,6 @@
<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" />
@@ -158,17 +157,12 @@
<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" />