Xamarin.Forms Opakovaně použitelný RoundEffect

Důležité

Už není nutné použít RoundEffect k vykreslení ovládacího prvku jako kruh. Nejnovějším doporučeným přístupem je vystřižovat ovládací prvek pomocí EllipseGeometryovládacího prvku . Další informace najdete v tématu Klipování s geometrií.

RoundEffect zjednodušuje vykreslování jakéhokoli ovládacího prvku, který je odvozený z VisualElement kruhu. Tento efekt lze použít k vytváření cyklických obrázků, tlačítek a dalších ovládacích prvků:

Snímky obrazovky RoundEffect v iOSu a Androidu

Vytvoření sdílené třídy RoutingEffect

Třída efektu musí být vytvořena ve sdíleném projektu, aby se vytvořil efekt pro různé platformy. Ukázková aplikace vytvoří prázdnou RoundEffectRoutingEffect třídu, která je odvozena od třídy:

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

Tato třída umožňuje sdílenému projektu přeložit odkazy na účinek v kódu nebo XAML, ale neposkytuje žádné funkce. Účinek musí mít implementace pro každou platformu.

Implementace efektu Androidu

Projekt platformy Android definuje RoundEffect třídu, která je odvozena od PlatformEffect. Tato třída je označena assembly atributy, které umožňují Xamarin.Forms přeložit třídu efektu:

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

Platforma Android používá koncept definice OutlineProvider okrajů ovládacího prvku. Ukázkový projekt obsahuje CornerRadiusProvider třídu, která je odvozena z ViewOutlineProvider třídy:

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

Tato třída používá Width a Height vlastnosti Xamarin.FormsElement instance k výpočtu poloměru, který je polovinou nejkratší dimenze.

Jakmile je zprostředkovatel osnovy definován, RoundEffect třída ji může využívat k implementaci efektu:

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

Metoda OnAttached je volána, když je efekt připojen k elementu. Existující OutlineProvider objekt je uložen, takže jej lze obnovit při odpojení efektu. Nová instance se CornerRadiusOutlineProvider používá jako OutlineProvider a ClipToOutline je nastavena na hodnotu true pro oříznutí přetečení prvků na ohraničení obrysu.

Metoda OnDetatched je volána při odebrání efektu z elementu a obnoví původní OutlineProvider hodnotu.

Poznámka:

V závislosti na typu Control prvku může nebo nemusí být vlastnost null. Control Pokud vlastnost není null, lze zaoblené rohy použít přímo na ovládací prvek. Pokud však má hodnotu null, musí být u objektu Container použity zaoblené rohy. Pole effectTarget umožňuje použití efektu u příslušného objektu.

Implementace efektu iOS

Projekt platformy iOS definuje RoundEffect třídu, která je odvozena od PlatformEffect. Tato třída je označena assembly atributy, které umožňují Xamarin.Forms přeložit třídu efektu:

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

V iOSu Layer mají ovládací prvky vlastnost, která má CornerRadius vlastnost. Implementace RoundEffect třídy v iOSu vypočítá odpovídající poloměr rohu a aktualizuje vlastnost vrstvy 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;
    }
}

Metoda CalculateRadius vypočítá poloměr na základě minimální dimenze Xamarin.FormsElement. Metoda OnAttached se volá, když je efekt připojen k ovládacímu prvku a aktualizuje vlastnost vrstvy CornerRadius . ClipToBounds Nastaví vlastnost tak, aby true přetečení prvků byly oříznuty na ohraničení ovládacího prvku. Metoda OnDetatched je volána při odebrání efektu z ovládacího prvku a obrátí tyto změny, obnovení původní rohový poloměr.

Poznámka:

V závislosti na typu Control prvku může nebo nemusí být vlastnost null. Control Pokud vlastnost není null, lze zaoblené rohy použít přímo na ovládací prvek. Pokud však má hodnotu null, musí být u objektu Container použity zaoblené rohy. Pole effectTarget umožňuje použití efektu u příslušného objektu.

Využití efektu

Jakmile se efekt implementuje na různých platformách, můžou ho Xamarin.Forms využívat ovládací prvky. Běžnou aplikací objektu RoundEffectImage je kruhový objekt. Následující XAML ukazuje efekt použitý u Image instance:

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

Účinek lze použít také v kódu:

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

Třídu RoundEffect lze použít na jakýkoli ovládací prvek, který je odvozen z VisualElement.

Poznámka:

Aby bylo možné vypočítat správný poloměr, musí mít ovládací prvek, na který se použije explicitní nastavení velikosti. Proto by měly být definovány HeightRequest vlastnosti a WidthRequest vlastnosti. Pokud se ovlivněný ovládací prvek zobrazí v objektu StackLayout, HorizontalOptions jeho vlastnost by neměla používat jednu z hodnot Rozbalit , například LayoutOptions.CenterAndExpand nebo nebude mít přesné rozměry.