1using System.Globalization;
8 readonly GraphicsView _gfx;
13 public static readonly BindableProperty IsCheckedProperty =
14 BindableProperty.Create(nameof(IsChecked), typeof(
bool), typeof(
AnimatedCheckbox),
false,
19 get => (bool)GetValue(IsCheckedProperty);
20 set => SetValue(IsCheckedProperty, value);
23 public static readonly BindableProperty StrokeColorProperty =
24 BindableProperty.Create(
29 propertyChanged: (b, o, n) => ((
AnimatedCheckbox)b).OnStrokeColorChanged((Color)n));
31 public static readonly BindableProperty FillColorProperty =
32 BindableProperty.Create(
37 propertyChanged: (b, o, n) => ((
AnimatedCheckbox)b).OnFillColorChanged((Color)n));
39 public static readonly BindableProperty StrokeWidthProperty =
40 BindableProperty.Create(
45 propertyChanged: (b, o, n) => ((
AnimatedCheckbox)b).OnStrokeWidthChanged((
float)n));
47 public Color StrokeColor
49 get => (Color)GetValue(StrokeColorProperty);
50 set => SetValue(StrokeColorProperty, value);
53 public Color FillColor
55 get => (Color)GetValue(FillColorProperty);
56 set => SetValue(FillColorProperty, value);
59 public float StrokeWidth
61 get => (float)GetValue(StrokeWidthProperty);
62 set => SetValue(StrokeWidthProperty, value);
68 _gfx =
new GraphicsView { Drawable = _drawable, HeightRequest = 28, WidthRequest = 28 };
69 Content =
new Grid { Children = { _gfx } };
71 var tap =
new TapGestureRecognizer();
72 tap.Tapped += (
_, __) => IsChecked = !IsChecked;
74 GestureRecognizers.Add(tap);
75 _gfx.GestureRecognizers.Add(
new TapGestureRecognizer { Command =
new Command(() => IsChecked = !IsChecked) });
78 this.SetBinding(SemanticProperties.DescriptionProperty,
new Binding(nameof(this.IsChecked), source:
this, converter:
new BoolToStr(
"Checked",
"Unchecked")));
81 void OnStrokeColorChanged(Color color)
83 if (_drawable is not
null)
85 _drawable.StrokeColor = color;
90 void OnFillColorChanged(Color color)
92 if (_drawable is not
null)
94 _drawable.FillColor = color;
99 void OnStrokeWidthChanged(
float width)
101 if (_drawable is not
null)
103 _drawable.StrokeWidth = width;
108 void AnimateTo(
bool targetChecked)
110 var from = _drawable.Progress;
111 var to = targetChecked ? 1.0 : 0.0;
113 var anim =
new Animation(p =>
115 _drawable.Progress = p;
117 }, from, to, Easing.CubicOut);
120 var pop =
new Animation(s => { _scale = s; this.Scale = _scale; }, 1, 1.06, Easing.CubicOut);
121 var popBack =
new Animation(s => { _scale = s; this.Scale = _scale; }, 1.06, 1, Easing.CubicIn);
123 var parent =
new Animation();
124 parent.Add(0, 1, anim);
125 parent.Add(0, 0.5, pop);
126 parent.Add(0.5, 1, popBack);
128 parent.Commit(
this,
"chk", length: 180);
131 class BoolToStr : IValueConverter
133 string t, f;
public BoolToStr(
string t,
string f) { this.t=t; this.f=f; }
134 public object Convert(
object? value, Type ttype,
object? p, CultureInfo c) => (bool)value ? t : f;
135 public object ConvertBack(
object? v, Type ttype,
object? p, CultureInfo c) =>
throw new NotImplementedException();
142 public double Progress {
get;
set; } = 0;
143 public Color StrokeColor {
get;
set; } = Colors.Black;
144 public Color FillColor {
get;
set; } = Colors.Transparent;
145 public float StrokeWidth {
get;
set; } = 2f;
146 const float Corner = 6f;
148 public void Draw(ICanvas canvas, RectF rect)
153 var box = rect.Inflate(-2, -2);
154 canvas.FillColor = FillColor;
155 canvas.StrokeColor = StrokeColor;
156 canvas.StrokeSize = StrokeWidth;
157 canvas.FillRoundedRectangle(box, Corner);
158 canvas.DrawRoundedRectangle(box, Corner);
162 var p1 =
new PointF(box.X + box.Width * 0.26f, box.Y + box.Height * 0.54f);
163 var p2 =
new PointF(box.X + box.Width * 0.44f, box.Y + box.Height * 0.72f);
164 var p3 =
new PointF(box.X + box.Width * 0.76f, box.Y + box.Height * 0.34f);
167 var total = Distance(p1, p2) + Distance(p2, p3);
168 var drawLen = (float)(total * Progress);
170 canvas.StrokeLineCap = LineCap.Round;
171 canvas.StrokeLineJoin = LineJoin.Round;
174 var d1 = Distance(p1, p2);
179 var t = drawLen / d1;
180 var mid = Lerp(p1, p2, t);
181 canvas.DrawLine(p1, mid);
185 canvas.DrawLine(p1, p2);
187 var rem = drawLen - d1;
188 var t = Math.Clamp(rem / Distance(p2, p3), 0, 1);
189 var mid = Lerp(p2, p3, t);
190 canvas.DrawLine(p2, mid);
194 canvas.RestoreState();
197 static float Distance(PointF a, PointF b) => (float)Math.Sqrt((a.X-b.X)*(a.X-b.X)+(a.Y-b.Y)*(a.Y-b.Y));
198 static PointF Lerp(PointF a, PointF b,
float t) =>
new(a.X + (b.X-a.X)*t, a.Y + (b.Y-a.Y)*t);