Xamarin.FormsのAndroidでButtonのBorderやRadiusを有効化しつつFlatにするEffect
Xamarin.FormsのAndroidのButtonはデフォルトでは影があって少し立体的なものが使われるのですが、どうしても平らなボタンが欲しくなったりします。そういう時のためにButtonをFlatにするEffectを作成しました。
またXamarin.FormsのButtonにはBorderWidthやBorderColorやBorderRadiusが存在するのですが、Androidではそれらのプロパティが機能していません。(v2.3.4現在) ということでそれらのプロパティをついでに有効にする機能も入れました。
これを使えばiOSとほぼ同じようなButtonデザインにすることができます。
作成したもの
nugetでも公開してます
ToFlatButtonの機能はプレリリースの0.1.0-preからです。
Install-Package AiForms.Effects -Pre
nuget経由で使う場合はiOSのAppDelegate.csの
global::Xamarin.Forms.Forms.Init();
の後に
AiForms.Effects.iOS.Effects.Init();
の記述が必要です。
概要
AndroidのButtonから影や余分なInsetなどを取り除いてFlatな形状にします。 またAndroidでは機能していないプロパティ(BorderWidth BorderColor BorderRadius)を機能するようにします。 これによりiOSとだいたい同じ形状のボタンにすることができます。
パラメータ
- On
- EffectのOn・Off
- RippleColor
- タップした時のリプルエフェクトの色を指定(デフォルトはRippleなし)
Xaml
<Button Text="ButtonText" ef:ToFlatButton.On="true" ef:ToFlatButton.RippleColor="Red" BorderWidth="4" BorderColor="Green" BorderRadius="10" />
コードについて
AiForms.Effects/ToFlatButtonPlatformEffect.cs at master · muak/AiForms.Effects · GitHub
何をやっているかというと
- 影を消す
- Backgroundを動的に生成したものにごっそり入れ替える
- PropertyChanged対応
だいたいこんな感じです。
影を消す
StateListAnimatorをnullにすれば消えてくれました。 影を消すだけでよければこれで解決です。
NativeButton = Control as AppCompatButton; if (NativeButton == null) return; //略 //shadow off NativeButton.StateListAnimator = null;
Backgroundの入れ替え
Ripple対応は以下の記事を参考にしてください。
Xamarin.Forms(Android)でコードだけで任意のViewにRippleEffectを追加する方法 - kamulog
基本的にはデフォルトの定義
- https://github.com/android/platform_frameworks_base/blob/master/core/res/res/drawable/btn_default_material.xml
- https://github.com/android/platform_frameworks_base/blob/master/core/res/res/drawable/btn_default_mtrl_shape.xml
あたりを見ながらこれをコードに起こして、それにRadiusやBorderの処理を追加しました。 Paddingのコードへの起こし方がわからなかったのでこれは放置しています。
Insetは、これの値が大きいと実際のボタンの領域が思ってたより小さくなって余白ができてしまうのでできれば0にしたかったんですが、0にするとボタンテキストの位置がおかしくなってしまうので、上下に1ずついれるようにして妥協してます。タップ領域を調整したい場合を考えたらこれもプロパティ化しとけば良いんですが、今回は固定にしてます。
Inset = new InsetDrawable(Shape,0,1,0,1); Ripple = new RippleDrawable(getPressedColorSelector(rippleColor.ToArgb()), Inset, null);
全然関係ないんですがContextの拡張メソッドのToPixelsがとても便利で、これを使えばFormsの単位をDeviceにあったPixelによしなに変換してくれます。これとは逆のFromPixelsもあります。
//Forms側の単位を変換してくれるやつ var size = (int)Container.Context.ToPixels(FormsButton.BorderWidth);
参考サイト
- http://qiita.com/pside/items/587dd9f1562d02d2ee2c
- http://qiita.com/guchio/items/bebc13db0d2c0ae03363
終わりに
今回からpreにはバグを恐れずにさっさと機能を追加する方向にしました。