【问题标题】:How to set shadow effect on ImageView如何在 ImageView 上设置阴影效果
【发布时间】:2018-07-05 18:11:49
【问题描述】:

我正在尝试在 Xamarin.Forms(针对 Android 平台)上的图像视图上设置阴影,我在互联网上获得了一些示例。

PCL 代码非常简单,平台看起来也很简单。 The recipe available at the official xamarin developer site 是这样的:

[assembly: ResolutionGroupName("MyGroupName")]
[assembly: ExportEffect(typeof(LabelShadowEffect), "ShadowEffect")]
namespace MyWorkspace
{
    public class LabelShadowEffect : PlatformEffect
    {
        protected override void OnAttached()
        {
            try
            {
                var control = (Control as TextView); // TextView have the SetShadowLayer method, but others views don't

                var effect = (ShadowEffect)Element.Effects.FirstOrDefault(e => e is ShadowEffect);
                if (effect != null)
                {
                    float radius = effect.Radius;
                    float distanceX = effect.DistanceX;
                    float distanceY = effect.DistanceY;
                    Android.Graphics.Color color = effect.Color.ToAndroid();
                    control?.SetShadowLayer(radius, distanceX, distanceY, color);
                }
            }
            catch (Exception)
            {               
            }
        }

        protected override void OnDetached() 
        { 
        }
    }
}

所以我注意到这个配方仅适用于使用 TextView 呈现的组件(这是唯一具有 SetShadowLayer 方法的类)。在other sources 我看到了一些更通用的东西,比如:

public class ShadowEffect : PlatformEffect
{
    protected override void OnAttached ()
    {
        Container.Layer.ShadowOpacity = 1;
        Container.Layer.ShadowColor = UIColor.Black.ToCGColor;
        Container.Layer.ShadowRadius = 6;
    }

    protected override void OnDetached ()
    {
        Container.Layer.ShadowOpacity = 0;
    }
}

通过使用UIColor,我知道它是针对iOS 平台的。在 Android 视图上没有这样的事情。我查看了 XF FrameRenderer 源代码,但无法理解它们如何使阴影效果起作用。

有人可以帮我吗?

【问题讨论】:

    标签: c# xamarin xamarin.forms xamarin.android shadow


    【解决方案1】:

    遗憾的是,没有直接的方法可以让它在 android 上运行。但是您可以尝试一些选项。

    选项#1

    There are several unsupported drawing operations for hardware accelerated layers,包括用于非文本视图的SetShadowLayer

    因此,为了获得非文本视图的SetShadowLayer 渲染,您需要将LayerType 渲染设置为SOFTWARE,如solution 中所述。

    SetLayerType(LayerType.Software, null);
    

    但主要的缺点当然是它可能是一个性能问题。

    选项 #2

    第二个选项是使用径向渐变来模拟阴影。我已经将它实现为渲染器(但您也应该能够将其实现为效果)。结果当然不如模糊阴影效果好。您还必须设置正确的Padding,以便为阴影渲染留出一些空间,并在图像下方可见。

    protected override void DispatchDraw(global::Android.Graphics.Canvas canvas)
    {
        try
        {
            var nativeCtrl = Control;
            var formsElement = Element;
            if (nativeCtrl == null || formsElement == null)
            {
                base.DispatchDraw(canvas);
                return;
            }
    
            //convert from logical to native metrics if need be
            var shadowDistanceX = 10f;
            var shadowDistanceY = 10f;
            var shadowRadius = 5f;
            var shadowOpacity = .5f;
            var shadowColor = Color.Black;
            var cornerRadius = 0.2f;
    
            var bounds = formsElement.Bounds;
    
            var left = shadowDistanceX;
            var top = shadowDistanceY;
            var right = Width + shadowDistanceX;
            var bottom = Height + shadowDistanceY;
    
            var rect = new Android.Graphics.RectF(left, top, right, bottom);
    
            canvas.Save();
            using (var paint = new Android.Graphics.Paint { AntiAlias = true })
            {
                paint.SetStyle(Android.Graphics.Paint.Style.Fill);
    
                var nativeShadowColor = shadowColor.MultiplyAlpha(shadowOpacity * 0.75f).ToAndroid();
                paint.Color = nativeShadowColor;
    
                var gradient = new Android.Graphics.RadialGradient(
                    0.5f, 0.5f,
                    shadowRadius,
                    shadowColor.ToAndroid(),
                    nativeShadowColor,
                    Android.Graphics.Shader.TileMode.Clamp
                );
                paint.SetShader(gradient);
    
                //convert from logical to native metrics if need be
                var nativeRadius = cornerRadius;  
                canvas.DrawRoundRect(rect, nativeRadius, nativeRadius, paint);
    
                var clipPath = new Android.Graphics.Path();
                clipPath.AddRoundRect(new Android.Graphics.RectF(0f, 0f, Width, Height), nativeRadius, nativeRadius, Android.Graphics.Path.Direction.Cw);
                canvas.ClipPath(clipPath);
            }
            canvas.Restore();
        }
        catch (Exception ex)
        {
            //log exception
        }
    
        base.DispatchDraw(canvas);
    }   
    

    选项#3

    另一种选择是使用SkiaSharp for Forms - 即创建一个容器(或分层)视图,在子视图(图像)周围呈现阴影。您也可以让 SkiaSharp 渲染图像,或者在布局中嵌入基于 XF 的图像控件。

    protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
    {
        var imgInfo = args.Info;
        var surface = args.Surface;
        var canvas = surface.Canvas;
    
        var drawBounds = imgInfo.Rect;
        var path = new SKPath();
        var cornerRadius = 5f;
    
        if (cornerRadius > 0)
        {
            path.AddRoundedRect(drawBounds, cornerRadius, cornerRadius);
        }
        else
        {
            path.AddRect(drawBounds);
        }
    
        using (var paint = new SKPaint()
        {
            ImageFilter = SKImageFilter.CreateDropShadow(
                                            offsetX,
                                            offsetY,
                                            blurX,
                                            blurY,
                                            color,
                                            SKDropShadowImageFilterShadowMode.DrawShadowOnly),
        })
        {
            canvas.DrawPath(path, paint);
        }
    }
    

    【讨论】:

    • 太棒了!所有这些选项看起来都非常有希望。稍后我会决定哪个最适合每种需求,但它确实是我所期待的一种解决方案。谢谢
    【解决方案2】:

    请试试下面的代码是否可行

    <Frame OutlineColor="Transparent" Padding="0" CornerRadius="0" VerticalOptions="Center" BackgroundColor="Teal">
        <Image Source="Imagename" Aspect="Fill" />
    </Frame>
    

    【讨论】:

    • 感谢您的宝贵时间!我会试一试,让你知道。
    • 正如我所想,它只是一个框架内的图像,图像没有附加阴影效果,但还是谢谢
    • 框架默认提供阴影效果和它的工作,所以一旦尝试,让我知道
    • 是的,确实如此。但是这个阴影在框架的矩形形状下(有或没有圆角。我需要图像形状中的阴影
    【解决方案3】:

    我建议使用Elevation 属性而不是ShadowLayer; Android 会根据您提供的高度自行添加正确的阴影,并且会遵循 Material Design。

    尽管您必须为此方法使用自定义渲染器,并且需要为 iOS/UWP 创建不同的渲染器。

    【讨论】:

    • 我也想设置阴影颜色...但我会尽力让你知道
    • @DiegoRafaelSouza 啊,很遗憾,Elevation 属性不允许您自定义阴影颜色。
    • 我已经测试了海拔的东西,但是效果太软了,我没有太多的选项来定制它=/。非常感谢您的检查
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多