LoginSignup
1
4

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-04-26

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

XAMLで作成

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

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

opacity.png

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

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

では、どうするか?

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

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

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

カスタムUI化

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

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

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

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が見やすくなる。

これが、

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>

こうなる。

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

角丸にする

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

iOS

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

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

参考

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

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

1
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4