Xamarin.Forms 可重用 RoundEffect
重要
不再需要使用 RoundEffect
将控件呈现为圆形。 最新推荐的方法是使用 EllipseGeometry
来剪辑控件。 有关详细信息,请参阅使用几何图形进行剪切。
RoundEffect 简化了将派生自 VisualElement
的任何控件呈现为圆形的过程。 此效果可用于创建圆形图像、按钮和其他控件:
创建共享的 RoutingEffect
必须在共享项目中创建一个效果类才能创建跨平台效果。 该示例应用程序会创建一个空的 RoundEffect
类,该类派生自 RoutingEffect
类:
public class RoundEffect : RoutingEffect
{
public RoundEffect() : base($"Xamarin.{nameof(RoundEffect)}")
{
}
}
该类允许共享项目解析对代码或 XAML 中效果的引用,但不提供任何功能。 必须对每个平台实现这种效果。
实现 Android 效果
Android 平台项目定义了一个派生自 PlatformEffect
的 RoundEffect
类。 该类标记有允许 Xamarin.Forms 解析效果类的 assembly
属性:
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.Droid.RoundEffect), nameof(RoundEffectDemo.Droid.RoundEffect))]
namespace RoundEffectDemo.Droid
{
public class RoundEffect : PlatformEffect
{
// ...
}
}
Android 平台使用 OutlineProvider
的概念来定义控件的边缘。 该示例项目包括一个派生自 ViewOutlineProvider
类的 CornerRadiusProvider
类:
class CornerRadiusOutlineProvider : ViewOutlineProvider
{
Element element;
public CornerRadiusOutlineProvider(Element formsElement)
{
element = formsElement;
}
public override void GetOutline(Android.Views.View view, Outline outline)
{
float scale = view.Resources.DisplayMetrics.Density;
double width = (double)element.GetValue(VisualElement.WidthProperty) * scale;
double height = (double)element.GetValue(VisualElement.HeightProperty) * scale;
float minDimension = (float)Math.Min(height, width);
float radius = minDimension / 2f;
Rect rect = new Rect(0, 0, (int)width, (int)height);
outline.SetRoundRect(rect, radius);
}
}
此类使用 Xamarin.FormsElement
实例的 Width
和 Height
属性来计算半径,该半径是最短尺寸的一半。
定义边框提供程序后,RoundEffect
类就可以使用它来实现效果:
public class RoundEffect : PlatformEffect
{
ViewOutlineProvider originalProvider;
Android.Views.View effectTarget;
protected override void OnAttached()
{
try
{
effectTarget = Control ?? Container;
originalProvider = effectTarget.OutlineProvider;
effectTarget.OutlineProvider = new CornerRadiusOutlineProvider(Element);
effectTarget.ClipToOutline = true;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to set corner radius: {ex.Message}");
}
}
protected override void OnDetached()
{
if(effectTarget != null)
{
effectTarget.OutlineProvider = originalProvider;
effectTarget.ClipToOutline = false;
}
}
}
当效果附加到元素时,将调用 OnAttached
方法。 保存现有的 OutlineProvider
对象,以便在分离效果时可以将其还原。 将 CornerRadiusOutlineProvider
的新实例用作 OutlineProvider
,并将 ClipToOutline
设置为 true,以将溢出的元素剪裁到外部边框。
当从元素中删除效果并还原原始的 OnDetatched
值时,将调用 OutlineProvider
方法。
注意
Control
属性可以为 null,也可以不为 null,具体取决于元素类型。 如果 Control
属性不为 null,则可以直接将圆角应用于控件。 但是,如果该属性为 null,则必须将圆角应用于 Container
对象。 effectTarget
字段允许将该效果应用于相应的对象。
实现 iOS 效果
iOS 平台项目定义了一个派生自 PlatformEffect
的 RoundEffect
类。 该类标记有允许 Xamarin.Forms 解析效果类的 assembly
属性:
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.iOS.RoundEffect), nameof(RoundEffectDemo.iOS.RoundEffect))]
namespace RoundEffectDemo.iOS
{
public class RoundEffect : PlatformEffect
{
// ...
}
在 iOS 上,控件具有 Layer
属性,而该属性又具有 CornerRadius
属性。 iOS 上的 RoundEffect
类实现计算相应的角半径并更新层的 CornerRadius
属性:
public class RoundEffect : PlatformEffect
{
nfloat originalRadius;
UIKit.UIView effectTarget;
protected override void OnAttached()
{
try
{
effectTarget = Control ?? Container;
originalRadius = effectTarget.Layer.CornerRadius;
effectTarget.ClipsToBounds = true;
effectTarget.Layer.CornerRadius = CalculateRadius();
}
catch (Exception ex)
{
Console.WriteLine($"Failed to set corner radius: {ex.Message}");
}
}
protected override void OnDetached()
{
if (effectTarget != null)
{
effectTarget.ClipsToBounds = false;
if (effectTarget.Layer != null)
{
effectTarget.Layer.CornerRadius = originalRadius;
}
}
}
float CalculateRadius()
{
double width = (double)Element.GetValue(VisualElement.WidthRequestProperty);
double height = (double)Element.GetValue(VisualElement.HeightRequestProperty);
float minDimension = (float)Math.Min(height, width);
float radius = minDimension / 2f;
return radius;
}
}
CalculateRadius
方法根据 Xamarin.FormsElement
的最小尺寸来计算半径。 将效果附加到控件时,将调用 OnAttached
方法,并更新层的 CornerRadius
属性。 它将 ClipToBounds
属性设置为 true
,以便将溢出的元素剪裁到控件的边框。 当从控件中删除效果并反转这些更改以还原原始角半径时,将调用 OnDetatched
方法。
注意
Control
属性可以为 null,也可以不为 null,具体取决于元素类型。 如果 Control
属性不为 null,则可以直接将圆角应用于控件。 但是,如果该属性为 null,则必须将圆角应用于 Container
对象。 effectTarget
字段允许将该效果应用于相应的对象。
使用效果
跨平台实现效果后,Xamarin.Forms 控件就可以使用这种效果。 RoundEffect
的常见应用是使 Image
对象成为圆形。 以下 XAML 显示了应用于 Image
实例的效果:
<Image Source=outdoors"
HeightRequest="100"
WidthRequest="100">
<Image.Effects>
<local:RoundEffect />
</Image.Effects>
</Image>
该效果也可以应用于代码:
var image = new Image
{
Source = ImageSource.FromFile("outdoors"),
HeightRequest = 100,
WidthRequest = 100
};
image.Effects.Add(new RoundEffect());
RoundEffect
类可以应用于派生自 VisualElement
的任何控件。
注意
对于计算正确半径的效果,所应用的控件必须能够显式调整大小。 因此,应定义 HeightRequest
和 WidthRequest
属性。 如果受影响的控件出现在 StackLayout
中,则其 HorizontalOptions
属性不应使用 LayoutOptions.CenterAndExpand
等 Expand 值,否则它将没有准确的尺寸。