Xamarin.Forms RoundEffect riutilizzabile

Importante

Non è più necessario usare un RoundEffect oggetto per eseguire il rendering di un controllo come cerchio. L'approccio consigliato più recente consiste nel ritagliare il controllo usando un oggetto EllipseGeometry. Per altre informazioni, vedere Ritaglia con una geometria.

RoundEffect semplifica il rendering di qualsiasi controllo che deriva da VisualElement come cerchio. Questo effetto può essere usato per creare immagini circolari, pulsanti e altri controlli:

Screenshot RoundEffect in iOS e Android

Creare un routing condivisoEffect

Per creare un effetto multipiattaforma, è necessario creare una classe di effetto nel progetto condiviso. L'applicazione di esempio crea una classe vuota RoundEffect che deriva dalla RoutingEffect classe :

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

Questa classe consente al progetto condiviso di risolvere i riferimenti all'effetto nel codice o xaml, ma non fornisce alcuna funzionalità. L'effetto deve avere implementazioni per ogni piattaforma.

Implementare l'effetto Android

Il progetto di piattaforma Android definisce una RoundEffect classe che deriva da PlatformEffect. Questa classe è contrassegnata con assembly attributi che consentono di Xamarin.Forms risolvere la classe dell'effetto:

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

La piattaforma Android usa il concetto di un oggetto OutlineProvider per definire i bordi di un controllo. Il progetto di esempio include una CornerRadiusProvider classe che deriva dalla ViewOutlineProvider classe :

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

Questa classe utilizza le Width proprietà e Height dell'istanza Xamarin.FormsElement per calcolare un raggio che è la metà della dimensione più breve.

Una volta definito un provider di struttura, la RoundEffect classe può utilizzarla per implementare l'effetto:

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

Il OnAttached metodo viene chiamato quando l'effetto è associato a un elemento. L'oggetto esistente OutlineProvider viene salvato in modo che possa essere ripristinato quando l'effetto viene scollegato. Una nuova istanza di CornerRadiusOutlineProvider viene utilizzata come OutlineProvider e ClipToOutline viene impostata su true per ritagliare gli elementi di overflow ai bordi della struttura.

Il OnDetatched metodo viene chiamato quando l'effetto viene rimosso da un elemento e ripristina il valore originale OutlineProvider .

Nota

A seconda del tipo di elemento, la Control proprietà può essere o meno Null. Se la Control proprietà non è Null, gli angoli arrotondati possono essere applicati direttamente al controllo. Tuttavia, se è Null, gli angoli arrotondati devono essere applicati all'oggetto Container . Il effectTarget campo consente di applicare l'effetto all'oggetto appropriato.

Implementare l'effetto iOS

Il progetto di piattaforma iOS definisce una RoundEffect classe che deriva da PlatformEffect. Questa classe è contrassegnata con assembly attributi che consentono di Xamarin.Forms risolvere la classe dell'effetto:

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

In iOS i controlli hanno una Layer proprietà , che ha una CornerRadius proprietà . L'implementazione RoundEffect della classe in iOS calcola il raggio dell'angolo appropriato e aggiorna la proprietà del CornerRadius livello:

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

Il CalculateRadius metodo calcola un raggio in base alla dimensione minima dell'oggetto Xamarin.FormsElement. Il OnAttached metodo viene chiamato quando l'effetto viene associato a un controllo e aggiorna la proprietà del CornerRadius livello. Imposta la ClipToBounds proprietà su true in modo che gli elementi di overflow vengano ritagliati ai bordi del controllo. Il OnDetatched metodo viene chiamato quando l'effetto viene rimosso da un controllo e inverte queste modifiche, ripristinando il raggio dell'angolo originale.

Nota

A seconda del tipo di elemento, la Control proprietà può essere o meno Null. Se la Control proprietà non è Null, gli angoli arrotondati possono essere applicati direttamente al controllo. Tuttavia, se è Null, gli angoli arrotondati devono essere applicati all'oggetto Container . Il effectTarget campo consente di applicare l'effetto all'oggetto appropriato.

Utilizzare l'effetto

Una volta implementato l'effetto tra le piattaforme, può essere utilizzato dai Xamarin.Forms controlli. Un'applicazione comune di è la creazione di RoundEffect un Image oggetto circolare. Il codice XAML seguente mostra l'effetto applicato a un'istanza Image :

<Image Source=outdoors"
       HeightRequest="100"
       WidthRequest="100">
    <Image.Effects>
        <local:RoundEffect />
    </Image.Effects>
</Image>

L'effetto può essere applicato anche nel codice:

var image = new Image
{
    Source = ImageSource.FromFile("outdoors"),
    HeightRequest = 100,
    WidthRequest = 100
};
image.Effects.Add(new RoundEffect());

La RoundEffect classe può essere applicata a qualsiasi controllo che deriva da VisualElement.

Nota

Affinché l'effetto calcoli il raggio corretto, il controllo a cui viene applicato deve avere un ridimensionamento esplicito. Pertanto, è necessario definire le HeightRequest proprietà e WidthRequest . Se il controllo interessato viene visualizzato in un StackLayoutoggetto , la relativa HorizontalOptions proprietà non deve utilizzare uno dei valori Expand , LayoutOptions.CenterAndExpand ad esempio o non avrà dimensioni accurate.