using Android.Content; using Android.Graphics.Drawables; using Android.Views; using Android.Widget; using Gallery.Droid.Renderers; using Gallery.UI; using Xamarin.Forms.Platform.Android; [assembly: Xamarin.Forms.ExportRenderer(typeof(SegmentedControl), typeof(SegmentedControlRenderer))] namespace Gallery.Droid.Renderers { public class SegmentedControlRenderer : ViewRenderer { RadioGroup nativeControl; RadioButton _rb; readonly Context context; public SegmentedControlRenderer(Context context) : base(context) { this.context = context; } protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); if (Control == null) { // Instantiate the native control and assign it to the Control property with // the SetNativeControl method var layoutInflater = LayoutInflater.From(context); var view = layoutInflater.Inflate(Resource.Layout.RadioGroup, null); nativeControl = (RadioGroup)layoutInflater.Inflate(Resource.Layout.RadioGroup, null); var density = context.Resources.DisplayMetrics.Density; for (var i = 0; i < Element.Children.Count; i++) { var o = Element.Children[i]; var rb = (RadioButton)layoutInflater.Inflate(Resource.Layout.RadioButton, null); var width = rb.Paint.MeasureText(o.Text) * density + 0.5f; rb.LayoutParameters = new RadioGroup.LayoutParams((int)width, LayoutParams.WrapContent, 1f); rb.Text = o.Text; if (i == 0) rb.SetBackgroundResource(Resource.Drawable.segmented_control_first_background); else if (i == Element.Children.Count - 1) rb.SetBackgroundResource(Resource.Drawable.segmented_control_last_background); else rb.SetBackgroundResource(Resource.Drawable.segmented_control_background); ConfigureRadioButton(i, rb); nativeControl.AddView(rb); } var option = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex); if (option != null) option.Checked = true; SetNativeControl(nativeControl); } if (e.OldElement != null) { // Unsubscribe from event handlers and cleanup any resources if (nativeControl != null) nativeControl.CheckedChange -= NativeControl_ValueChanged; } if (e.NewElement != null) { // Configure the control and subscribe to event handlers nativeControl.CheckedChange += NativeControl_ValueChanged; } } protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (nativeControl == null || Element == null) return; switch (e.PropertyName) { case "Renderer": Element?.SendValueChanged(); break; case nameof(SegmentedControl.SelectedSegmentIndex): var option = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex); if (option != null) option.Checked = true; if (Element.SelectedSegmentIndex < 0) { var layoutInflater = LayoutInflater.From(context); nativeControl = (RadioGroup)layoutInflater.Inflate(Resource.Layout.RadioGroup, null); for (var i = 0; i < Element.Children.Count; i++) { var o = Element.Children[i]; var rb = (RadioButton)layoutInflater.Inflate(Resource.Layout.RadioButton, null); var width = rb.Paint.MeasureText(o.Text); rb.LayoutParameters = new RadioGroup.LayoutParams((int)width, LayoutParams.WrapContent, 1f); rb.Text = o.Text; if (i == 0) rb.SetBackgroundResource(Resource.Drawable.segmented_control_first_background); else if (i == Element.Children.Count - 1) rb.SetBackgroundResource(Resource.Drawable.segmented_control_last_background); else rb.SetBackgroundResource(Resource.Drawable.segmented_control_background); ConfigureRadioButton(i, rb); nativeControl.AddView(rb); } nativeControl.CheckedChange += NativeControl_ValueChanged; SetNativeControl(nativeControl); } Element.SendValueChanged(); break; case nameof(SegmentedControl.TintColor): OnPropertyChanged(); break; case nameof(SegmentedControl.IsEnabled): OnPropertyChanged(); break; case nameof(SegmentedControl.SelectedTextColor): var v = (RadioButton)nativeControl.GetChildAt(Element.SelectedSegmentIndex); v.SetTextColor(Element.SelectedTextColor.ToAndroid()); break; } } void OnPropertyChanged() { if (nativeControl != null && Element != null) { for (var i = 0; i < Element.Children.Count; i++) { var rb = (RadioButton)nativeControl.GetChildAt(i); ConfigureRadioButton(i, rb); } } } void ConfigureRadioButton(int i, RadioButton rb) { var textColor = Element.SelectedTextColor; if (i == Element.SelectedSegmentIndex) { rb.SetTextColor(textColor.ToAndroid()); rb.Paint.FakeBoldText = true; _rb = rb; } else { var tColor = Element.IsEnabled ? textColor.IsDefault ? Element.TintColor.ToAndroid() : textColor.MultiplyAlpha(.6).ToAndroid() : Element.DisabledColor.ToAndroid(); rb.SetTextColor(tColor); } GradientDrawable selectedShape; GradientDrawable unselectedShape; var gradientDrawable = (StateListDrawable)rb.Background; var drawableContainerState = (DrawableContainer.DrawableContainerState)gradientDrawable.GetConstantState(); var children = drawableContainerState.GetChildren(); // Doesnt works on API < 18 selectedShape = children[0] is GradientDrawable ? (GradientDrawable)children[0] : (GradientDrawable)((InsetDrawable)children[0]).Drawable; unselectedShape = children[1] is GradientDrawable ? (GradientDrawable)children[1] : (GradientDrawable)((InsetDrawable)children[1]).Drawable; var color = Element.IsEnabled ? Element.TintColor.ToAndroid() : Element.DisabledColor.ToAndroid(); selectedShape.SetStroke(3, color); selectedShape.SetColor(color); unselectedShape.SetStroke(3, color); rb.Enabled = Element.IsEnabled; } void NativeControl_ValueChanged(object sender, RadioGroup.CheckedChangeEventArgs e) { var rg = (RadioGroup)sender; if (rg.CheckedRadioButtonId != -1) { var id = rg.CheckedRadioButtonId; var radioButton = rg.FindViewById(id); var radioId = rg.IndexOfChild(radioButton); var rb = (RadioButton)rg.GetChildAt(radioId); var textColor = Element.SelectedTextColor; var color = Element.IsEnabled ? textColor.IsDefault ? Element.TintColor.ToAndroid() : textColor.MultiplyAlpha(.6).ToAndroid() : Element.DisabledColor.ToAndroid(); if (_rb != null) { _rb.SetTextColor(color); _rb.Paint.FakeBoldText = false; } rb.SetTextColor(Element.SelectedTextColor.ToAndroid()); rb.Paint.FakeBoldText = true; _rb = rb; Element.SelectedSegmentIndex = radioId; } } protected override void Dispose(bool disposing) { if (nativeControl != null) { nativeControl.CheckedChange -= NativeControl_ValueChanged; nativeControl.Dispose(); nativeControl = null; _rb = null; } try { base.Dispose(disposing); } catch { return; } } } }