зеркало из https://github.com/DeGsoft/maui-linux.git
368 строки
11 KiB
C#
368 строки
11 KiB
C#
#if __ANDROID_28__
|
|
using System;
|
|
using System.ComponentModel;
|
|
using Android.Content;
|
|
using Android.Content.Res;
|
|
using Android.Graphics;
|
|
using Android.Support.V4.View;
|
|
using Android.Support.V7.Widget;
|
|
using Android.Util;
|
|
using Android.Views;
|
|
using Xamarin.Forms;
|
|
using Xamarin.Forms.Internals;
|
|
using Xamarin.Forms.Platform.Android.FastRenderers;
|
|
using Xamarin.Forms.Material.Android;
|
|
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
|
|
using AColor = Android.Graphics.Color;
|
|
using AView = Android.Views.View;
|
|
using MButton = Android.Support.Design.Button.MaterialButton;
|
|
using Xamarin.Forms.Platform.Android;
|
|
|
|
|
|
namespace Xamarin.Forms.Material.Android
|
|
{
|
|
public class MaterialButtonRenderer : MButton,
|
|
IBorderVisualElementRenderer, IButtonLayoutRenderer, IVisualElementRenderer, IViewRenderer, ITabStop,
|
|
AView.IOnAttachStateChangeListener, AView.IOnFocusChangeListener, AView.IOnClickListener, AView.IOnTouchListener
|
|
{
|
|
int _defaultCornerRadius = -1;
|
|
int _defaultBorderWidth = -1;
|
|
ColorStateList _defaultBorderColor;
|
|
float _defaultFontSize = -1;
|
|
int? _defaultLabelFor;
|
|
Typeface _defaultTypeface;
|
|
bool _disposed;
|
|
bool _inputTransparent;
|
|
Button _button;
|
|
IPlatformElementConfiguration<PlatformConfiguration.Android, Button> _platformElementConfiguration;
|
|
VisualElementTracker _tracker;
|
|
VisualElementRenderer _visualElementRenderer;
|
|
ButtonLayoutManager _buttonLayoutManager;
|
|
readonly AutomationPropertiesProvider _automationPropertiesProvider;
|
|
|
|
public MaterialButtonRenderer(Context context)
|
|
: this(MaterialContextThemeWrapper.Create(context), null) { }
|
|
|
|
public MaterialButtonRenderer(Context context, BindableObject element)
|
|
: base(MaterialContextThemeWrapper.Create(context))
|
|
{
|
|
_automationPropertiesProvider = new AutomationPropertiesProvider(this);
|
|
_buttonLayoutManager = new ButtonLayoutManager(this,
|
|
alignIconWithText: true,
|
|
preserveInitialPadding: true,
|
|
borderAdjustsPadding: false,
|
|
maintainLegacyMeasurements: false);
|
|
|
|
SoundEffectsEnabled = false;
|
|
SetOnClickListener(this);
|
|
SetOnTouchListener(this);
|
|
AddOnAttachStateChangeListener(this);
|
|
OnFocusChangeListener = this;
|
|
|
|
Tag = this;
|
|
}
|
|
|
|
public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
|
|
public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
|
|
protected MButton Control => this;
|
|
|
|
protected Button Element
|
|
{
|
|
get => _button;
|
|
set
|
|
{
|
|
if (_button == value)
|
|
return;
|
|
|
|
var oldElement = _button;
|
|
|
|
_button = value;
|
|
_platformElementConfiguration = null;
|
|
|
|
Performance.Start(out string reference);
|
|
|
|
if (oldElement != null)
|
|
{
|
|
oldElement.PropertyChanged -= OnElementPropertyChanged;
|
|
}
|
|
|
|
_button.PropertyChanged += OnElementPropertyChanged;
|
|
|
|
// Can't set up the tracker in the constructor because it access the Element (for now)
|
|
if (_tracker == null)
|
|
_tracker = new VisualElementTracker(this);
|
|
|
|
if (_visualElementRenderer == null)
|
|
_visualElementRenderer = new VisualElementRenderer(this);
|
|
|
|
OnElementChanged(new ElementChangedEventArgs<Button>(oldElement, Element));
|
|
|
|
_button.SendViewInitialized(this);
|
|
|
|
Performance.Stop(reference);
|
|
}
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (_disposed)
|
|
return;
|
|
|
|
_disposed = true;
|
|
|
|
if (disposing)
|
|
{
|
|
SetOnClickListener(null);
|
|
SetOnTouchListener(null);
|
|
RemoveOnAttachStateChangeListener(this);
|
|
|
|
_automationPropertiesProvider?.Dispose();
|
|
_tracker?.Dispose();
|
|
_visualElementRenderer?.Dispose();
|
|
_buttonLayoutManager?.Dispose();
|
|
_buttonLayoutManager = null;
|
|
|
|
if (Element != null)
|
|
{
|
|
Element.PropertyChanged -= OnElementPropertyChanged;
|
|
|
|
if (Platform.Android.Platform.GetRenderer(Element) == this)
|
|
Element.ClearValue(Platform.Android.Platform.RendererProperty);
|
|
}
|
|
}
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
public override bool OnTouchEvent(MotionEvent e)
|
|
{
|
|
if (!Enabled || (_inputTransparent && Enabled))
|
|
return false;
|
|
|
|
return base.OnTouchEvent(e);
|
|
}
|
|
|
|
protected virtual void OnElementChanged(ElementChangedEventArgs<Button> e)
|
|
{
|
|
if (e.NewElement != null && !_disposed)
|
|
{
|
|
this.EnsureId();
|
|
|
|
_buttonLayoutManager?.Update();
|
|
UpdateBorder();
|
|
UpdateFont();
|
|
UpdatePrimaryColors();
|
|
UpdateInputTransparent();
|
|
|
|
ElevationHelper.SetElevation(this, e.NewElement);
|
|
}
|
|
|
|
ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
|
|
}
|
|
|
|
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == Button.BorderWidthProperty.PropertyName || e.PropertyName == Button.BorderColorProperty.PropertyName || e.PropertyName == Button.CornerRadiusProperty.PropertyName)
|
|
UpdateBorder();
|
|
else if (e.PropertyName == Button.FontProperty.PropertyName)
|
|
UpdateFont();
|
|
else if (e.PropertyName == Button.TextColorProperty.PropertyName || e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
|
|
UpdatePrimaryColors();
|
|
else if (e.PropertyName == VisualElement.InputTransparentProperty.PropertyName)
|
|
UpdateInputTransparent();
|
|
|
|
ElementPropertyChanged?.Invoke(this, e);
|
|
}
|
|
|
|
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
|
|
{
|
|
_buttonLayoutManager?.OnLayout(changed, left, top, right, bottom);
|
|
base.OnLayout(changed, left, top, right, bottom);
|
|
}
|
|
|
|
void UpdateFont()
|
|
{
|
|
if (_disposed || Element == null)
|
|
return;
|
|
|
|
Font font = Element.Font;
|
|
if (font == Font.Default && _defaultTypeface == null && _defaultFontSize < 0f)
|
|
return;
|
|
|
|
if (_defaultTypeface == null)
|
|
_defaultTypeface = Typeface;
|
|
|
|
if (font == Font.Default)
|
|
Typeface = _defaultTypeface;
|
|
else
|
|
Typeface = font.ToTypeface();
|
|
|
|
if (_defaultFontSize < 0f)
|
|
_defaultFontSize = TextSize;
|
|
|
|
if (Element.FontSize < 0f)
|
|
SetTextSize(ComplexUnitType.Px, _defaultFontSize);
|
|
else
|
|
SetTextSize(ComplexUnitType.Sp, font.ToScaledPixel());
|
|
}
|
|
|
|
void UpdateBorder()
|
|
{
|
|
if (_disposed || Element == null)
|
|
return;
|
|
|
|
var cornerRadius = Element.CornerRadius;
|
|
if (cornerRadius >= 0f || _defaultCornerRadius >= 0f)
|
|
{
|
|
if (_defaultCornerRadius < 0f)
|
|
_defaultCornerRadius = CornerRadius;
|
|
|
|
if (cornerRadius < 0f)
|
|
CornerRadius = _defaultCornerRadius;
|
|
else
|
|
CornerRadius = (int)Context.ToPixels(cornerRadius);
|
|
}
|
|
|
|
Color borderColor = Element.BorderColor;
|
|
if (!borderColor.IsDefault || _defaultBorderColor != null)
|
|
{
|
|
if (_defaultBorderColor == null)
|
|
_defaultBorderColor = StrokeColor;
|
|
|
|
if (borderColor.IsDefault)
|
|
StrokeColor = _defaultBorderColor;
|
|
else
|
|
StrokeColor = new ColorStateList(new[] { new int[0] }, new int[] { borderColor.ToAndroid() });
|
|
}
|
|
|
|
double borderWidth = Element.BorderWidth;
|
|
if (borderWidth >= 0f || _defaultBorderWidth >= 0f)
|
|
{
|
|
if (_defaultBorderWidth < 0)
|
|
_defaultBorderWidth = StrokeWidth;
|
|
|
|
// TODO: The Material button does not support borders:
|
|
// https://github.com/xamarin/Xamarin.Forms/issues/4951
|
|
if (borderWidth > 1)
|
|
borderWidth = 1;
|
|
|
|
if (borderWidth < 0f)
|
|
StrokeWidth = _defaultBorderWidth;
|
|
else
|
|
StrokeWidth = (int)Context.ToPixels(borderWidth);
|
|
}
|
|
}
|
|
|
|
void UpdateInputTransparent()
|
|
{
|
|
if (_disposed || Element == null)
|
|
return;
|
|
|
|
_inputTransparent = Element.InputTransparent;
|
|
}
|
|
|
|
void UpdatePrimaryColors()
|
|
{
|
|
if (_disposed || Element == null)
|
|
return;
|
|
|
|
// background
|
|
Color backgroundColor = Element.BackgroundColor;
|
|
AColor background;
|
|
if (backgroundColor.IsDefault)
|
|
background = MaterialColors.Light.PrimaryColor;
|
|
else
|
|
background = backgroundColor.ToAndroid();
|
|
|
|
// text
|
|
Color textColor = Element.TextColor;
|
|
AColor text;
|
|
if (textColor.IsDefault)
|
|
text = MaterialColors.Light.OnPrimaryColor;
|
|
else
|
|
text = textColor.ToAndroid();
|
|
|
|
// apply
|
|
SetTextColor(MaterialColors.CreateButtonTextColors(background, text));
|
|
ViewCompat.SetBackgroundTintList(this, MaterialColors.CreateButtonBackgroundColors(background));
|
|
}
|
|
|
|
IPlatformElementConfiguration<PlatformConfiguration.Android, Button> OnThisPlatform() =>
|
|
_platformElementConfiguration ?? (_platformElementConfiguration = Element.OnThisPlatform());
|
|
|
|
// IOnAttachStateChangeListener
|
|
void IOnAttachStateChangeListener.OnViewAttachedToWindow(AView attachedView) =>
|
|
_buttonLayoutManager?.OnViewAttachedToWindow(attachedView);
|
|
|
|
void IOnAttachStateChangeListener.OnViewDetachedFromWindow(AView detachedView) =>
|
|
_buttonLayoutManager?.OnViewDetachedFromWindow(detachedView);
|
|
|
|
// IOnFocusChangeListener
|
|
void IOnFocusChangeListener.OnFocusChange(AView v, bool hasFocus) =>
|
|
Element.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, hasFocus);
|
|
|
|
// IOnClickListener
|
|
void IOnClickListener.OnClick(AView v) =>
|
|
ButtonElementManager.OnClick(Element, Element, v);
|
|
|
|
// IOnTouchListener
|
|
bool IOnTouchListener.OnTouch(AView v, MotionEvent e) =>
|
|
ButtonElementManager.OnTouch(Element, Element, v, e);
|
|
|
|
// IBorderVisualElementRenderer
|
|
float IBorderVisualElementRenderer.ShadowRadius => ShadowRadius;
|
|
float IBorderVisualElementRenderer.ShadowDx => ShadowDx;
|
|
float IBorderVisualElementRenderer.ShadowDy => ShadowDy;
|
|
AColor IBorderVisualElementRenderer.ShadowColor => ShadowColor;
|
|
bool IBorderVisualElementRenderer.UseDefaultPadding() => OnThisPlatform().UseDefaultPadding();
|
|
bool IBorderVisualElementRenderer.UseDefaultShadow() => OnThisPlatform().UseDefaultShadow();
|
|
bool IBorderVisualElementRenderer.IsShadowEnabled() => true;
|
|
VisualElement IBorderVisualElementRenderer.Element => Element;
|
|
AView IBorderVisualElementRenderer.View => this;
|
|
|
|
// IVisualElementRenderer
|
|
VisualElement IVisualElementRenderer.Element => Element;
|
|
VisualElementTracker IVisualElementRenderer.Tracker => _tracker;
|
|
ViewGroup IVisualElementRenderer.ViewGroup => null;
|
|
AView IVisualElementRenderer.View => this;
|
|
|
|
SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint)
|
|
{
|
|
Measure(widthConstraint, heightConstraint);
|
|
return new SizeRequest(new Size(MeasuredWidth, MeasuredHeight), new Size());
|
|
}
|
|
|
|
void IVisualElementRenderer.SetElement(VisualElement element) =>
|
|
Element = (element as Button) ?? throw new ArgumentException("Element must be of type Button.");
|
|
|
|
void IVisualElementRenderer.SetLabelFor(int? id)
|
|
{
|
|
if (_defaultLabelFor == null)
|
|
_defaultLabelFor = ViewCompat.GetLabelFor(this);
|
|
ViewCompat.SetLabelFor(this, (int)(id ?? _defaultLabelFor));
|
|
}
|
|
|
|
void IVisualElementRenderer.UpdateLayout() =>
|
|
_tracker?.UpdateLayout();
|
|
|
|
// IViewRenderer
|
|
void IViewRenderer.MeasureExactly() =>
|
|
ViewRenderer.MeasureExactly(this, Element, Context);
|
|
|
|
// ITabStop
|
|
AView ITabStop.TabStop => this;
|
|
|
|
// IButtonLayoutRenderer
|
|
AppCompatButton IButtonLayoutRenderer.View => this;
|
|
|
|
Button IButtonLayoutRenderer.Element => this.Element;
|
|
|
|
event EventHandler<VisualElementChangedEventArgs> IButtonLayoutRenderer.ElementChanged
|
|
{
|
|
add => ((IVisualElementRenderer)this).ElementChanged += value;
|
|
remove => ((IVisualElementRenderer)this).ElementChanged -= value;
|
|
}
|
|
}
|
|
}
|
|
#endif
|