2016-03-22 23:02:25 +03:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
using System.ComponentModel;
|
2017-10-09 20:51:55 +03:00
|
|
|
using Android.Content;
|
2016-03-22 23:02:25 +03:00
|
|
|
using Android.Support.V4.View;
|
|
|
|
using Android.Views;
|
2017-01-09 13:53:15 +03:00
|
|
|
using Xamarin.Forms.Internals;
|
2016-03-22 23:02:25 +03:00
|
|
|
using AView = Android.Views.View;
|
2018-01-02 14:36:21 +03:00
|
|
|
using Xamarin.Forms.Platform.Android.FastRenderers;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
namespace Xamarin.Forms.Platform.Android
|
|
|
|
{
|
2017-09-15 21:49:19 +03:00
|
|
|
public abstract class VisualElementRenderer<TElement> : FormsViewGroup, IVisualElementRenderer,
|
|
|
|
IEffectControlProvider where TElement : VisualElement
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
readonly List<EventHandler<VisualElementChangedEventArgs>> _elementChangedHandlers = new List<EventHandler<VisualElementChangedEventArgs>>();
|
|
|
|
|
|
|
|
VisualElementRendererFlags _flags = VisualElementRendererFlags.AutoPackage | VisualElementRendererFlags.AutoTrack;
|
|
|
|
|
2017-01-31 22:49:15 +03:00
|
|
|
string _defaultContentDescription;
|
|
|
|
bool? _defaultFocusable;
|
|
|
|
string _defaultHint;
|
2018-02-01 13:47:24 +03:00
|
|
|
bool _inputTransparentInherited = true;
|
2018-01-02 14:36:21 +03:00
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
VisualElementPackager _packager;
|
|
|
|
PropertyChangedEventHandler _propertyChangeHandler;
|
2017-09-15 21:49:19 +03:00
|
|
|
|
|
|
|
readonly GestureManager _gestureManager;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2017-10-09 20:51:55 +03:00
|
|
|
protected VisualElementRenderer(Context context) : base(context)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2017-09-15 21:49:19 +03:00
|
|
|
_gestureManager = new GestureManager(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnTouchEvent(MotionEvent e)
|
|
|
|
{
|
2017-10-16 20:38:50 +03:00
|
|
|
return _gestureManager.OnTouchEvent(e) || base.OnTouchEvent(e);
|
2017-09-15 21:49:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public override bool OnInterceptTouchEvent(MotionEvent ev)
|
|
|
|
{
|
|
|
|
if (!Enabled)
|
|
|
|
{
|
|
|
|
// If Enabled is false, prevent all the events from being dispatched to child Views
|
|
|
|
// and prevent them from being processed by this View as well
|
|
|
|
return true; // IOW, intercepted
|
|
|
|
}
|
|
|
|
|
|
|
|
return base.OnInterceptTouchEvent(ev);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override bool DispatchTouchEvent(MotionEvent e)
|
|
|
|
{
|
2018-02-01 13:47:24 +03:00
|
|
|
if (InputTransparent && _inputTransparentInherited)
|
2017-09-15 21:49:19 +03:00
|
|
|
{
|
|
|
|
// If the Element is InputTransparent, this ViewGroup will be marked InputTransparent
|
2018-02-01 13:47:24 +03:00
|
|
|
// If we're InputTransparent and our transparency should be applied to our child controls,
|
|
|
|
// we return false on all touch events without even bothering to send them to the child Views
|
2017-09-15 21:49:19 +03:00
|
|
|
|
|
|
|
return false; // IOW, not handled
|
|
|
|
}
|
|
|
|
|
|
|
|
return base.DispatchTouchEvent(e);
|
2017-10-09 20:51:55 +03:00
|
|
|
}
|
|
|
|
|
2017-10-20 23:02:16 +03:00
|
|
|
[Obsolete("This constructor is obsolete as of version 2.5. Please use VisualElementRenderer(Context) instead.")]
|
2017-10-09 20:51:55 +03:00
|
|
|
protected VisualElementRenderer() : this(Forms.Context)
|
|
|
|
{
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:01:31 +03:00
|
|
|
View View => Element as View;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
void IEffectControlProvider.RegisterEffect(Effect effect)
|
|
|
|
{
|
|
|
|
var platformEffect = effect as PlatformEffect;
|
|
|
|
if (platformEffect != null)
|
|
|
|
OnRegisterEffect(platformEffect);
|
|
|
|
}
|
|
|
|
|
2016-09-30 22:01:31 +03:00
|
|
|
VisualElement IVisualElementRenderer.Element => Element;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
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))
|
2016-09-30 22:01:31 +03:00
|
|
|
throw new ArgumentException("element is not of type " + typeof(TElement), nameof(element));
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
SetElement((TElement)element);
|
|
|
|
}
|
|
|
|
|
|
|
|
public VisualElementTracker Tracker { get; private set; }
|
|
|
|
|
|
|
|
public void UpdateLayout()
|
|
|
|
{
|
2018-05-23 02:54:21 +03:00
|
|
|
Performance.Start(out string reference);
|
2016-09-30 22:01:31 +03:00
|
|
|
Tracker?.UpdateLayout();
|
2018-01-31 21:09:15 +03:00
|
|
|
Performance.Stop(reference);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2016-09-30 22:01:31 +03:00
|
|
|
public ViewGroup ViewGroup => this;
|
2017-04-06 16:19:52 +03:00
|
|
|
AView IVisualElementRenderer.View => this;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
public event EventHandler<ElementChangedEventArgs<TElement>> ElementChanged;
|
2017-04-06 16:19:52 +03:00
|
|
|
public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
public void SetElement(TElement element)
|
|
|
|
{
|
|
|
|
if (element == null)
|
2016-09-30 22:01:31 +03:00
|
|
|
throw new ArgumentNullException(nameof(element));
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
TElement oldElement = Element;
|
|
|
|
Element = element;
|
|
|
|
|
2018-01-31 21:09:15 +03:00
|
|
|
var reference = Guid.NewGuid().ToString();
|
|
|
|
Performance.Start(reference);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
if (oldElement != null)
|
|
|
|
{
|
|
|
|
oldElement.PropertyChanged -= _propertyChangeHandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
if (oldElement == null)
|
|
|
|
{
|
|
|
|
SoundEffectsEnabled = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-01-09 13:53:15 +03:00
|
|
|
EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
if (element != null && !string.IsNullOrEmpty(element.AutomationId))
|
|
|
|
SetAutomationId(element.AutomationId);
|
|
|
|
|
2017-01-31 22:49:15 +03:00
|
|
|
SetContentDescription();
|
|
|
|
SetFocusable();
|
2017-03-23 20:18:38 +03:00
|
|
|
UpdateInputTransparent();
|
2018-02-01 13:47:24 +03:00
|
|
|
UpdateInputTransparentInherited();
|
2017-01-31 22:49:15 +03:00
|
|
|
|
2018-01-31 21:09:15 +03:00
|
|
|
Performance.Stop(reference);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
2016-04-24 09:13:27 +03:00
|
|
|
/// <summary>
|
|
|
|
/// Determines whether the native control is disposed of when this renderer is disposed
|
|
|
|
/// Can be overridden in deriving classes
|
|
|
|
/// </summary>
|
|
|
|
protected virtual bool ManageNativeControlLifetime => true;
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
protected override void Dispose(bool disposing)
|
|
|
|
{
|
|
|
|
if ((_flags & VisualElementRendererFlags.Disposed) != 0)
|
|
|
|
return;
|
|
|
|
_flags |= VisualElementRendererFlags.Disposed;
|
|
|
|
|
|
|
|
if (disposing)
|
|
|
|
{
|
2016-09-27 13:51:22 +03:00
|
|
|
SetOnClickListener(null);
|
|
|
|
SetOnTouchListener(null);
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
if (Tracker != null)
|
|
|
|
{
|
|
|
|
Tracker.Dispose();
|
|
|
|
Tracker = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_packager != null)
|
|
|
|
{
|
|
|
|
_packager.Dispose();
|
|
|
|
_packager = null;
|
|
|
|
}
|
|
|
|
|
2016-04-24 09:13:27 +03:00
|
|
|
if (ManageNativeControlLifetime)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
2016-04-24 09:13:27 +03:00
|
|
|
int count = ChildCount;
|
|
|
|
for (var i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
AView child = GetChildAt(i);
|
|
|
|
child.Dispose();
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
RemoveAllViews();
|
|
|
|
|
|
|
|
if (Element != null)
|
|
|
|
{
|
|
|
|
Element.PropertyChanged -= _propertyChangeHandler;
|
|
|
|
|
|
|
|
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);
|
2016-09-30 22:01:31 +03:00
|
|
|
foreach (EventHandler<VisualElementChangedEventArgs> handler in _elementChangedHandlers)
|
|
|
|
handler(this, args);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2016-09-30 22:01:31 +03:00
|
|
|
ElementChanged?.Invoke(this, e);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2017-09-26 13:14:41 +03:00
|
|
|
ElevationHelper.SetElevation(this, e.NewElement);
|
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
|
|
{
|
|
|
|
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
|
|
|
|
UpdateBackgroundColor();
|
2017-05-10 20:20:57 +03:00
|
|
|
else if (e.PropertyName == AutomationProperties.HelpTextProperty.PropertyName)
|
2017-01-31 22:49:15 +03:00
|
|
|
SetContentDescription();
|
2017-05-10 20:20:57 +03:00
|
|
|
else if (e.PropertyName == AutomationProperties.NameProperty.PropertyName)
|
2017-01-31 22:49:15 +03:00
|
|
|
SetContentDescription();
|
2017-05-10 20:20:57 +03:00
|
|
|
else if (e.PropertyName == AutomationProperties.IsInAccessibleTreeProperty.PropertyName)
|
2017-01-31 22:49:15 +03:00
|
|
|
SetFocusable();
|
2017-03-23 20:18:38 +03:00
|
|
|
else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
|
|
|
|
UpdateInputTransparent();
|
2018-02-02 15:00:00 +03:00
|
|
|
else if (e.PropertyName == Xamarin.Forms.Layout.CascadeInputTransparentProperty.PropertyName)
|
2018-02-01 13:47:24 +03:00
|
|
|
UpdateInputTransparentInherited();
|
2017-04-06 16:19:52 +03:00
|
|
|
|
|
|
|
ElementPropertyChanged?.Invoke(this, e);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnLayout(bool changed, int l, int t, int r, int b)
|
|
|
|
{
|
|
|
|
if (Element == null)
|
|
|
|
return;
|
|
|
|
|
2017-09-13 12:18:59 +03:00
|
|
|
UpdateLayout(((IElementController)Element).LogicalChildren);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void UpdateLayout(IEnumerable<Element> children)
|
|
|
|
{
|
|
|
|
foreach (Element element in children) {
|
2016-09-30 22:01:31 +03:00
|
|
|
var visualElement = element as VisualElement;
|
2016-03-22 23:02:25 +03:00
|
|
|
if (visualElement == null)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
IVisualElementRenderer renderer = Platform.GetRenderer(visualElement);
|
2017-09-13 12:18:59 +03:00
|
|
|
if (renderer == null && CompressedLayout.GetIsHeadless(visualElement))
|
|
|
|
UpdateLayout(visualElement.LogicalChildren);
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
renderer?.UpdateLayout();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void OnRegisterEffect(PlatformEffect effect)
|
|
|
|
{
|
2017-03-07 22:56:24 +03:00
|
|
|
effect.SetContainer(this);
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
protected virtual void SetAutomationId(string id)
|
2018-01-02 14:36:21 +03:00
|
|
|
=> AutomationPropertiesProvider.SetAutomationId(this, Element, id);
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2017-01-31 22:49:15 +03:00
|
|
|
protected virtual void SetContentDescription()
|
2018-01-02 14:36:21 +03:00
|
|
|
=> AutomationPropertiesProvider.SetContentDescription(this, Element, ref _defaultContentDescription, ref _defaultHint);
|
2017-01-31 22:49:15 +03:00
|
|
|
|
|
|
|
protected virtual void SetFocusable()
|
2018-01-02 14:36:21 +03:00
|
|
|
=> AutomationPropertiesProvider.SetFocusable(this, Element, ref _defaultFocusable);
|
2017-01-31 22:49:15 +03:00
|
|
|
|
2017-03-23 20:18:38 +03:00
|
|
|
void UpdateInputTransparent()
|
|
|
|
{
|
|
|
|
InputTransparent = Element.InputTransparent;
|
|
|
|
}
|
|
|
|
|
2018-02-01 13:47:24 +03:00
|
|
|
void UpdateInputTransparentInherited()
|
|
|
|
{
|
|
|
|
var layout = Element as Layout;
|
|
|
|
|
|
|
|
if (layout == null)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-02 15:00:00 +03:00
|
|
|
_inputTransparentInherited = layout.CascadeInputTransparent;
|
2018-02-01 13:47:24 +03:00
|
|
|
}
|
|
|
|
|
2016-03-22 23:02:25 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-01-31 22:49:15 +03:00
|
|
|
void IVisualElementRenderer.SetLabelFor(int? id)
|
2018-01-02 14:36:21 +03:00
|
|
|
=> LabelFor = id ?? LabelFor;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|