我正试着在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源代码,但我无法理解它们如何使阴影效果起作用。
有人可以帮我吗?
答案 0 :(得分:6)
Sadly there is no straight-forward way to get this working for android. But there are some options that you can try.
There are several unsupported drawing operations for hardware accelerated layers, that includes SetShadowLayer
for non-text views.
So, in order to get the SetShadowLayer
render for a non-text view, you need to set the LayerType
rendering as SOFTWARE
as explained in this solution.
SetLayerType(LayerType.Software, null);
But the major drawback of course is that it can be a performance issue.
Second option is to use an radial gradient to emulate the shadow. I had implemented it as a renderer (but you should be able to implement it as an effect too). The result of course is not as great as a blurred shadow effect. You will also have to set the right Padding
to let some space for the shadow to render, and be visible under the image.
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);
}
Another option would be to use SkiaSharp for Forms - i.e. create a container (or layered) view that renders the shadow around the child view (image). You can either have SkiaSharp render the image too, or embed a XF based image control inside the layout.
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);
}
}
答案 1 :(得分:4)
请尝试以下代码才能正常工作
<Frame OutlineColor="Transparent" Padding="0" CornerRadius="0" VerticalOptions="Center" BackgroundColor="Teal">
<Image Source="Imagename" Aspect="Fill" />
</Frame>
答案 2 :(得分:0)
我建议使用Elevation
属性而不是ShadowLayer
; Android将根据您提供的高程添加正确的阴影,并且它将遵循Material Design。
虽然您必须为此方法使用自定义渲染器,但需要为iOS / UWP创建不同的渲染器。