Partager via


Xamarin.Forms RoundEffect réutilisable

Important

Il n’est plus nécessaire d’utiliser un RoundEffect contrôle pour afficher un contrôle en tant que cercle. La dernière approche recommandée consiste à découper le contrôle à l’aide d’un EllipseGeometry. Pour plus d’informations, consultez Clip avec une géométrie.

RoundEffect simplifie le rendu de tout contrôle dérivé d’un VisualElement cercle. Cet effet peut être utilisé pour créer des images circulaires, des boutons et d’autres contrôles :

Captures d’écran RoundEffect sur iOS et Android

Créer un RoutingEffect partagé

Une classe d’effet doit être créée dans le projet partagé pour créer un effet multiplateforme. L’exemple d’application crée une classe vide RoundEffect qui dérive de la RoutingEffect classe :

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

Cette classe permet au projet partagé de résoudre les références à l’effet dans le code ou xaml, mais ne fournit aucune fonctionnalité. L’effet doit avoir des implémentations pour chaque plateforme.

Implémenter l’effet Android

Le projet de plateforme Android définit une RoundEffect classe qui dérive de PlatformEffect. Cette classe est marquée avec assembly des attributs qui permettent Xamarin.Forms de résoudre la classe d’effet :

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

La plateforme Android utilise le concept d’un OutlineProvider pour définir les bords d’un contrôle. L’exemple de projet inclut une CornerRadiusProvider classe qui dérive de la 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);
    }
}

Cette classe utilise les propriétés et Height les Width propriétés de l’instance Xamarin.FormsElement pour calculer un rayon qui correspond à la moitié de la dimension la plus courte.

Une fois qu’un fournisseur de plan est défini, la RoundEffect classe peut l’utiliser pour implémenter l’effet :

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

La OnAttached méthode est appelée lorsque l’effet est attaché à un élément. L’objet existant OutlineProvider est enregistré afin qu’il puisse être restauré lorsque l’effet est détaché. Une nouvelle instance du fichier CornerRadiusOutlineProvider est utilisée comme OutlineProvider valeur true pour ClipToOutline découper les éléments débordants vers les bordures hiérarchiques.

La OnDetatched méthode est appelée lorsque l’effet est supprimé d’un élément et restaure la valeur d’origine OutlineProvider .

Remarque

Selon le type d’élément, la Control propriété peut ou non être null. Si la Control propriété n’est pas null, les angles arrondis peuvent être appliqués directement au contrôle. Toutefois, s’il s’agit de null, les angles arrondis doivent être appliqués à l’objet Container . Le effectTarget champ permet l’application de l’effet à l’objet approprié.

Implémenter l’effet iOS

Le projet de plateforme iOS définit une RoundEffect classe qui dérive de PlatformEffect. Cette classe est marquée avec assembly des attributs qui permettent Xamarin.Forms de résoudre la classe d’effet :

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

Sur iOS, les contrôles ont une Layer propriété, qui a une CornerRadius propriété. L’implémentation RoundEffect de classe sur iOS calcule le rayon d’angle approprié et met à jour la propriété de la CornerRadius couche :

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

La CalculateRadius méthode calcule un rayon en fonction de la dimension minimale du Xamarin.FormsElement. La OnAttached méthode est appelée lorsque l’effet est attaché à un contrôle et met à jour la propriété de CornerRadius la couche. Elle définit la ClipToBounds propriété pour true que les éléments débordants soient clippés aux bordures du contrôle. La OnDetatched méthode est appelée lorsque l’effet est supprimé d’un contrôle et inverse ces modifications, en restaurant le rayon d’angle d’origine.

Remarque

Selon le type d’élément, la Control propriété peut ou non être null. Si la Control propriété n’est pas null, les angles arrondis peuvent être appliqués directement au contrôle. Toutefois, s’il s’agit de null, les angles arrondis doivent être appliqués à l’objet Container . Le effectTarget champ permet l’application de l’effet à l’objet approprié.

Consommer l’effet

Une fois l’effet implémenté sur plusieurs plateformes, il peut être consommé par Xamarin.Forms les contrôles. Une application courante de l’objet RoundEffect fait circulaire un Image objet. Le code XAML suivant montre l’effet appliqué à une Image instance :

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

L’effet peut également être appliqué dans le code :

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

La RoundEffect classe peut être appliquée à n’importe quel contrôle dérivé de VisualElement.

Remarque

Pour que l’effet calcule le rayon correct, le contrôle auquel il est appliqué doit avoir un dimensionnement explicite. Par conséquent, les HeightRequest propriétés doivent WidthRequest être définies. Si le contrôle affecté apparaît dans un StackLayout, sa HorizontalOptions propriété ne doit pas utiliser l’une des valeurs Expand telles que LayoutOptions.CenterAndExpand ou elle n’aura pas de dimensions précises.