File: Android\AppCompat\ImageButtonRenderer.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.AppCompat.Widget;
using Microsoft.Extensions.Logging;
using Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
using Microsoft.Maui.Graphics;
using AColor = Android.Graphics.Color;
using AView = Android.Views.View;
 
namespace Microsoft.Maui.Controls.Compatibility.Platform.Android
{
	[System.Obsolete(Compatibility.Hosting.MauiAppBuilderExtensions.UseMapperInstead)]
	public class ImageButtonRenderer :
		AppCompatImageButton,
		IVisualElementRenderer,
		IBorderVisualElementRenderer,
		IImageRendererController,
		AView.IOnFocusChangeListener,
		AView.IOnClickListener,
		AView.IOnTouchListener,
		ILayoutChanges,
		IDisposedState
	{
		bool _hasLayoutOccurred;
		bool _inputTransparent;
		bool _disposed;
		bool _skipInvalidate;
		int? _defaultLabelFor;
		VisualElementTracker _tracker;
		VisualElementRenderer _visualElementRenderer;
		BorderBackgroundManager _backgroundTracker;
		IPlatformElementConfiguration<PlatformConfiguration.Android, ImageButton> _platformElementConfiguration;
		ImageButton _imageButton;
 
		public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
		public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
 
		void IVisualElementRenderer.UpdateLayout() => _tracker?.UpdateLayout();
		VisualElement IVisualElementRenderer.Element => Element;
		AView IVisualElementRenderer.View => this;
		VisualElementTracker IVisualElementRenderer.Tracker => _tracker;
		bool IDisposedState.IsDisposed => ((IImageRendererController)this).IsDisposed;
 
		public ImageButton Element
		{
			get => _imageButton;
			private set
			{
				_imageButton = value;
				_platformElementConfiguration = null;
			}
		}
 
		void IImageRendererController.SkipInvalidate() => _skipInvalidate = true;
		bool IImageRendererController.IsDisposed => _disposed || !Control.IsAlive();
 
		AppCompatImageButton Control => this;
		public ImageButtonRenderer(Context context) : base(context)
		{
			// These set the defaults so visually it matches up with other platforms
			SetPadding(0, 0, 0, 0);
			SoundEffectsEnabled = false;
			SetOnClickListener(this);
			SetOnTouchListener(this);
			OnFocusChangeListener = this;
 
			// Setting the tag will break Glide
			// Tag = this;
 
			_backgroundTracker = new BorderBackgroundManager(this, false);
		}
 
		protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
		{
			base.OnLayout(changed, left, top, right, bottom);
			_hasLayoutOccurred = true;
		}
 
		protected override void Dispose(bool disposing)
		{
			if (_disposed)
				return;
 
			_disposed = true;
 
			if (disposing)
			{
				if (Element != null)
				{
					Element.PropertyChanged -= OnElementPropertyChanged;
				}
 
				SetOnClickListener(null);
				SetOnTouchListener(null);
				OnFocusChangeListener = null;
 
				ImageElementManager.Dispose(this);
 
				_tracker?.Dispose();
				_tracker = null;
 
				_backgroundTracker?.Dispose();
				_backgroundTracker = null;
 
				if (Element != null)
				{
					if (Platform.GetRenderer(Element) == this)
					{
						Element.ClearValue(Platform.RendererProperty);
					}
 
					Element = null;
				}
			}
 
			base.Dispose(disposing);
		}
 
		public override void Invalidate()
		{
			if (_skipInvalidate)
			{
				_skipInvalidate = false;
				return;
			}
 
			base.Invalidate();
		}
 
		Size MinimumSize()
		{
			return new Size();
		}
 
		SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
		{
			if (_disposed)
			{
				return new SizeRequest();
			}
			Measure(widthConstraint, heightConstraint);
			return new SizeRequest(new Size(MeasuredWidth, MeasuredHeight), MinimumSize());
		}
 
		void IVisualElementRenderer.SetElement(VisualElement element)
		{
 
			if (element == null)
			{
				throw new ArgumentNullException(nameof(element));
			}
 
			if (!(element is ImageButton image))
			{
				throw new ArgumentException("Element is not of type " + typeof(ImageButton), nameof(element));
			}
 
			ImageButton oldElement = Element;
			Element = image;
 
			Performance.Start(out string reference);
 
			if (oldElement != null)
			{
				oldElement.PropertyChanged -= OnElementPropertyChanged;
			}
 
			element.PropertyChanged += OnElementPropertyChanged;
 
			if (_tracker == null)
			{
				_tracker = new VisualElementTracker(this);
				ImageElementManager.Init(this);
 
			}
 
			if (_visualElementRenderer == null)
			{
				_visualElementRenderer = new VisualElementRenderer(this);
			}
 
			Performance.Stop(reference);
			this.EnsureId();
 
			UpdateInputTransparent();
			UpdatePadding();
 
			OnElementChanged(new ElementChangedEventArgs<ImageButton>(oldElement, Element));
			Element?.SendViewInitialized(Control);
		}
 
		protected virtual void OnElementChanged(ElementChangedEventArgs<ImageButton> e)
		{
			ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
		}
 
		public override void Draw(Canvas canvas)
		{
			if (Element == null)
				return;
 
			canvas.ClipShape(Context, Element);
 
			var backgroundDrawable = _backgroundTracker?.BackgroundDrawable;
			global::Android.Graphics.RectF drawableBounds = null;
 
			if (Drawable != null)
			{
				if (backgroundDrawable != null)
				{
					var outlineBounds = backgroundDrawable.GetPaddingBounds(canvas.Width, canvas.Height);
					var width = (float)canvas.Width;
					var height = (float)canvas.Height;
					var widthRatio = 1f;
					var heightRatio = 1f;
 
					if (Element.Aspect == Aspect.AspectFill && OnThisPlatform().GetIsShadowEnabled())
						Application.Current?.FindMauiContext()?.CreateLogger<ImageButtonRenderer>()?.LogWarning("AspectFill isn't fully supported when using shadows. Image may be clipped incorrectly to Border");
 
					switch (Element.Aspect)
					{
						case Aspect.Fill:
							break;
						case Aspect.AspectFill:
						case Aspect.AspectFit:
							heightRatio = (float)Drawable.IntrinsicHeight / height;
							widthRatio = (float)Drawable.IntrinsicWidth / width;
							break;
					}
 
					drawableBounds = new global::Android.Graphics.RectF(outlineBounds.Left * widthRatio, outlineBounds.Top * heightRatio, outlineBounds.Right * widthRatio, outlineBounds.Bottom * heightRatio);
				}
 
				if (drawableBounds != null)
					Drawable.SetBounds((int)drawableBounds.Left, (int)drawableBounds.Top, (int)drawableBounds.Right, (int)drawableBounds.Bottom);
			}
 
			if (_backgroundTracker?.BackgroundDrawable != null)
			{
				_backgroundTracker.BackgroundDrawable.DrawCircle(canvas, canvas.Width, canvas.Height, base.Draw);
				_backgroundTracker.BackgroundDrawable.DrawOutline(canvas, canvas.Width, canvas.Height);
			}
			else
			{
				base.Draw(canvas);
			}
		}
 
		void IVisualElementRenderer.SetLabelFor(int? id)
		{
			if (_defaultLabelFor == null)
				_defaultLabelFor = LabelFor;
 
			LabelFor = (int)(id ?? _defaultLabelFor);
		}
 
		public override bool OnTouchEvent(MotionEvent e)
		{
			if (!Enabled || (_inputTransparent && Enabled))
				return false;
 
			return base.OnTouchEvent(e);
		}
 
		[PortHandler]
		void UpdatePadding()
		{
			SetPadding(
				(int)(Context.ToPixels(Element.Padding.Left)),
				(int)(Context.ToPixels(Element.Padding.Top)),
				(int)(Context.ToPixels(Element.Padding.Right)),
				(int)(Context.ToPixels(Element.Padding.Bottom))
			);
		}
 
		void UpdateInputTransparent()
		{
			if (Element == null || _disposed)
			{
				return;
			}
 
			_inputTransparent = Element.InputTransparent;
		}
 
		protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			if (this.IsDisposed())
			{
				return;
			}
 
			if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
				UpdateInputTransparent();
			else if (e.PropertyName == ImageButton.PaddingProperty.PropertyName)
				UpdatePadding();
 
			ElementPropertyChanged?.Invoke(this, e);
		}
 
