Xamarin.Forms
カスタムレンダラー
サーチ…
ListViewのカスタムレンダラー
カスタムレンダラーを使用すると、開発者は各プラットフォームのXamarin.Formsコントロールの外観と動作をカスタマイズできます。開発者はネイティブコントロールの機能を使用できます。
たとえば、 ListView
スクロールを無効にする必要があります。 iOS上ですべてのアイテムが画面に配置されていてもListView
をスクロールすることはできません。 Xamarin.Forms.ListView
はそのような設定を管理しません。この場合、レンダラーが助けに来ています。
まず、必要なバインド可能なプロパティを宣言するカスタムコントロールをPCLプロジェクトで作成する必要があります。
public class SuperListView : ListView
{
public static readonly BindableProperty IsScrollingEnableProperty =
BindableProperty.Create(nameof(IsScrollingEnable),
typeof(bool),
typeof(SuperListView),
true);
public bool IsScrollingEnable
{
get { return (bool)GetValue(IsScrollingEnableProperty); }
set { SetValue(IsScrollingEnableProperty, value); }
}
}
次のステップでは、各プラットフォームのレンダラーを作成します。
iOS:
[assembly: ExportRenderer(typeof(SuperListView), typeof(SuperListViewRenderer))]
namespace SuperForms.iOS.Renderers
{
public class SuperListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var superListView = Element as SuperListView;
if (superListView == null)
return;
Control.ScrollEnabled = superListView.IsScrollingEnable;
}
}
}
アンドロイド(Androidのリストには、すべてのアイテムが画面に配置されている場合はスクロールしないので、スクロールを無効にすることはできませんが、ネイティブプロパティを使用することはできます)。
[assembly: ExportRenderer(typeof(SuperListView), typeof(SuperListViewRenderer))]
namespace SuperForms.Droid.Renderers
{
public class SuperListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
var superListView = Element as SuperListView;
if (superListView == null)
return;
}
}
}
レンダラーのElement
プロパティは、PCLプロジェクトのSuperListView
コントロールです。
レンダラーのControl
プロパティはネイティブコントロールです。 Android.Widget.ListView
AndroidとのためUIKit.UITableView
iOS用。
XAML
どのように使用するのでしょうか?
<ContentPage x:Name="Page"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:SuperForms.Controls;assembly=SuperForms.Controls"
x:Class="SuperForms.Samples.SuperListViewPage">
<controls:SuperListView ItemsSource="{Binding Items, Source={x:Reference Page}}"
IsScrollingEnable="false"
Margin="20">
<controls:SuperListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding .}"/>
</ViewCell>
</DataTemplate>
</controls:SuperListView.ItemTemplate>
</controls:SuperListView>
</ContentPage>
ページの.cs
ファイル:
public partial class SuperListViewPage : ContentPage
{
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged();
}
}
public SuperListViewPage()
{
var list = new SuperListView();
InitializeComponent();
var items = new List<string>(10);
for (int i = 1; i <= 10; i++)
{
items.Add($"Item {i}");
}
Items = new ObservableCollection<string>(items);
}
}
BoxViewのカスタムレンダラー
カスタムレンダラのヘルプを使用すると、新しいプロパティを追加して、ネイティブプラットフォームで別の方法でレンダリングすることができます。ネイティブプラットフォームでは、共有コードを使用することはできません。この例では、半径と影をボックスビューに追加します。
まず、必要なバインド可能なプロパティを宣言するカスタムコントロールをPCLプロジェクトで作成する必要があります。
namespace Mobile.Controls
{
public class ExtendedBoxView : BoxView
{
/// <summary>
/// Respresents the background color of the button.
/// </summary>
public static readonly BindableProperty BorderRadiusProperty = BindableProperty.Create<ExtendedBoxView, double>(p => p.BorderRadius, 0);
public double BorderRadius
{
get { return (double)GetValue(BorderRadiusProperty); }
set { SetValue(BorderRadiusProperty, value); }
}
public static readonly BindableProperty StrokeProperty =
BindableProperty.Create<ExtendedBoxView, Color>(p => p.Stroke, Color.Transparent);
public Color Stroke
{
get { return (Color)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public static readonly BindableProperty StrokeThicknessProperty =
BindableProperty.Create<ExtendedBoxView, double>(p => p.StrokeThickness, 0);
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
}
}
次のステップでは、各プラットフォームのレンダラーを作成します。
iOS:
[assembly: ExportRenderer(typeof(ExtendedBoxView), typeof(ExtendedBoxViewRenderer))]
namespace Mobile.iOS.Renderers
{
public class ExtendedBoxViewRenderer : VisualElementRenderer<BoxView>
{
public ExtendedBoxViewRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
Layer.MasksToBounds = true;
Layer.CornerRadius = (float)((ExtendedBoxView)this.Element).BorderRadius / 2.0f;
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedBoxView.BorderRadiusProperty.PropertyName)
{
SetNeedsDisplay();
}
}
public override void Draw(CGRect rect)
{
ExtendedBoxView roundedBoxView = (ExtendedBoxView)this.Element;
using (var context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(roundedBoxView.Color.ToCGColor());
context.SetStrokeColor(roundedBoxView.Stroke.ToCGColor());
context.SetLineWidth((float)roundedBoxView.StrokeThickness);
var rCorner = this.Bounds.Inset((int)roundedBoxView.StrokeThickness / 2, (int)roundedBoxView.StrokeThickness / 2);
nfloat radius = (nfloat)roundedBoxView.BorderRadius;
radius = (nfloat)Math.Max(0, Math.Min(radius, Math.Max(rCorner.Height / 2, rCorner.Width / 2)));
var path = CGPath.FromRoundedRect(rCorner, radius, radius);
context.AddPath(path);
context.DrawPath(CGPathDrawingMode.FillStroke);
}
}
}
}
繰り返しますが、描画メソッド内で必要に応じてカスタマイズすることもできます。
アンドロイドでも同じです:
[assembly: ExportRenderer(typeof(ExtendedBoxView), typeof(ExtendedBoxViewRenderer))]
namespace Mobile.Droid
{
/// <summary>
///
/// </summary>
public class ExtendedBoxViewRenderer : VisualElementRenderer<BoxView>
{
/// <summary>
///
/// </summary>
public ExtendedBoxViewRenderer()
{
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedBoxView.BorderRadiusProperty.PropertyName)
{
Invalidate();
}
}
/// <summary>
///
/// </summary>
/// <param name="canvas"></param>
public override void Draw(Canvas canvas)
{
var box = Element as ExtendedBoxView;
base.Draw(canvas);
Paint myPaint = new Paint();
myPaint.SetStyle(Paint.Style.Stroke);
myPaint.StrokeWidth = (float)box.StrokeThickness;
myPaint.SetARGB(convertTo255ScaleColor(box.Color.A), convertTo255ScaleColor(box.Color.R), convertTo255ScaleColor(box.Color.G), convertTo255ScaleColor(box.Color.B));
myPaint.SetShadowLayer(20, 0, 5, Android.Graphics.Color.Argb(100, 0, 0, 0));
SetLayerType(Android.Views.LayerType.Software, myPaint);
var number = (float)box.StrokeThickness / 2;
RectF rectF = new RectF(
number, // left
number, // top
canvas.Width - number, // right
canvas.Height - number // bottom
);
var radius = (float)box.BorderRadius;
canvas.DrawRoundRect(rectF, radius, radius, myPaint);
}
/// <summary>
///
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
private int convertTo255ScaleColor(double color)
{
return (int) Math.Ceiling(color * 255);
}
}
}
XAML:
最初に定義した名前空間で、私たちのコントロールを参照します。
xmlns:Controls="clr-namespace:Mobile.Controls"
次に、コントロールを次のように使用し、最初に定義されたプロパティを使用します。
<Controls:ExtendedBoxView
x:Name="search_boxview"
Color="#444"
BorderRadius="5"
HorizontalOptions="CenterAndExpand"
/>
ネイティブプロジェクトからレンダラーにアクセスする
var renderer = Platform.GetRenderer(visualElement);
if (renderer == null)
{
renderer = Platform.CreateRenderer(visualElement);
Platform.SetRenderer(visualElement, renderer);
}
DoSomeThingWithRender(render); // now you can do whatever you want with render
Frame(PCL&iOSパーツ)用のカスタムレンダラーを備えた丸いラベル
最初のステップ:PCLパート
using Xamarin.Forms;
namespace ProjectNamespace
{
public class ExtendedFrame : Frame
{
/// <summary>
/// The corner radius property.
/// </summary>
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create("CornerRadius", typeof(double), typeof(ExtendedFrame), 0.0);
/// <summary>
/// Gets or sets the corner radius.
/// </summary>
public double CornerRadius
{
get { return (double)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
}
第2ステップ:iOSパート
using ProjectNamespace;
using ProjectNamespace.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ExtendedFrame), typeof(ExtendedFrameRenderer))]
namespace ProjectNamespace.iOS
{
public class ExtendedFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (Element != null)
{
Layer.MasksToBounds = true;
Layer.CornerRadius = (float)(Element as ExtendedFrame).CornerRadius;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedFrame.CornerRadiusProperty.PropertyName)
{
Layer.CornerRadius = (float)(Element as ExtendedFrame).CornerRadius;
}
}
}
}
3番目の手順:ExtendedFrameを呼び出すXAMLコード
XAMLの部分でそれを使用したい場合、これを書くことを忘れないでください:
xmlns:controls="clr-namespace:ProjectNamespace;assembly:ProjectNamespace"
後
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
これで、ExtendedFrameを以下のように使用することができます:
<controls:ExtendedFrame
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="Gray"
CornerRadius="35.0">
<Frame.Content>
<Label
Text="MyText"
TextColor="Blue"/>
</Frame.Content>
</controls:ExtendedFrame>
選択可能な背景色を持つ丸みを帯びたBoxView
最初のステップ:PCLパート
public class RoundedBoxView : BoxView
{
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create("CornerRadius", typeof(double), typeof(RoundedEntry), default(double));
public double CornerRadius
{
get
{
return (double)GetValue(CornerRadiusProperty);
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
public static readonly BindableProperty FillColorProperty =
BindableProperty.Create("FillColor", typeof(string), typeof(RoundedEntry), default(string));
public string FillColor
{
get
{
return (string) GetValue(FillColorProperty);
}
set
{
SetValue(FillColorProperty, value);
}
}
}
第二段階:ドロイド部分
[assembly: ExportRenderer(typeof(RoundedBoxView), typeof(RoundedBoxViewRenderer))]
namespace MyNamespace.Droid
{
public class RoundedBoxViewRenderer : VisualElementRenderer<BoxView>
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
SetWillNotDraw(false);
Invalidate();
}
public override void Draw(Canvas canvas)
{
var box = Element as RoundedBoxView;
var rect = new Rect();
var paint = new Paint
{
Color = Xamarin.Forms.Color.FromHex(box.FillColor).ToAndroid(),
AntiAlias = true,
};
GetDrawingRect(rect);
var radius = (float)(rect.Width() / box.Width * box.CornerRadius);
canvas.DrawRoundRect(new RectF(rect), radius, radius, paint);
}
}
}
第3段階:iOSの部分
[assembly: ExportRenderer(typeof(RoundedBoxView), typeof(RoundedBoxViewRenderer))]
namespace MyNamespace.iOS
{
public class RoundedBoxViewRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element != null)
{
Layer.CornerRadius = (float)(Element as RoundedBoxView).CornerRadius;
Layer.BackgroundColor = Color.FromHex((Element as RoundedBoxView).FillColor).ToCGColor();
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Element != null)
{
Layer.CornerRadius = (float)(Element as RoundedBoxView).CornerRadius;
Layer.BackgroundColor = (Element as RoundedBoxView).FillColor.ToCGColor();
}
}
}
}