如何将 CornerRadius 设置为包含内部元素的框架 (Xamarin.Forms)
How to set a CornerRadius to a frame with elements inside (Xamarin.Forms)
我需要将框架的角设置为圆角,但我的代码不起作用。我认为问题是我有两个 StackLayout 和 BackgroundColor。为什么 IsClippedToBounds="True" 不起作用?
This is how it should be
This is what i get
这是代码:
<Frame CornerRadius="20"
Margin="15,7,15,7"
Padding="0"
IsClippedToBounds="True">
<Grid
IsClippedToBounds="True"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0"
Grid.Column="0"
BackgroundColor="{DynamicResource DarkGray}"
Padding="10">
<Label>...<Label>
</StackLayout>
<StackLayout Grid.Row="0"
Grid.Column="1"
BackgroundColor="{DynamicResource DarkGrayVariant}"
Padding="10">
<Image>...</Image>
</StackLayout>
</Grid>
</Frame>
您可以为帧上的角半径创建自定义渲染器。首先,在您的可移植项目中创建一个继承自框架的自定义控件:
public class ExtendedFrame : Frame
{
public new Thickness Padding { get; set; } = 0;
public int BorderThickness { get; set; }
public ExtendedFrame()
{
base.Padding = this.Padding;
}
}
然后您可以按如下方式实现渲染器:
对于Android:
public class ExtendedFrameRenderer : FrameRenderer
{
GradientDrawable _gi;
public ExtendedFrameRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
var origFrame = e.NewElement as ExtendedFrame;
if(origFrame != null)
{
GradientDrawable gi = new GradientDrawable();
_gi = gi;
gi.SetStroke(origFrame.BorderThickness, origFrame.OutlineColor.ToAndroid());
gi.SetColor(origFrame.BackgroundColor.ToAndroid());
gi.SetCornerRadius(origFrame.CornerRadius);
#pragma warning disable CS0618 // Type or member is obsolete
SetBackgroundDrawable(gi);
#pragma warning restore CS0618 // Type or member is obsolete
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (ChildCount > 0 && _gi != null)
{
#pragma warning disable CS0618 // Type or member is obsolete
SetBackgroundDrawable(_gi);
#pragma warning restore CS0618 // Type or member is obsolete
}
base.OnElementPropertyChanged(sender, e);
}
}
对于IOS:
public class ExtendedFrameRenderer : FrameRenderer
{
private ExtendedFrame customFrame;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
customFrame = e.NewElement as ExtendedFrame;
SetupLayer();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.CornerRadiusProperty.PropertyName)
{
SetupLayer();
}
}
void SetupLayer()
{
float cornerRadius = customFrame.CornerRadius;
if (cornerRadius == -1f)
cornerRadius = 5f; // default corner radius
Layer.CornerRadius = cornerRadius;
Layer.BackgroundColor = customFrame.BackgroundColor.ToCGColor();
if (customFrame.HasShadow)
{
Layer.ShadowRadius = 2;
Layer.ShadowColor = UIColor.Black.CGColor;
Layer.ShadowOpacity = 0.3f;
Layer.ShadowOffset = new SizeF();
}
else
Layer.ShadowOpacity = 0;
//if (customFrame.OutlineColor == Color.Default)
// Layer.BorderColor = UIColor.Clear.CGColor;
//else
//{
Layer.BorderColor = customFrame.OutlineColor.ToCGColor();
Layer.BorderWidth = customFrame.BorderThickness;
// }
Layer.RasterizationScale = UIScreen.MainScreen.Scale;
Layer.ShouldRasterize = true;
}
}
有了 Shaw 的代码,我找到了解决方案!
我删除了框架,并将网格内的两个堆栈布局替换为两个不同的 CustomRenderer,一个用于左角圆角,一个用于右角圆角。
<Grid ColumnSpacing="0"
Padding="15,7,15,7">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<customRenders:LeftCornerRadiusFrame CornerRadius="5"
Grid.Row="0"
Grid.Column="0"
Padding="10"
BackgroundColor="{DynamicResource DarkGray}">
<Label>...</Label>
</customRenders:LeftCornerRadiusFrame>
<customRenders:RightCornerRadiusFrame Grid.Row="0"
Grid.Column="1"
CornerRadius="5"
Padding="10"
BackgroundColor="{DynamicResource DarkGrayVariant}">
<Image>...</Image>
</customRenders:RightCornerRadiusFrame>
</Grid>
在 Android 渲染器中删除构造函数并替换:
gi.SetCornerRadius(origFrame.CornerRadius);
与:
//in LeftCornerRadiusFrameRenderer
gi.SetCornerRadii(new float[] { origFrame.CornerRadius, origFrame.CornerRadius, 0, 0, 0, 0, origFrame.CornerRadius, origFrame.CornerRadius });
//in RightCornerRadiusFrameRenderer
gi.SetCornerRadii(new float[] { 0, 0, origFrame.CornerRadius, origFrame.CornerRadius, origFrame.CornerRadius, origFrame.CornerRadius, 0, 0 });
在IOS渲染器(我还没有测试过)之后:
Layer.CornerRadius = cornerRadius;
放:
//in LeftCornerRadiusFrameRenderer
Layer.MaskedCorners = (CoreAnimation.CACornerMask)5;
//in RightCornerRadiusFrameRenderer
Layer.MaskedCorners = (CoreAnimation.CACornerMask)10;
我需要将框架的角设置为圆角,但我的代码不起作用。我认为问题是我有两个 StackLayout 和 BackgroundColor。为什么 IsClippedToBounds="True" 不起作用?
This is how it should be
This is what i get
这是代码:
<Frame CornerRadius="20"
Margin="15,7,15,7"
Padding="0"
IsClippedToBounds="True">
<Grid
IsClippedToBounds="True"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0"
Grid.Column="0"
BackgroundColor="{DynamicResource DarkGray}"
Padding="10">
<Label>...<Label>
</StackLayout>
<StackLayout Grid.Row="0"
Grid.Column="1"
BackgroundColor="{DynamicResource DarkGrayVariant}"
Padding="10">
<Image>...</Image>
</StackLayout>
</Grid>
</Frame>
您可以为帧上的角半径创建自定义渲染器。首先,在您的可移植项目中创建一个继承自框架的自定义控件:
public class ExtendedFrame : Frame
{
public new Thickness Padding { get; set; } = 0;
public int BorderThickness { get; set; }
public ExtendedFrame()
{
base.Padding = this.Padding;
}
}
然后您可以按如下方式实现渲染器:
对于Android:
public class ExtendedFrameRenderer : FrameRenderer
{
GradientDrawable _gi;
public ExtendedFrameRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
var origFrame = e.NewElement as ExtendedFrame;
if(origFrame != null)
{
GradientDrawable gi = new GradientDrawable();
_gi = gi;
gi.SetStroke(origFrame.BorderThickness, origFrame.OutlineColor.ToAndroid());
gi.SetColor(origFrame.BackgroundColor.ToAndroid());
gi.SetCornerRadius(origFrame.CornerRadius);
#pragma warning disable CS0618 // Type or member is obsolete
SetBackgroundDrawable(gi);
#pragma warning restore CS0618 // Type or member is obsolete
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (ChildCount > 0 && _gi != null)
{
#pragma warning disable CS0618 // Type or member is obsolete
SetBackgroundDrawable(_gi);
#pragma warning restore CS0618 // Type or member is obsolete
}
base.OnElementPropertyChanged(sender, e);
}
}
对于IOS:
public class ExtendedFrameRenderer : FrameRenderer
{
private ExtendedFrame customFrame;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
customFrame = e.NewElement as ExtendedFrame;
SetupLayer();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.OutlineColorProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.HasShadowProperty.PropertyName ||
e.PropertyName == Xamarin.Forms.Frame.CornerRadiusProperty.PropertyName)
{
SetupLayer();
}
}
void SetupLayer()
{
float cornerRadius = customFrame.CornerRadius;
if (cornerRadius == -1f)
cornerRadius = 5f; // default corner radius
Layer.CornerRadius = cornerRadius;
Layer.BackgroundColor = customFrame.BackgroundColor.ToCGColor();
if (customFrame.HasShadow)
{
Layer.ShadowRadius = 2;
Layer.ShadowColor = UIColor.Black.CGColor;
Layer.ShadowOpacity = 0.3f;
Layer.ShadowOffset = new SizeF();
}
else
Layer.ShadowOpacity = 0;
//if (customFrame.OutlineColor == Color.Default)
// Layer.BorderColor = UIColor.Clear.CGColor;
//else
//{
Layer.BorderColor = customFrame.OutlineColor.ToCGColor();
Layer.BorderWidth = customFrame.BorderThickness;
// }
Layer.RasterizationScale = UIScreen.MainScreen.Scale;
Layer.ShouldRasterize = true;
}
}
有了 Shaw 的代码,我找到了解决方案!
我删除了框架,并将网格内的两个堆栈布局替换为两个不同的 CustomRenderer,一个用于左角圆角,一个用于右角圆角。
<Grid ColumnSpacing="0"
Padding="15,7,15,7">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<customRenders:LeftCornerRadiusFrame CornerRadius="5"
Grid.Row="0"
Grid.Column="0"
Padding="10"
BackgroundColor="{DynamicResource DarkGray}">
<Label>...</Label>
</customRenders:LeftCornerRadiusFrame>
<customRenders:RightCornerRadiusFrame Grid.Row="0"
Grid.Column="1"
CornerRadius="5"
Padding="10"
BackgroundColor="{DynamicResource DarkGrayVariant}">
<Image>...</Image>
</customRenders:RightCornerRadiusFrame>
</Grid>
在 Android 渲染器中删除构造函数并替换:
gi.SetCornerRadius(origFrame.CornerRadius);
与:
//in LeftCornerRadiusFrameRenderer
gi.SetCornerRadii(new float[] { origFrame.CornerRadius, origFrame.CornerRadius, 0, 0, 0, 0, origFrame.CornerRadius, origFrame.CornerRadius });
//in RightCornerRadiusFrameRenderer
gi.SetCornerRadii(new float[] { 0, 0, origFrame.CornerRadius, origFrame.CornerRadius, origFrame.CornerRadius, origFrame.CornerRadius, 0, 0 });
在IOS渲染器(我还没有测试过)之后:
Layer.CornerRadius = cornerRadius;
放:
//in LeftCornerRadiusFrameRenderer
Layer.MaskedCorners = (CoreAnimation.CACornerMask)5;
//in RightCornerRadiusFrameRenderer
Layer.MaskedCorners = (CoreAnimation.CACornerMask)10;