it-swarm.com.ru

Как установить эффект тени на ImageView

Я пытаюсь установить тень на представлении «Изображение» на Xamarin.Forms (для платформы Android), и у меня есть несколько примеров в Интернете. 

Код PCL довольно прост, и платформа тоже казалась довольно простой. Рецепт, доступный на официальном сайте разработчиков xamarin выглядит примерно так:

[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). В другие источники я увидел нечто более общее, например:

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, но не смог понять, как они заставили работать эффект тени. 

Кто-то может помочь мне с этим?

15
Diego Rafael Souza

К сожалению, нет простого способа заставить это работать на Android. Но есть несколько вариантов, которые вы можете попробовать. 

Опция 1

Существует несколько неподдерживаемых операций рисования для слоев с аппаратным ускорением , включая SetShadowLayer для нетекстовых представлений.

Итак, чтобы получить визуализацию SetShadowLayer для нетекстового представления, вам нужно установить визуализацию LayerType как SOFTWARE, как описано в этом решении

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);
    }
}
6
G.Sharada

Пожалуйста, попробуйте этот код ниже будет работать

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

Я рекомендую использовать свойство Elevation вместо ShadowLayer; Android сам добавит правильную тень в зависимости от заданной вами высоты и будет придерживаться Material Design.

Хотя для этого подхода вам придется использовать пользовательский рендер, и вам нужно будет создать другой рендер для iOS/UWP.

0
sme