Xamarin.Forms: 在两个视图之间共享渐变
Xamarin.Forms: Share a gradient between two Views
我在 Xamarin.Forms 中创建了一个允许渐变背景的自定义框架。我正在尝试从两个具有渐变背景的不同框架创建复合形状,但我希望渐变在两者之间共享。我已经通过使用 Skia.Sharp.Forms 获得了预期的效果,但我想知道是否有一种方法可以仅使用 Xamarin.Forms 和自定义渲染器来做到这一点。
我正在寻找的示例:
使用自定义 2 个自定义框架时得到的示例:(请注意形状略有不同)
编辑
我的想法是将两个框架(或与此相关的任何控件)封装在给定渐变颜色的自定义网格中。然后在 Grid 的自定义渲染器中,它将 children 控件的背景设置为渐变。这样,LinearGradient
具有 parent 网格的起点 (0,0),并且不会为每个 child 创建新的渐变。这是一些代码来解释我的意思,我只是还没有弄清楚我将 children 的背景设置为渐变的部分,SetLayerPaint
( 方法似乎不起作用..)
protected override void DispatchDraw(Canvas canvas)
{
_gradient = new Android.Graphics.LinearGradient(
0, 0, Width, Height,
new int[] { _startColor.ToAndroid(), _middleColor.ToAndroid(), _endColor.ToAndroid() },
null,
Android.Graphics.Shader.TileMode.Mirror);
for(var i = 0; i < ChildCount; i++ )
{
var paint = new Android.Graphics.Paint()
{
Dither = true
};
paint.SetShader(_gradient);
var child = GetChildAt(i);
child.SetLayerPaint(paint);
}
base.DispatchDraw(canvas);
}
有人知道这是否可行吗?
这是我的解决方案:
网格的自定义渲染器
public class GradientGridRenderer_Android : ViewRenderer
{
private Xamarin.Forms.Color _startColor;
private Xamarin.Forms.Color _middleColor;
private Xamarin.Forms.Color _endColor;
LinearGradient _gradient;
public GradientGridRenderer_Android(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if(e.NewElement != null && e.NewElement is GradientGrid grid)
{
_startColor = grid.StartColor;
_middleColor = grid.MiddleColor;
_endColor = grid.EndColor;
}
}
protected override void DispatchDraw(Canvas canvas)
{
base.DispatchDraw(canvas);
_gradient = new LinearGradient(
0, 0, Width, Height,
new int[]
{
_startColor.ToAndroid(),
_middleColor.ToAndroid(),
_endColor.ToAndroid(),
},
null,
Shader.TileMode.Mirror);
for (var i = 0; i < ChildCount; i++)
{
var child = GetChildAt(i);
if(child is FrameRenderer_Android gFrame)
{
gFrame.Gradient = _gradient;
gFrame.Invalidate();
}
}
}
}
这是 Child 的自定义渲染器,如果需要,您可以将其抽象出来并制作任意数量的采用渐变的自定义渲染器,但出于我的目的,我只需要一个框架。
public class FrameRenderer_Android : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
public LinearGradient Gradient;
public FrameRenderer_Android(Context context)
: base(context) { }
protected override void DispatchDraw(Canvas canvas)
{
if(Control != null && Gradient != null)
{
var paint = new Android.Graphics.Paint()
{
Dither = true,
};
paint.SetShader(Gradient);
canvas.DrawPaint(paint);
}
base.DispatchDraw(canvas);
}
}
这是 xaml
<ContentPage.Content>
<cntrl:GradientGrid RowSpacing="0"
Margin="0,20,0,0"
StartColor="{StaticResource GracePink}"
MiddleColor="{StaticResource GracePurple}"
EndColor="{StaticResource GraceDarkPurple}"
IsClippedToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<cntrl:CustomFrame Grid.Column="2"
Grid.Row="0"
IsClippedToBounds="True"
CornerRadius="20,20,0,0">
</cntrl:CustomFrame>
<cntrl:CustomFrame Grid.ColumnSpan="4"
Grid.Row="1"
IsClippedToBounds="True"
CornerRadius="40,40,0,0">
</cntrl:CustomFrame>
</cntrl:GradientGrid>
</ContentPage.Content>
我在 Xamarin.Forms 中创建了一个允许渐变背景的自定义框架。我正在尝试从两个具有渐变背景的不同框架创建复合形状,但我希望渐变在两者之间共享。我已经通过使用 Skia.Sharp.Forms 获得了预期的效果,但我想知道是否有一种方法可以仅使用 Xamarin.Forms 和自定义渲染器来做到这一点。
我正在寻找的示例:
使用自定义 2 个自定义框架时得到的示例:(请注意形状略有不同)
编辑
我的想法是将两个框架(或与此相关的任何控件)封装在给定渐变颜色的自定义网格中。然后在 Grid 的自定义渲染器中,它将 children 控件的背景设置为渐变。这样,LinearGradient
具有 parent 网格的起点 (0,0),并且不会为每个 child 创建新的渐变。这是一些代码来解释我的意思,我只是还没有弄清楚我将 children 的背景设置为渐变的部分,SetLayerPaint
( 方法似乎不起作用..)
protected override void DispatchDraw(Canvas canvas)
{
_gradient = new Android.Graphics.LinearGradient(
0, 0, Width, Height,
new int[] { _startColor.ToAndroid(), _middleColor.ToAndroid(), _endColor.ToAndroid() },
null,
Android.Graphics.Shader.TileMode.Mirror);
for(var i = 0; i < ChildCount; i++ )
{
var paint = new Android.Graphics.Paint()
{
Dither = true
};
paint.SetShader(_gradient);
var child = GetChildAt(i);
child.SetLayerPaint(paint);
}
base.DispatchDraw(canvas);
}
有人知道这是否可行吗?
这是我的解决方案: 网格的自定义渲染器
public class GradientGridRenderer_Android : ViewRenderer
{
private Xamarin.Forms.Color _startColor;
private Xamarin.Forms.Color _middleColor;
private Xamarin.Forms.Color _endColor;
LinearGradient _gradient;
public GradientGridRenderer_Android(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if(e.NewElement != null && e.NewElement is GradientGrid grid)
{
_startColor = grid.StartColor;
_middleColor = grid.MiddleColor;
_endColor = grid.EndColor;
}
}
protected override void DispatchDraw(Canvas canvas)
{
base.DispatchDraw(canvas);
_gradient = new LinearGradient(
0, 0, Width, Height,
new int[]
{
_startColor.ToAndroid(),
_middleColor.ToAndroid(),
_endColor.ToAndroid(),
},
null,
Shader.TileMode.Mirror);
for (var i = 0; i < ChildCount; i++)
{
var child = GetChildAt(i);
if(child is FrameRenderer_Android gFrame)
{
gFrame.Gradient = _gradient;
gFrame.Invalidate();
}
}
}
}
这是 Child 的自定义渲染器,如果需要,您可以将其抽象出来并制作任意数量的采用渐变的自定义渲染器,但出于我的目的,我只需要一个框架。
public class FrameRenderer_Android : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
public LinearGradient Gradient;
public FrameRenderer_Android(Context context)
: base(context) { }
protected override void DispatchDraw(Canvas canvas)
{
if(Control != null && Gradient != null)
{
var paint = new Android.Graphics.Paint()
{
Dither = true,
};
paint.SetShader(Gradient);
canvas.DrawPaint(paint);
}
base.DispatchDraw(canvas);
}
}
这是 xaml
<ContentPage.Content>
<cntrl:GradientGrid RowSpacing="0"
Margin="0,20,0,0"
StartColor="{StaticResource GracePink}"
MiddleColor="{StaticResource GracePurple}"
EndColor="{StaticResource GraceDarkPurple}"
IsClippedToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<cntrl:CustomFrame Grid.Column="2"
Grid.Row="0"
IsClippedToBounds="True"
CornerRadius="20,20,0,0">
</cntrl:CustomFrame>
<cntrl:CustomFrame Grid.ColumnSpan="4"
Grid.Row="1"
IsClippedToBounds="True"
CornerRadius="40,40,0,0">
</cntrl:CustomFrame>
</cntrl:GradientGrid>
</ContentPage.Content>