		[PortHandler]
		// general state related
		void IOnFocusChangeListener.OnFocusChange(AView v, bool hasFocus)
		{
			((IElementController)Element).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, hasFocus);
		}
		// general state related
 
 
		// Button related
		void IOnClickListener.OnClick(AView v) =>
			ButtonElementManager.OnClick(Element, Element, v);
 
		bool IOnTouchListener.OnTouch(AView v, MotionEvent e) =>
			ButtonElementManager.OnTouch(Element, Element, v, e);
		// Button related
 
		float IBorderVisualElementRenderer.ShadowRadius => Context.ToPixels(OnThisPlatform().GetShadowRadius());
		float IBorderVisualElementRenderer.ShadowDx => Context.ToPixels(OnThisPlatform().GetShadowOffset().Width);
		float IBorderVisualElementRenderer.ShadowDy => Context.ToPixels(OnThisPlatform().GetShadowOffset().Height);
		AColor IBorderVisualElementRenderer.ShadowColor => OnThisPlatform().GetShadowColor().ToAndroid(Colors.Black);
		bool IBorderVisualElementRenderer.IsShadowEnabled() => OnThisPlatform().GetIsShadowEnabled();
		bool IBorderVisualElementRenderer.UseDefaultPadding() => false;
		bool IBorderVisualElementRenderer.UseDefaultShadow() => false;
		VisualElement IBorderVisualElementRenderer.Element => Element;
		AView IBorderVisualElementRenderer.View => this;
 
		bool ILayoutChanges.HasLayoutOccurred => _hasLayoutOccurred;
 
		IPlatformElementConfiguration<PlatformConfiguration.Android, ImageButton> OnThisPlatform()
		{
			if (_platformElementConfiguration == null)
				_platformElementConfiguration = Element.OnThisPlatform();
 
			return _platformElementConfiguration;
		}
 
		void IImageRendererController.SetFormsAnimationDrawable(IFormsAnimationDrawable value)
		{
		}
	}
}