Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

ShadowEffect #725

Merged
merged 9 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions samples/XCT.Sample/Pages/Effects/ShadowEffectPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<pages:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:pages="clr-namespace:Xamarin.CommunityToolkit.Sample.Pages"
x:Class="Xamarin.CommunityToolkit.Sample.Pages.Effects.ShadowEffectPage"
BackgroundColor="White"
x:Name="Page">

<pages:BasePage.Resources>
<Style x:Key="ShadowLabel" TargetType="Label">
<Setter Property="FontSize" Value="22" />
<Setter Property="FontAttributes" Value="Bold" />
<Setter Property="TextColor" Value="Black" />
</Style>
</pages:BasePage.Resources>

<ScrollView Padding="{StaticResource ContentPadding}">
<StackLayout Spacing="30"
Padding="15, 0">

<StackLayout Spacing="10">
<Label Text="Labels:"/>

<Label Text="Label With Black Shadow"
Style="{StaticResource ShadowLabel}"
xct:ShadowEffect.Color="Black"/>

<Label Text="Label With Shifted Red Shadow"
Style="{StaticResource ShadowLabel}"
xct:ShadowEffect.Color="Red"
xct:ShadowEffect.OffsetX="10"
xct:ShadowEffect.OffsetY="10" />

<Label Text="Label With Gold Shadow"
Style="{StaticResource ShadowLabel}"
xct:ShadowEffect.Color="Gold"
xct:ShadowEffect.Radius="20"/>
</StackLayout>

<StackLayout Spacing="10">
<Label Text="Stack Layout with Shadow" />
<StackLayout xct:ShadowEffect.Color="Black">
<BoxView Color="White" />
<BoxView Color="Red" />
<BoxView Color="White" />
</StackLayout>
</StackLayout>

<StackLayout Spacing="10">
<Label Text="Box Views With Colored Shadows" />
<Grid RowDefinitions="*,*"
ColumnDefinitions="*,*,*,*,*"
RowSpacing="10"
ColumnSpacing="10">

<Grid.Behaviors>
<xct:ImpliedOrderGridBehavior/>
</Grid.Behaviors>

<BoxView Color="White" xct:ShadowEffect.Color="Blue" />
<BoxView Color="White" xct:ShadowEffect.Color="Gold" />
<BoxView Color="White" xct:ShadowEffect.Color="Purple" />
<BoxView Color="White" xct:ShadowEffect.Color="HotPink" />
<BoxView Color="White" xct:ShadowEffect.Color="Orange" />
<BoxView Color="White" xct:ShadowEffect.Color="Brown" />
<BoxView Color="White" xct:ShadowEffect.Color="Lime" />
<BoxView Color="White" xct:ShadowEffect.Color="Yellow" />
<BoxView Color="White" xct:ShadowEffect.Color="DarkRed" />
<BoxView Color="White" xct:ShadowEffect.Color="LightSalmon" />

</Grid>
</StackLayout>

</StackLayout>
</ScrollView>
</pages:BasePage>
11 changes: 11 additions & 0 deletions samples/XCT.Sample/Pages/Effects/ShadowEffectPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

namespace Xamarin.CommunityToolkit.Sample.Pages.Effects
{
public partial class ShadowEffectPage
{
public ShadowEffectPage()
{
InitializeComponent();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public class EffectsGalleryViewModel : BaseGalleryViewModel
typeof(TouchEffectPage),
nameof(TouchEffect),
"The TouchEffect is an effect that allows changing the view's appearance depending on the touch state (normal, pressed, hovered). Also, it allows to handle long presses."),

new SectionModel(
typeof(ShadowEffectPage),
nameof(ShadowEffect),
"The ShadowEffect allows all views to display shadow."),
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ sealed class EffectIds
/// Effect Id for <see cref="TouchEffect"/>
/// </summary>
public static string TouchEffect => $"{effectResolutionGroupName}.{nameof(TouchEffect)}";

/// <summary>
/// Effect Id for <see cref="ShadowEffect"/>
/// </summary>
public static string ShadowEffect => $"{effectResolutionGroupName}.{nameof(ShadowEffect)}";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
using Android.Views;
using AView = Android.Views.View;
using Android.OS;
using System.ComponentModel;
using Xamarin.CommunityToolkit.Effects;
using Xamarin.CommunityToolkit.Android.Effects;
using Android.Widget;

[assembly: ExportEffect(typeof(PlatformShadowEffect), nameof(ShadowEffect))]

namespace Xamarin.CommunityToolkit.Android.Effects
{
public class PlatformShadowEffect : PlatformEffect
{
const float defaultRadius = 10f;

const float defaultOpacity = 1f;

AView View => Control ?? Container;

protected override void OnAttached()
=> Update();

protected override void OnDetached()
{
if (View == null)
return;

View.Elevation = 0;
}

protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged(args);

if (View == null)
return;

switch (args.PropertyName)
{
case nameof(ShadowEffect.ColorPropertyName):
case nameof(ShadowEffect.OpacityPropertyName):
case nameof(ShadowEffect.RadiusPropertyName):
case nameof(ShadowEffect.OffsetXPropertyName):
case nameof(ShadowEffect.OffsetYPropertyName):
View.Invalidate();
Update();
break;
}
}

void Update()
{
if (View == null || Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
return;

var radius = (float)ShadowEffect.GetRadius(Element);
if (radius < 0)
radius = defaultRadius;

var opacity = ShadowEffect.GetOpacity(Element);
if (opacity < 0)
opacity = defaultOpacity;

var androidColor = ShadowEffect.GetColor(Element).MultiplyAlpha(opacity).ToAndroid();

if (View is TextView textView)
{
var offsetX = (float)ShadowEffect.GetOffsetX(Element);
var offsetY = (float)ShadowEffect.GetOffsetY(Element);
textView.SetShadowLayer(radius, offsetX, offsetY, androidColor);
return;
}

View.OutlineProvider = (Element as VisualElement)?.BackgroundColor.A > 0
? ViewOutlineProvider.PaddedBounds
: ViewOutlineProvider.Bounds;

View.Elevation = View.Context.ToPixels(radius);

if (Build.VERSION.SdkInt < BuildVersionCodes.P)
return;

View.SetOutlineAmbientShadowColor(androidColor);
View.SetOutlineSpotShadowColor(androidColor);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System;
using System.ComponentModel;
using CoreGraphics;
using Xamarin.CommunityToolkit.Effects;
using Xamarin.Forms;

#if __IOS__
using NativeView = UIKit.UIView;
using Xamarin.Forms.Platform.iOS;
using Xamarin.CommunityToolkit.iOS.Effects;
#elif __MACOS__
using NativeView = AppKit.NSView;
using Xamarin.Forms.Platform.MacOS;
using Xamarin.CommunityToolkit.macOS.Effects;
#endif

[assembly: ExportEffect(typeof(PlatformShadowEffect), nameof(ShadowEffect))]

#if __IOS__
namespace Xamarin.CommunityToolkit.iOS.Effects
#elif __MACOS__
namespace Xamarin.CommunityToolkit.macOS.Effects
#endif
{
public class PlatformShadowEffect : PlatformEffect
{
const float defaultRadius = 10f;

const float defaultOpacity = .5f;

NativeView View => Control ?? Container;

protected override void OnAttached()
{
if (View == null)
return;

UpdateColor();
UpdateOpacity();
UpdateRadius();
UpdateOffset();
}

protected override void OnDetached()
{
if (View?.Layer == null)
return;

View.Layer.ShadowOpacity = 0;
}

protected override void OnElementPropertyChanged(PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged(args);

if (View == null)
return;

switch (args.PropertyName)
{
case nameof(ShadowEffect.ColorPropertyName):
UpdateColor();
break;
case nameof(ShadowEffect.OpacityPropertyName):
UpdateOpacity();
break;
case nameof(ShadowEffect.RadiusPropertyName):
UpdateRadius();
break;
case nameof(ShadowEffect.OffsetXPropertyName):
case nameof(ShadowEffect.OffsetYPropertyName):
UpdateOffset();
break;
}
}

void UpdateColor()
=> View.Layer.ShadowColor = ShadowEffect.GetColor(Element).ToCGColor();

void UpdateOpacity()
{
var opacity = (float)ShadowEffect.GetOpacity(Element);
View.Layer.ShadowOpacity = opacity < 0
? defaultOpacity
: opacity;
}

void UpdateRadius()
{
var radius = (nfloat)ShadowEffect.GetRadius(Element);
View.Layer.ShadowRadius = radius < 0
? defaultRadius
: radius;
}

void UpdateOffset()
=> View.Layer.ShadowOffset = new CGSize((double)ShadowEffect.GetOffsetX(Element), (double)ShadowEffect.GetOffsetY(Element));
}
}
Loading