如何在ImageView上设置阴影效果
How to set shadow effect on ImageView
我正在尝试在 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
方法的 class)。在 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 源代码,但无法理解它们是如何使阴影效果起作用的。
有人可以帮我吗?
我建议使用 Elevation
属性 而不是 ShadowLayer
; Android 会根据您提供的高度自行添加正确的阴影,并且会遵守 Material 设计。
尽管您必须为此方法使用自定义渲染器,并且需要为 iOS/UWP 创建一个不同的渲染器。
请尝试下面的代码会起作用
<Frame OutlineColor="Transparent" Padding="0" CornerRadius="0" VerticalOptions="Center" BackgroundColor="Teal">
<Image Source="Imagename" Aspect="Fill" />
</Frame>
遗憾的是,android 没有直接的方法来实现这一点。但是您可以尝试一些选项。
选项 #1
There are several unsupported drawing operations for hardware accelerated layers,包括 SetShadowLayer
的非文本视图。
因此,为了获得非文本视图的 SetShadowLayer
渲染,您需要将 LayerType
渲染设置为 SOFTWARE
,如此 solution.
SetLayerType(LayerType.Software, null);
但主要缺点当然是它可能是性能问题。
选项#2
第二种选择是使用径向渐变来模拟阴影。我已将其实现为渲染器(但您也应该能够将其实现为效果)。结果当然不如模糊阴影效果那么好。您还必须设置右侧 Padding
以让一些 space 渲染阴影,并在图像下方可见。
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);
}
}
我正在尝试在 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
方法的 class)。在 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 源代码,但无法理解它们是如何使阴影效果起作用的。
有人可以帮我吗?
我建议使用 Elevation
属性 而不是 ShadowLayer
; Android 会根据您提供的高度自行添加正确的阴影,并且会遵守 Material 设计。
尽管您必须为此方法使用自定义渲染器,并且需要为 iOS/UWP 创建一个不同的渲染器。
请尝试下面的代码会起作用
<Frame OutlineColor="Transparent" Padding="0" CornerRadius="0" VerticalOptions="Center" BackgroundColor="Teal">
<Image Source="Imagename" Aspect="Fill" />
</Frame>
遗憾的是,android 没有直接的方法来实现这一点。但是您可以尝试一些选项。
选项 #1
There are several unsupported drawing operations for hardware accelerated layers,包括 SetShadowLayer
的非文本视图。
因此,为了获得非文本视图的 SetShadowLayer
渲染,您需要将 LayerType
渲染设置为 SOFTWARE
,如此 solution.
SetLayerType(LayerType.Software, null);
但主要缺点当然是它可能是性能问题。
选项#2
第二种选择是使用径向渐变来模拟阴影。我已将其实现为渲染器(但您也应该能够将其实现为效果)。结果当然不如模糊阴影效果那么好。您还必须设置右侧 Padding
以让一些 space 渲染阴影,并在图像下方可见。
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);
}
}