Xamarin.Forms の再利用可能な RoundEffect
重要
コントロールを円の形にレンダリングするために RoundEffect
を使用する必要はなくなりました。 最新の推奨手法は EllipseGeometry
を利用してコントロールをクリップすることです。 詳しくは、「Geometry を使用してクリップする」をご覧ください。
RoundEffect を利用すると、円として VisualElement
から派生するあらゆるコントロールのレンダリングが簡単になります。 このエフェクトを使用し、円形の画像、ボタン、その他のコントロールを作成できます。
共有 RoutingEffect を作成する
クロスプラットフォームの効果を作成するには、効果クラスを共有プロジェクトで作成する必要があります。 このサンプル アプリケーションでは、RoutingEffect
クラスから派生する空の RoundEffect
クラスが作成されます。
public class RoundEffect : RoutingEffect
{
public RoundEffect() : base($"Xamarin.{nameof(RoundEffect)}")
{
}
}
このクラスを利用すると、コードまたは XAML の効果の参照を共有プロジェクトで解決できますが、機能は与えられません。 この効果では、プラットフォームごとの実装が必要になります。
Android 効果を実装する
Android プラットフォーム プロジェクトでは、PlatformEffect
から派生する RoundEffect
クラスが定義されます。 このクラスには、Xamarin.Forms で効果クラスを解決できるようにする assembly
属性がタグ付けされます。
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.Droid.RoundEffect), nameof(RoundEffectDemo.Droid.RoundEffect))]
namespace RoundEffectDemo.Droid
{
public class RoundEffect : PlatformEffect
{
// ...
}
}
Android プラットフォームでは、OutlineProvider
の概念を使用し、コントロールの端が定義されます。 このサンプル プロジェクトには、ViewOutlineProvider
クラスから派生する CornerRadiusProvider
クラスが含まれています。
class CornerRadiusOutlineProvider : ViewOutlineProvider
{
Element element;
public CornerRadiusOutlineProvider(Element formsElement)
{
element = formsElement;
}
public override void GetOutline(Android.Views.View view, Outline outline)
{
float scale = view.Resources.DisplayMetrics.Density;
double width = (double)element.GetValue(VisualElement.WidthProperty) * scale;
double height = (double)element.GetValue(VisualElement.HeightProperty) * scale;
float minDimension = (float)Math.Min(height, width);
float radius = minDimension / 2f;
Rect rect = new Rect(0, 0, (int)width, (int)height);
outline.SetRoundRect(rect, radius);
}
}
このクラスでは、Xamarin.Forms の Element
インスタンスの Width
および Height
プロパティを使用し、最短寸法の半分である半径が計算されます。
輪郭を与えるものが定義されると、RoundEffect
クラスではそれを使用して効果を実装できます。
public class RoundEffect : PlatformEffect
{
ViewOutlineProvider originalProvider;
Android.Views.View effectTarget;
protected override void OnAttached()
{
try
{
effectTarget = Control ?? Container;
originalProvider = effectTarget.OutlineProvider;
effectTarget.OutlineProvider = new CornerRadiusOutlineProvider(Element);
effectTarget.ClipToOutline = true;
}
catch (Exception ex)
{
Console.WriteLine($"Failed to set corner radius: {ex.Message}");
}
}
protected override void OnDetached()
{
if(effectTarget != null)
{
effectTarget.OutlineProvider = originalProvider;
effectTarget.ClipToOutline = false;
}
}
}
OnAttached
メソッドは、効果が要素にアタッチされると呼び出されます。 既存の OutlineProvider
オブジェクトは保存され、効果がデタッチされたときに復元できます。 CornerRadiusOutlineProvider
の新しいインスタンスが OutlineProvider
として使用され、ClipToOutline
が true に設定され、オーバーフローする要素が輪郭の縁取りに留められます。
効果が要素から削除されると OnDetatched
メソッドが呼び出され、元の OutlineProvider
値が復元されます。
Note
要素の型によっては、Control
プロパティは null になることもならないこともあります。 Control
プロパティが null でない場合、丸い角をコントロールに直接適用できます。 ただし、null の場合、丸い角は Container
オブジェクトに適用する必要があります。 effectTarget
フィールドを利用すると、適切なオブジェクトに効果を適用できます。
iOS 効果を実装する
iOS プラットフォーム プロジェクトでは、PlatformEffect
から派生する RoundEffect
クラスが定義されます。 このクラスには、Xamarin.Forms で効果クラスを解決できるようにする assembly
属性がタグ付けされます。
[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.iOS.RoundEffect), nameof(RoundEffectDemo.iOS.RoundEffect))]
namespace RoundEffectDemo.iOS
{
public class RoundEffect : PlatformEffect
{
// ...
}
iOS では、コントロールに Layer
プロパティが与えられ、このプロパティには CornerRadius
プロパティが含まれています。 iOS での RoundEffect
クラス実装では、角の半径が適切に計算され、レイヤーの CornerRadius
プロパティが更新されます。
public class RoundEffect : PlatformEffect
{
nfloat originalRadius;
UIKit.UIView effectTarget;
protected override void OnAttached()
{
try
{
effectTarget = Control ?? Container;
originalRadius = effectTarget.Layer.CornerRadius;
effectTarget.ClipsToBounds = true;
effectTarget.Layer.CornerRadius = CalculateRadius();
}
catch (Exception ex)
{
Console.WriteLine($"Failed to set corner radius: {ex.Message}");
}
}
protected override void OnDetached()
{
if (effectTarget != null)
{
effectTarget.ClipsToBounds = false;
if (effectTarget.Layer != null)
{
effectTarget.Layer.CornerRadius = originalRadius;
}
}
}
float CalculateRadius()
{
double width = (double)Element.GetValue(VisualElement.WidthRequestProperty);
double height = (double)Element.GetValue(VisualElement.HeightRequestProperty);
float minDimension = (float)Math.Min(height, width);
float radius = minDimension / 2f;
return radius;
}
}
CalculateRadius
メソッドでは、Xamarin.Forms の Element
の最小寸法に基づいて半径が計算されます。 効果がコントロールにアタッチされると OnAttached
メソッドが呼び出され、レイヤーの CornerRadius
プロパティが更新されます。 オーバーフローする要素がコントロールの縁取りに留められるよう、ClipToBounds
プロパティが true
に設定されます。 効果がコントロールから削除されると OnDetatched
メソッドが呼び出され、変更が元に戻され、元の角の半径が復元されます。
Note
要素の型によっては、Control
プロパティは null になることもならないこともあります。 Control
プロパティが null でない場合、丸い角をコントロールに直接適用できます。 ただし、null の場合、丸い角は Container
オブジェクトに適用する必要があります。 effectTarget
フィールドを利用すると、適切なオブジェクトに効果を適用できます。
エフェクトを使用する
プラットフォーム間で効果が実装されると、それは Xamarin.Forms コントロールで使用できます。 RoundEffect
の一般的な応用では、Image
オブジェクトが丸くなります。 次の XAML では、効果が Image
インスタンスに適用された場合を確認できます。
<Image Source=outdoors"
HeightRequest="100"
WidthRequest="100">
<Image.Effects>
<local:RoundEffect />
</Image.Effects>
</Image>
この効果はコードでも適用できます。
var image = new Image
{
Source = ImageSource.FromFile("outdoors"),
HeightRequest = 100,
WidthRequest = 100
};
image.Effects.Add(new RoundEffect());
RoundEffect
クラスは、VisualElement
から派生するすべてのコントロールに適用できます。
Note
効果で正しい半径を計算するには、それが適用されるコントロールに明示的なサイズ設定を与える必要があります。 そのため、HeightRequest
プロパティと WidthRequest
プロパティを定義する必要があります。 影響を受けるコントロールが StackLayout
に表示される場合、その HorizontalOptions
プロパティでは、LayoutOptions.CenterAndExpand
など、Expand 値を使用しないでください。使用した場合、寸法が精確になりません。