Xamarin.FormsでFrameに調整可能な影をつける方法
こちらの記事がまさにピッタリのものだった.素晴らしい.ありがとうございます.
現時点ではこれがベストかな. カスタムレンダラーを使う.
Frameを継承したShadowFrameクラスを作る.
Elevationプロパティを追加する.バインディング可能にしておいたけれど,用途的にバインディングで影を調節するということは考えにくいので,必要はないと思う.
Elevationプロパティの値が高いほど,影がぼやけてコントロールの位置が高いように見える.
public class ShadowFrame:Frame { public float Elevation { get { return (float)GetValue(ElevationProperty); } set { SetValue(ElevationProperty, value); } } public static readonly BindableProperty ElevationProperty = BindableProperty.Create( propertyName: nameof(Elevation), returnType: typeof(float), declaringType: typeof(ShadowFrame), defaultValue: 4.0f, defaultBindingMode: BindingMode.OneWay, propertyChanged: ElevationPropertyChanged ); private static void ElevationPropertyChanged(BindableObject bindable, object oldValue, object newValue) { } }
ShadowFrameに影をつけるレンダラー
カスタムレンダラーの方は以下のとおり.
using Android.Content; using Android.Support.V4.View; using CustomRenderer.Controlls; using CustomRenderer.Droid.Renderers; using System.ComponentModel; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer(typeof(ShadowFrame), typeof(ShadowFrameRenderer))] namespace CustomRenderer.Droid.Renderers { public class ShadowFrameRenderer : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer { public ShadowFrameRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Frame> e) { base.OnElementChanged(e); if(e.NewElement==null) { return; } UpdateElevation(); } private void UpdateElevation() { var shadowFrame = (ShadowFrame)Element; Control.StateListAnimator = new Android.Animation.StateListAnimator(); ViewCompat.SetElevation(this, shadowFrame.Elevation); ViewCompat.SetElevation(Control, shadowFrame.Elevation); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if(e.PropertyName=="Elevation") { UpdateElevation(); } } } }
UpdateElevationメソッドでElevationプロパティの値を更新している.
ViewCompat.SetElevation(this, shadowFrame.Elevation);
ViewCompat.SetElevation(Control, shadowFrame.Elevation);
使ってみる
以下のようにShadowFrameを使ってみる.
<controls:ShadowFrame Elevation="50" />
こんな感じにFrameに影がつき,Elevationの値を変更すると影の様子が異なる.
Elevation=50
Elevation=10
影付きのボタンを作る
このShadowFrameの中にButtonを入れて,どちらも円形にすると影付きのButtonになる.
<controls:ShadowFrame Padding="0" CornerRadius="40" Elevation="50" HorizontalOptions="Center" IsClippedToBounds="True" VerticalOptions="Center"> <Button BackgroundColor="White" CornerRadius="40" HeightRequest="70" HorizontalOptions="Center" Text="A" VerticalOptions="Center" WidthRequest="70" /> </controls:ShadowFrame>
ボタンに色を付けると,影があまり目立たなくなるけれども立体感は感じられる.
FrameのHasShadow
プロパティはだいたいElevation=10ぐらいの感覚.
参考にしたサイトはiOSのレンダラーも書いてある.
iOS側レンダラー
public class MaterialFrameRenderer : FrameRenderer { public static void Initialize() { // empty, but used for beating the linker } protected override void OnElementChanged(ElementChangedEventArgs<Frame> e) { base.OnElementChanged(e); if (e.NewElement == null) return; UpdateShadow(); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if(e.PropertyName == "Elevation") { UpdateShadow(); } } private void UpdateShadow() { var materialFrame = (MaterialFrame)Element; // Update shadow to match better material design standards of elevation Layer.ShadowRadius = materialFrame.Elevation; Layer.ShadowColor = UIColor.Gray.CGColor; Layer.ShadowOffset = new CGSize(2, 2); Layer.ShadowOpacity = 0.80f; Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath; Layer.MasksToBounds = false; } }
iOS側は色々設定できるみたいでいいなぁ.
参考: