共用方式為


Xamarin.Forms 可重複使用的 RoundEffect

重要

不再需要使用 RoundEffect 將控件轉譯為圓形。 最新的建議方法是使用 EllipseGeometry來裁剪控件。 如需詳細資訊,請參閱 使用 Geometry 裁剪。

RoundEffect 可簡化轉譯任何衍生自 VisualElement 做為圓形的控件。 這個效果可用來建立迴圈影像、按鈕和其他控件:

iOS 和 Android 上的 RoundEffect 螢幕快照

建立共用 RoutingEffect

必須在共享專案中建立效果類別,才能建立跨平臺效果。 範例應用程式會建立衍生自 類別的RoutingEffect空白RoundEffect類別:

public class RoundEffect : RoutingEffect
{
    public RoundEffect() : base($"Xamarin.{nameof(RoundEffect)}")
    {
    }
}

這個類別可讓共用專案解析程序代碼或 XAML 中效果的參考,但不提供任何功能。 效果必須具有每個平台的實作。

實作Android效果

Android 平台專案會 RoundEffect 定義衍生自 PlatformEffect的類別。 這個類別會以 assembly 允許 Xamarin.Forms 解析效果類別的屬性標記:

[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.Droid.RoundEffect), nameof(RoundEffectDemo.Droid.RoundEffect))]
namespace RoundEffectDemo.Droid
{
    public class RoundEffect : PlatformEffect
    {
        // ...
    }
}

Android 平臺會使用的概念 OutlineProvider 來定義控件的邊緣。 範例專案包含 CornerRadiusProvider 衍生自 類別的 ViewOutlineProvider 類別:

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);
    }
}

這個類別會使用 Width 實例的 Xamarin.FormsElementHeight 屬性來計算最小維度的一半半徑。

定義大綱提供者之後, 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會當做 ,並ClipToOutline設定為 OutlineProvider true,將溢位元素裁剪到外框線。

OnDetatched 項目移除效果並還原原始 OutlineProvider 值時,會呼叫 方法。

注意

視專案類型而定, Control 屬性可能或可能不是 Null。 Control如果 屬性不是 Null,則可以將圓角直接套用至 控件。 不過,如果它是 Null,則必須將圓角套用至 Container 物件。 欄位 effectTarget 允許將效果套用至適當的物件。

實作 iOS 效果

iOS 平台專案會 RoundEffect 定義衍生自 PlatformEffect的類別。 這個類別會以 assembly 允許 Xamarin.Forms 解析效果類別的屬性標記:

[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(RoundEffectDemo.iOS.RoundEffect), nameof(RoundEffectDemo.iOS.RoundEffect))]
namespace RoundEffectDemo.iOS
{
    public class RoundEffect : PlatformEffect
    {
        // ...
    }

在 iOS 上,控制件具有 Layer 屬性的屬性 CornerRadiusRoundEffect iOS 上的類別實作會計算適當的圓角半徑,並更新圖層的 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.FormsElement最小維度計算半徑。 當 OnAttached 效果附加至控件時,會呼叫 方法,並更新層次的 CornerRadius 屬性。 它會將 ClipToBounds 屬性設定為 , true 讓溢位的專案裁剪到控件的框線。 OnDetatched從控件移除效果並反轉這些變更時,會呼叫 方法,以還原原始圓角半徑。

注意

視專案類型而定, 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的任何控件。

注意

若要讓效果計算正確的半徑,套用至的控件必須具有明確的重設大小。 因此, HeightRequest 應該定義 和 WidthRequest 屬性。 如果受影響的控件出現在 中 StackLayout,則其 HorizontalOptions 屬性不應該使用其中一個 Expand 值,例如 LayoutOptions.CenterAndExpand ,否則其不會有精確的維度。