Xamarin Forms实现双色圆形控件:求助设置左右颜色及文本
Got it, let's solve this problem! The built-in controls like ProgressBar or basic shapes don't give you the exact dual-color circle with center text you're after, so we'll build a custom control that's flexible and easy to configure.
Approach 1: Custom ContentView with BoxViews (Simple & No External Libraries)
This method uses native Xamarin.Forms elements to create the dual-color circle, no extra packages needed.
Step 1: Create the Custom Control Class
First, define a custom ContentView with bindable properties for your required settings:
using Xamarin.Forms; namespace YourAppNamespace { public class DualColorCircle : ContentView { // Bindable properties for colors and text public static readonly BindableProperty LeftColorProperty = BindableProperty.Create(nameof(LeftColor), typeof(Color), typeof(DualColorCircle), Color.Blue); public static readonly BindableProperty RightColorProperty = BindableProperty.Create(nameof(RightColor), typeof(Color), typeof(DualColorCircle), Color.Gray); public static readonly BindableProperty CenterTextProperty = BindableProperty.Create(nameof(CenterText), typeof(string), typeof(DualColorCircle), "0%"); public Color LeftColor { get => (Color)GetValue(LeftColorProperty); set => SetValue(LeftColorProperty, value); } public Color RightColor { get => (Color)GetValue(RightColorProperty); set => SetValue(RightColorProperty, value); } public string CenterText { get => (string)GetValue(CenterTextProperty); set => SetValue(CenterTextProperty, value); } public DualColorCircle() { BuildView(); } private void BuildView() { // Use AbsoluteLayout to overlay the two half-circles and center label var layout = new AbsoluteLayout(); // Left half-circle var leftHalf = new BoxView { Color = LeftColor, CornerRadius = new CornerRadius(100, 0, 0, 100) // Top-left, Top-right, Bottom-right, Bottom-left }; // Right half-circle var rightHalf = new BoxView { Color = RightColor, CornerRadius = new CornerRadius(0, 100, 100, 0) }; // Center text label var centerLabel = new Label { Text = CenterText, FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)), FontAttributes = FontAttributes.Bold, HorizontalTextAlignment = TextAlignment.Center, VerticalTextAlignment = TextAlignment.Center }; // Layout all elements AbsoluteLayout.SetLayoutBounds(leftHalf, new Rectangle(0, 0, 0.5, 1)); AbsoluteLayout.SetLayoutFlags(leftHalf, AbsoluteLayoutFlags.All); AbsoluteLayout.SetLayoutBounds(rightHalf, new Rectangle(0.5, 0, 0.5, 1)); AbsoluteLayout.SetLayoutFlags(rightHalf, AbsoluteLayoutFlags.All); AbsoluteLayout.SetLayoutBounds(centerLabel, new Rectangle(0.5, 0.5, 1, 1)); AbsoluteLayout.SetLayoutFlags(centerLabel, AbsoluteLayoutFlags.PositionProportional); layout.Children.Add(leftHalf); layout.Children.Add(rightHalf); layout.Children.Add(centerLabel); Content = layout; // Update label when properties change this.PropertyChanged += (s, e) => { if (e.PropertyName == nameof(CenterText)) { centerLabel.Text = CenterText; } else if (e.PropertyName == nameof(LeftColor)) { leftHalf.Color = LeftColor; } else if (e.PropertyName == nameof(RightColor)) { rightHalf.Color = RightColor; } }; } } }
Step 2: Use the Control in Your Page
Add the control to your XAML page (don't forget the namespace declaration):
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:YourAppNamespace" x:Class="YourAppNamespace.MainPage"> <StackLayout Padding="20" VerticalOptions="Center" HorizontalOptions="Center"> <!-- Custom dual-color circle --> <local:DualColorCircle WidthRequest="200" HeightRequest="200" LeftColor="#2196F3" RightColor="#E0E0E0" CenterText="50%" /> </StackLayout> </ContentPage>
Approach 2: SkiaSharp (Smoother, More Flexible)
If you want smoother edges or plan to add animations later, SkiaSharp is a great choice. First, install the SkiaSharp.Views.Forms NuGet package.
Step 1: Create the SkiaSharp Control
using SkiaSharp; using SkiaSharp.Views.Forms; using Xamarin.Forms; namespace YourAppNamespace { public class DualColorCircleSK : ContentView { public static readonly BindableProperty LeftColorProperty = BindableProperty.Create(nameof(LeftColor), typeof(Color), typeof(DualColorCircleSK), Color.Blue); public static readonly BindableProperty RightColorProperty = BindableProperty.Create(nameof(RightColor), typeof(Color), typeof(DualColorCircleSK), Color.Gray); public static readonly BindableProperty CenterTextProperty = BindableProperty.Create(nameof(CenterText), typeof(string), typeof(DualColorCircleSK), "0%"); public Color LeftColor { get => (Color)GetValue(LeftColorProperty); set => SetValue(LeftColorProperty, value); } public Color RightColor { get => (Color)GetValue(RightColorProperty); set => SetValue(RightColorProperty, value); } public string CenterText { get => (string)GetValue(CenterTextProperty); set => SetValue(CenterTextProperty, value); } private SKCanvasView canvasView; public DualColorCircleSK() { canvasView = new SKCanvasView(); canvasView.PaintSurface += OnCanvasViewPaintSurface; Content = canvasView; // Redraw when properties change this.PropertyChanged += (s, e) => { if (e.PropertyName is nameof(LeftColor) or nameof(RightColor) or nameof(CenterText)) { canvasView.InvalidateSurface(); } }; } private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs e) { SKImageInfo info = e.Info; SKSurface surface = e.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); // Calculate circle bounds float radius = Math.Min(info.Width, info.Height) / 2f; SKPoint center = new SKPoint(info.Width / 2f, info.Height / 2f); SKRect circleRect = new SKRect(center.X - radius, center.Y - radius, center.X + radius, center.Y + radius); // Draw left half-circle using (SKPaint leftPaint = new SKPaint()) { leftPaint.Color = LeftColor.ToSKColor(); leftPaint.IsAntialias = true; canvas.DrawArc(circleRect, 90, 180, true, leftPaint); } // Draw right half-circle using (SKPaint rightPaint = new SKPaint()) { rightPaint.Color = RightColor.ToSKColor(); rightPaint.IsAntialias = true; canvas.DrawArc(circleRect, 270, 180, true, rightPaint); } // Draw center text using (SKPaint textPaint = new SKPaint()) { textPaint.Color = Colors.Black.ToSKColor(); textPaint.TextSize = radius * 0.4f; textPaint.IsAntialias = true; textPaint.TextAlign = SKTextAlign.Center; textPaint.Typeface = SKTypeface.FromFamilyName(null, SKFontStyle.Bold); // Calculate text position to center it vertically SKRect textBounds = new SKRect(); textPaint.MeasureText(CenterText, ref textBounds); float textY = center.Y - textBounds.MidY; canvas.DrawText(CenterText, center.X, textY, textPaint); } } } }
Step 2: Use the SkiaSharp Control
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:YourAppNamespace" xmlns:skia="clr-namespace:SkiaSharp.Views.Forms;assembly=SkiaSharp.Views.Forms" x:Class="YourAppNamespace.MainPage"> <StackLayout Padding="20" VerticalOptions="Center" HorizontalOptions="Center"> <local:DualColorCircleSK WidthRequest="200" HeightRequest="200" LeftColor="#4CAF50" RightColor="#FFC107" CenterText="75%" /> </StackLayout> </ContentPage>
Both approaches let you easily set the left/right colors and center text, and the SkiaSharp version gives you more control over rendering quality and potential animations. Pick whichever fits your project's needs!
内容的提问来源于stack exchange,提问作者Bartvandee

