Xamarin.Formsで背景のみ透過するカスタムUI


（追記）
これはバッドノウハウです。
sh_akiraさんが簡単な方法をコメントしてくれました。
BackgroundColorをアルファ付きで指定できるなんて...>_<;

## XAMLで作成

普通にOpactiyを設定すると、ラベルの文字も透過されてしまって見づらい。

```xml:XAML
<ContentView BackgroundColor="White" Opacity="0.2" Padding="10">
	<Label Text="Label" />
</ContentView>
```

![opacity.png](https://qiita-image-store.s3.amazonaws.com/0/169798/cd1baa9d-8728-94e9-6dfd-c9e5329279f3.png "opacity.png")


文字は不透明で、背景色のみ半透明にしたい。

Labelの前に半透明のViewをねじ込めば良い感じがするが、
ContentViewは子要素をひとつしか持てない...

では、どうするか？

Gridのひとつのセルに複数の要素を設定すると、重なって表示される挙動を利用する。

```xml:XAML
<Grid>
	<BoxView Grid.Row="0" Grid.Column="0" BackgroundColor="White" Opacity="0.2" />
	<Label   Grid.Row="0" Grid.Column="0" Text="Label" Margin="10" VerticalOptions="Center"/>
</Grid>
```

![opacity2.png](https://qiita-image-store.s3.amazonaws.com/0/169798/aa5fc7d3-0adb-934b-e9e7-031d8d924061.png "opacity2.png")

これでLabelだけ不透明になった。

## カスタムUI化

できれば、これをひとつの要素としてXAMLに記述したい。
こんな感じで。

```xml:XAML
<local:MyLabel Text="Opacity Label" />
```


ContentViewを継承したクラスを定義して、XAMLと同等の内容をC#のコードで記載する。
ついでにTextプロパティも追加しておく。

```csharp:MyView.cs
using System;

using Xamarin.Forms;

namespace SampleApp
{
	public class MyLabel : ContentView
	{
		public static readonly BindableProperty TextProperty = BindableProperty.Create(
			"Text", typeof(string), typeof(MyLabel), "Label"
		);

		public string Text {
			get { return (string)GetValue(TextProperty); }
			set { SetValue(TextProperty, value); }
		}

		protected Label _label;

		public MyLabel()
		{
			_label = new Label {
				TextColor = Color.Black,
				Margin = 10,
				VerticalOptions = LayoutOptions.Center
			};

			BoxView box = new BoxView {
				BackgroundColor = Color.White,
				Opacity = 0.2f
			};

			Grid grid = new Grid();
			grid.Children.Add(box    ,0 ,0);
			grid.Children.Add(_label ,0 ,0);

			Content = grid;
		}

		protected override void OnPropertyChanged(string propertyName = null)
		{
			base.OnPropertyChanged(propertyName);

			if (propertyName == TextProperty.PropertyName)
				_label.Text = Text;
		}
	}
}
```

すると、XAMLで作ったのと同じUIが生成される。
1行で記述できるのでラク＆XAMLが見やすくなる。

これが、

```xml:XAML
<Grid>
	<BoxView Grid.Row="0" Grid.Column="0" BackgroundColor="White" Opacity="0.2" />
	<Label   Grid.Row="0" Grid.Column="0" Text="Opacity Label" Margin="10" VerticalOptions="Center"/>
</Grid>
```

こうなる。

```xml:XAML
<local:MyLabel Text="Opacity Label" />
```


## 角丸にする

共通コード(PCL)では角丸にできないので、カスタムレンダラーを使って各プラットフォームでネイティブ実装する。

### iOS

```cs:MyLabelRenderer.cs
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

using CoreGraphics;

using SampleApp;
using SampleApp.iOS;

[assembly: ExportRenderer(typeof(MyLabel), typeof(MyLabelRenderer))]

namespace SampleApp.iOS
{
	public class MyLabelRenderer : VisualElementRenderer<MyLabel>
	{
		public override void Draw(CGRect rect)
		{
			base.Draw(rect);
			Layer.CornerRadius = 5.0f;
			ClipsToBounds = true;
		}
	}
}
```

### Android

```cs:MyLabelRenderer.cs
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

using Android.Util;
using Android.Graphics;

using SampleApp;
using SampleApp.Droid;

[assembly: ExportRenderer(typeof(MyLabel), typeof(MyLabelRenderer))]

namespace SampleApp.Droid
{
	public class MyLabelRenderer : VisualElementRenderer<MyLabel>
	{
		protected float _radius;
		protected Path _path;

		public MyViewRenderer()
		{
			SetWillNotDraw(false);
		}

		protected override void OnElementChanged(ElementChangedEventArgs<MyLabel> e)
		{
			base.OnElementChanged(e);

			if (e.NewElement != null) {
				_radius = TypedValue.ApplyDimension(ComplexUnitType.Dip, 5, Context.Resources.DisplayMetrics);
			}
		}

		protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
		{
			base.OnSizeChanged(w, h, oldw, oldh);

			RectF bounds = new RectF(0, 0, w, h);
			_path = new Path();
			_path.Reset();
			_path.AddRoundRect(bounds, _radius, _radius, Path.Direction.Cw);
			_path.Close();
		}

		public override void Draw(Canvas canvas)
		{
			canvas.Save();
			canvas.ClipPath(_path);

			base.Draw(canvas);

			canvas.Restore();
		}
	}
}
```

## 完成

背景だけが透過した角丸ラベルができた！

![opacity3.png](https://qiita-image-store.s3.amazonaws.com/0/169798/497fa70d-4ca7-b66e-b8c3-9900ccb68c43.png "opacity3.png")

## 参考

Androidで角丸を実装するにあたり、こちらを
パクり
参考にしました。

Xamarin Forms – Rounded Corners Content View – QiMata Technologies
http://www.qimata.com/?p=7813