summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
diff options
context:
space:
mode:
authorJason Smith <jason.smith@xamarin.com>2016-03-22 13:02:25 -0700
committerJason Smith <jason.smith@xamarin.com>2016-03-22 16:13:41 -0700
commit17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch)
treeb5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
downloadxamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2
xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip
Initial import
Diffstat (limited to 'Xamarin.Forms.Platform.Android/VisualElementRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.Android/VisualElementRenderer.cs392
1 files changed, 392 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
new file mode 100644
index 00000000..41c97b18
--- /dev/null
+++ b/Xamarin.Forms.Platform.Android/VisualElementRenderer.cs
@@ -0,0 +1,392 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using Android.Support.V4.View;
+using Android.Views;
+using AView = Android.Views.View;
+
+namespace Xamarin.Forms.Platform.Android
+{
+ public abstract class VisualElementRenderer<TElement> : FormsViewGroup, IVisualElementRenderer, AView.IOnTouchListener, AView.IOnClickListener, IEffectControlProvider where TElement : VisualElement
+ {
+ readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
+
+ readonly Lazy<GestureDetector> _gestureDetector;
+ readonly PanGestureHandler _panGestureHandler;
+ readonly PinchGestureHandler _pinchGestureHandler;
+
+ readonly TapGestureHandler _tapGestureHandler;
+
+ bool _clickable;
+ NotifyCollectionChangedEventHandler _collectionChangeHandler;
+
+ VisualElementRendererFlags _flags = VisualElementRendererFlags.AutoPackage | VisualElementRendererFlags.AutoTrack;
+
+ InnerGestureListener _gestureListener;
+ VisualElementPackager _packager;
+ PropertyChangedEventHandler _propertyChangeHandler;
+ Lazy<ScaleGestureDetector> _scaleDetector;
+ VelocityTracker _velocity;
+
+ protected VisualElementRenderer() : base(Forms.Context)
+ {
+ _tapGestureHandler = new TapGestureHandler(() => View);
+ _panGestureHandler = new PanGestureHandler(() => View, Context.FromPixels);
+ _pinchGestureHandler = new PinchGestureHandler(() => View);
+
+ _gestureDetector =
+ new Lazy<GestureDetector>(
+ () =>
+ new GestureDetector(
+ _gestureListener =
+ new InnerGestureListener(_tapGestureHandler.OnTap, _tapGestureHandler.TapGestureRecognizers, _panGestureHandler.OnPan, _panGestureHandler.OnPanStarted, _panGestureHandler.OnPanComplete)));
+
+ _scaleDetector =
+ new Lazy<ScaleGestureDetector>(
+ () => new ScaleGestureDetector(Context, new InnerScaleListener(_pinchGestureHandler.OnPinch, _pinchGestureHandler.OnPinchStarted, _pinchGestureHandler.OnPinchEnded), Handler));
+ }
+
+ public TElement Element { get; private set; }
+
+ protected bool AutoPackage
+ {
+ get { return (_flags & VisualElementRendererFlags.AutoPackage) != 0; }
+ set
+ {
+ if (value)
+ _flags |= VisualElementRendererFlags.AutoPackage;
+ else
+ _flags &= ~VisualElementRendererFlags.AutoPackage;
+ }
+ }
+
+ protected bool AutoTrack
+ {
+ get { return (_flags & VisualElementRendererFlags.AutoTrack) != 0; }
+ set
+ {
+ if (value)
+ _flags |= VisualElementRendererFlags.AutoTrack;
+ else
+ _flags &= ~VisualElementRendererFlags.AutoTrack;
+ }
+ }
+
+ View View
+ {
+ get { return Element as View; }
+ }
+
+ void IEffectControlProvider.RegisterEffect(Effect effect)
+ {
+ var platformEffect = effect as PlatformEffect;
+ if (platformEffect != null)
+ OnRegisterEffect(platformEffect);
+ }
+
+ void IOnClickListener.OnClick(AView v)
+ {
+ _tapGestureHandler.OnSingleClick();
+ }
+
+ bool IOnTouchListener.OnTouch(AView v, MotionEvent e)
+ {
+ var handled = false;
+ if (_pinchGestureHandler.IsPinchSupported)
+ {
+ if (!_scaleDetector.IsValueCreated)
+ ScaleGestureDetectorCompat.SetQuickScaleEnabled(_scaleDetector.Value, true);
+ handled = _scaleDetector.Value.OnTouchEvent(e);
+ }
+ return _gestureDetector.Value.OnTouchEvent(e) || handled;
+ }
+
+ VisualElement IVisualElementRenderer.Element
+ {
+ get { return Element; }
+ }
+
+ event EventHandler<VisualElementChangedEventArgs> IVisualElementRenderer.ElementChanged
+ {
+ add { _elementChangedHandlers.Add(value); }
+ remove { _elementChangedHandlers.Remove(value); }
+ }
+
+ public virtual SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint)
+ {
+ Measure(widthConstraint, heightConstraint);
+ return new SizeRequest(new Size(MeasuredWidth, MeasuredHeight), MinimumSize());
+ }
+
+ void IVisualElementRenderer.SetElement(VisualElement element)
+ {
+ if (!(element is TElement))
+ throw new ArgumentException("element is not of type " + typeof(TElement), "element");
+
+ SetElement((TElement)element);
+ }
+
+ public VisualElementTracker Tracker { get; private set; }
+
+ public void UpdateLayout()
+ {
+ Performance.Start();
+ if (Tracker != null)
+ Tracker.UpdateLayout();
+
+ Performance.Stop();
+ }
+
+ public ViewGroup ViewGroup
+ {
+ get { return this; }
+ }
+
+ public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
+
+ public void SetElement(TElement element)
+ {
+ if (element == null)
+ throw new ArgumentNullException("element");
+
+ TElement oldElement = Element;
+ Element = element;
+
+ Performance.Start();
+
+ if (oldElement != null)
+ {
+ oldElement.PropertyChanged -= _propertyChangeHandler;
+ UnsubscribeGestureRecognizers(oldElement);
+ }
+
+ // element may be allowed to be passed as null in the future
+ if (element != null)
+ {
+ Color currentColor = oldElement != null ? oldElement.BackgroundColor : Color.Default;
+ if (element.BackgroundColor != currentColor)
+ UpdateBackgroundColor();
+ }
+
+ if (_propertyChangeHandler == null)
+ _propertyChangeHandler = OnElementPropertyChanged;
+
+ element.PropertyChanged += _propertyChangeHandler;
+ SubscribeGestureRecognizers(element);
+
+ if (oldElement == null)
+ {
+ SetOnClickListener(this);
+ SetOnTouchListener(this);
+ SoundEffectsEnabled = false;
+ }
+
+ InputTransparent = Element.InputTransparent;
+
+ // must be updated AFTER SetOnClickListener is called
+ // SetOnClickListener implicitly calls Clickable = true
+ UpdateGestureRecognizers(true);
+
+ OnElementChanged(new ElementChangedEventArgs<TElement>(oldElement, element));
+
+ if (AutoPackage && _packager == null)
+ SetPackager(new VisualElementPackager(this));
+
+ if (AutoTrack && Tracker == null)
+ SetTracker(new VisualElementTracker(this));
+
+ if (element != null)
+ SendVisualElementInitialized(element, this);
+
+ var controller = (IElementController)oldElement;
+ if (controller != null && controller.EffectControlProvider == this)
+ controller.EffectControlProvider = null;
+
+ controller = element;
+ if (controller != null)
+ controller.EffectControlProvider = this;
+
+ if (element != null && !string.IsNullOrEmpty(element.AutomationId))
+ SetAutomationId(element.AutomationId);
+
+ Performance.Stop();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if ((_flags & VisualElementRendererFlags.Disposed) != 0)
+ return;
+ _flags |= VisualElementRendererFlags.Disposed;
+
+ if (disposing)
+ {
+ if (Tracker != null)
+ {
+ Tracker.Dispose();
+ Tracker = null;
+ }
+
+ if (_packager != null)
+ {
+ _packager.Dispose();
+ _packager = null;
+ }
+
+ if (_scaleDetector != null && _scaleDetector.IsValueCreated)
+ {
+ _scaleDetector.Value.Dispose();
+ _scaleDetector = null;
+ }
+
+ if (_gestureListener != null)
+ {
+ _gestureListener.Dispose();
+ _gestureListener = null;
+ }
+
+ int count = ChildCount;
+ for (var i = 0; i < count; i++)
+ {
+ AView child = GetChildAt(i);
+ child.Dispose();
+ }
+
+ RemoveAllViews();
+
+ if (Element != null)
+ {
+ Element.PropertyChanged -= _propertyChangeHandler;
+ UnsubscribeGestureRecognizers(Element);
+
+ if (Platform.GetRenderer(Element) == this)
+ Platform.SetRenderer(Element, null);
+
+ Element = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected virtual Size MinimumSize()
+ {
+ return new Size();
+ }
+
+ protected virtual void OnElementChanged(ElementChangedEventArgs<TElement> e)
+ {
+ var args = new VisualElementChangedEventArgs(e.OldElement, e.NewElement);
+ for (var i = 0; i < _elementChangedHandlers.Count; i++)
+ _elementChangedHandlers[i](this, args);
+
+ EventHandler<ElementChangedEventArgs<TElement>> changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackgroundColor();
+ else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
+ InputTransparent = Element.InputTransparent;
+ }
+
+ protected override void OnLayout(bool changed, int l, int t, int r, int b)
+ {
+ if (Element == null)
+ return;
+
+ ReadOnlyCollection<Element> children = Element.LogicalChildren;
+ for (var i = 0; i < children.Count; i++)
+ {
+ var visualElement = children[i] as VisualElement;
+ if (visualElement == null)
+ continue;
+
+ IVisualElementRenderer renderer = Platform.GetRenderer(visualElement);
+ renderer?.UpdateLayout();
+ }
+ }
+
+ protected virtual void OnRegisterEffect(PlatformEffect effect)
+ {
+ effect.Container = this;
+ }
+
+ protected virtual void SetAutomationId(string id)
+ {
+ ContentDescription = id;
+ }
+
+ protected void SetPackager(VisualElementPackager packager)
+ {
+ _packager = packager;
+ packager.Load();
+ }
+
+ protected void SetTracker(VisualElementTracker tracker)
+ {
+ Tracker = tracker;
+ }
+
+ protected virtual void UpdateBackgroundColor()
+ {
+ SetBackgroundColor(Element.BackgroundColor.ToAndroid());
+ }
+
+ internal virtual void SendVisualElementInitialized(VisualElement element, AView nativeView)
+ {
+ element.SendViewInitialized(nativeView);
+ }
+
+ void HandleGestureRecognizerCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
+ {
+ UpdateGestureRecognizers();
+ }
+
+ void SubscribeGestureRecognizers(VisualElement element)
+ {
+ var view = element as View;
+ if (view == null)
+ return;
+
+ if (_collectionChangeHandler == null)
+ _collectionChangeHandler = HandleGestureRecognizerCollectionChanged;
+
+ var observableCollection = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ observableCollection.CollectionChanged += _collectionChangeHandler;
+ }
+
+ void UnsubscribeGestureRecognizers(VisualElement element)
+ {
+ var view = element as View;
+ if (view == null || _collectionChangeHandler == null)
+ return;
+
+ var observableCollection = (ObservableCollection<IGestureRecognizer>)view.GestureRecognizers;
+ observableCollection.CollectionChanged -= _collectionChangeHandler;
+ }
+
+ void UpdateClickable(bool force = false)
+ {
+ var view = Element as View;
+ if (view == null)
+ return;
+
+ bool newValue = view.ShouldBeMadeClickable();
+ if (force || _clickable != newValue)
+ Clickable = newValue;
+ }
+
+ void UpdateGestureRecognizers(bool forceClick = false)
+ {
+ if (View == null)
+ return;
+
+ UpdateClickable(forceClick);
+ }
+ }
+} \ No newline at end of file