File: Android\FastRenderers\FrameRenderer.cs
Web Access
Project: src\src\Compatibility\Core\src\Compatibility.csproj (Microsoft.Maui.Controls.Compatibility)
using System;
using System.ComponentModel;
using Android.Content;
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
using AndroidX.CardView.Widget;
using AndroidX.Core.View;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Graphics;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;
using Color = Microsoft.Maui.Graphics.Color;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers
{
	[Obsolete("Use Microsoft.Maui.Controls.Handlers.Compatibility.FrameRenderer instead")]
	public class FrameRenderer : CardView, IVisualElementRenderer, IViewRenderer, ITabStop
	{
		float _defaultElevation = -1f;
		float _defaultCornerRadius = -1f;
		int? _defaultLabelFor;
 
		int _height;
		int _width;
		bool _hasLayoutOccurred;
		bool _disposed;
		Frame _element;
		GradientDrawable _backgroundDrawable;
 
		VisualElementPackager _visualElementPackager;
		VisualElementTracker _visualElementTracker;
		VisualElementRenderer _visualElementRenderer;
 
		readonly MotionEventHelper _motionEventHelper = new MotionEventHelper();
 
		public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
		public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
 
		public FrameRenderer(Context context) : base(context)
		{
			_visualElementRenderer = new VisualElementRenderer(this);
		}
 
		protected CardView Control => this;
 
		AView ITabStop.TabStop => this;
 
		protected Frame Element
		{
			get { return _element; }
			set
			{
				if (_element == value)
					return;
 
				Frame oldElement = _element;
				_element = value;
 
				OnElementChanged(new ElementChangedEventArgs<Frame>(oldElement, _element));
 
				_element?.SendViewInitialized(Control);
			}
		}
 
		VisualElement IVisualElementRenderer.Element => Element;
		AView IVisualElementRenderer.View => this;
 
		SizeRequest IVisualElementRenderer.GetDesiredSize(int widthMeasureSpec, int heightMeasureSpec)
		{
			Context context = Context;
			return new SizeRequest(new Size(context.ToPixels(20), context.ToPixels(20)));
		}
 
		void IVisualElementRenderer.SetElement(VisualElement element)
		{
			var frame = element as Frame;
			if (frame == null)
				throw new ArgumentException("Element must be of type Frame");
			Element = frame;
			_motionEventHelper.UpdateElement(frame);
 
			if (!string.IsNullOrEmpty(Element.AutomationId))
				ContentDescription = Element.AutomationId;
		}
 
		void IVisualElementRenderer.SetLabelFor(int? id)
		{
			if (_defaultLabelFor == null)
				_defaultLabelFor = ViewCompat.GetLabelFor(this);
 
			ViewCompat.SetLabelFor(this, (int)(id ?? _defaultLabelFor));
		}
 
		VisualElementTracker IVisualElementRenderer.Tracker => _visualElementTracker;
 
		void IVisualElementRenderer.UpdateLayout()
		{
			VisualElementTracker tracker = _visualElementTracker;
			tracker?.UpdateLayout();
		}
 
		void IViewRenderer.MeasureExactly()
		{
			ViewRenderer.MeasureExactly(this, Element, Context);
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
				return;
 
			_disposed = true;
 
			if (disposing)
			{
				if (Element != null)
				{
					Element.PropertyChanged -= OnElementPropertyChanged;
				}
 
				if (_visualElementTracker != null)
				{
					_visualElementTracker.Dispose();
					_visualElementTracker = null;
				}
 
				if (_visualElementPackager != null)
				{
					_visualElementPackager.Dispose();
					_visualElementPackager = null;
				}
 
				if (_backgroundDrawable != null)
				{
					_backgroundDrawable.Dispose();
					_backgroundDrawable = null;
				}
 
				if (_visualElementRenderer != null)
				{
					_visualElementRenderer.Dispose();
					_visualElementRenderer = null;
				}
 
				while (ChildCount > 0)
				{
					AView child = GetChildAt(0);
					child.RemoveFromParent();
					child.Dispose();
				}
 
				if (Element != null)
				{
					if (Platform.GetRenderer(Element) == this)
						Element.ClearValue(Platform.RendererProperty);
				}
			}
 
			base.Dispose(disposing);
		}
 
		protected virtual void OnElementChanged(ElementChangedEventArgs<Frame> e)
		{
			ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
 
			if (e.OldElement != null)
			{
				e.OldElement.PropertyChanged -= OnElementPropertyChanged;
			}
 
			if (e.NewElement != null)
			{
				this.EnsureId();
				_backgroundDrawable = new GradientDrawable();
				_backgroundDrawable.SetShape(ShapeType.Rectangle);
				this.SetBackground(_backgroundDrawable);
 
				if (_visualElementTracker == null)
				{
					_visualElementTracker = new VisualElementTracker(this);
					_visualElementPackager = new VisualElementPackager(this);
					_visualElementPackager.Load();
				}
 
				e.NewElement.PropertyChanged += OnElementPropertyChanged;
				UpdateShadow();
				UpdateBackgroundColor();
				UpdateCornerRadius();
				UpdateBorderColor();
				UpdateClippedToBounds();
 
				ElevationHelper.SetElevation(this, e.NewElement);
			}
		}
 
		protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
		{
			if (Element == null)
				return;
 
			var children = ((IElementController)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();
			}
 
			_hasLayoutOccurred = true;
		}
 
		public override void Draw(Canvas canvas)
		{
			canvas.ClipShape(Context, Element);
 
			base.Draw(canvas);
		}
 
		public override bool OnTouchEvent(MotionEvent e)
		{
			if (base.OnTouchEvent(e))
			{
				return true;
			}
 
			return _motionEventHelper.HandleMotionEvent(Parent, e);
		}
 
		protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
		{
			base.OnSizeChanged(w, h, oldw, oldh);
 
			if (w != _width || h != _height)
				UpdateBackground();
		}
 
		protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (this.IsDisposed())
			{
				return;
			}
 
			ElementPropertyChanged?.Invoke(this, e);
 
			if (Control?.LayoutParameters == null && _hasLayoutOccurred)
			{
				return;
			}
 
			if (e.PropertyName == Frame.HasShadowProperty.PropertyName)
				UpdateShadow();
			else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
				UpdateBackgroundColor();
			else if (e.PropertyName == VisualElement.BackgroundProperty.PropertyName)
				UpdateBackground();
			else if (e.PropertyName == Frame.CornerRadiusProperty.PropertyName)
				UpdateCornerRadius();
			else if (e.PropertyName == Frame.BorderColorProperty.PropertyName)
				UpdateBorderColor();
			else if (e.Is(Microsoft.Maui.Controls.Compatibility.Layout.IsClippedToBoundsProperty))
				UpdateClippedToBounds();
		}
 
		void UpdateClippedToBounds()
		{
			var shouldClip = Element.IsSet(Microsoft.Maui.Controls.Compatibility.Layout.IsClippedToBoundsProperty)
					? Element.IsClippedToBounds : Element.CornerRadius > 0f;
 
			this.SetClipToOutline(shouldClip);
		}
 
		void UpdateBackgroundColor()
		{
			if (_disposed)
				return;
 
			Color bgColor = Element.BackgroundColor;
			_backgroundDrawable.SetColor(bgColor?.ToAndroid() ?? AColor.White);
		}
 
		void UpdateBackground()
		{
			if (_disposed)
				return;
 
			Brush background = Element.Background;
 
			if (Brush.IsNullOrEmpty(background))
			{
				if (_backgroundDrawable.UseGradients())
				{
					_backgroundDrawable.Dispose();
					_backgroundDrawable = null;
					this.SetBackground(null);
 
					_backgroundDrawable = new GradientDrawable();
					_backgroundDrawable.SetShape(ShapeType.Rectangle);
					this.SetBackground(_backgroundDrawable);
				}
 
				UpdateBackgroundColor();
			}
			else
			{
				_height = Control.Height;
				_width = Control.Width;
				_backgroundDrawable.UpdateBackground(background, _height, _width);
			}
		}
 
		void UpdateBorderColor()
		{
			if (_disposed)
				return;
 
			Color borderColor = Element.BorderColor;
 
			if (borderColor == null)
				_backgroundDrawable.SetStroke(0, AColor.Transparent);
			else
				_backgroundDrawable.SetStroke(3, borderColor.ToAndroid());
		}
 
		void UpdateShadow()
		{
			if (_disposed)
				return;
 
			float elevation = _defaultElevation;
 
			if (elevation == -1f)
				_defaultElevation = elevation = CardElevation;
 
			if (Element.HasShadow)
				CardElevation = elevation;
			else
				CardElevation = 0f;
		}
 
		void UpdateCornerRadius()
		{
			if (_disposed)
				return;
 
			if (_defaultCornerRadius == -1f)
			{
				_defaultCornerRadius = Radius;
			}
 
			float cornerRadius = Element.CornerRadius;
 
			if (cornerRadius == -1f)
				cornerRadius = _defaultCornerRadius;
			else
				cornerRadius = Context.ToPixels(cornerRadius);
 
			_backgroundDrawable.SetCornerRadius(cornerRadius);
 
			UpdateClippedToBounds();
		}
	}
}