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>