kamulog

xamarin.formsのネタなど

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デザインにすることができます。

作成したもの

github.com

nugetでも公開してます

ToFlatButtonの機能はプレリリースの0.1.0-preからです。

www.nuget.org

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とだいたい同じ形状のボタンにすることができます。

f:id:kamusoft:20170419163334p:plain:h400f:id:kamusoft:20170419163336p:plain:h400

パラメータ

  • 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

何をやっているかというと

  1. 影を消す
  2. Backgroundを動的に生成したものにごっそり入れ替える
  3. PropertyChanged対応

だいたいこんな感じです。

影を消す

StateListAnimatorをnullにすれば消えてくれました。 影を消すだけでよければこれで解決です。

NativeButton = Control as AppCompatButton;
if (NativeButton == null)
    return;
//略

//shadow off
NativeButton.StateListAnimator = null;

Backgroundの入れ替え

Ripple対応は以下の記事を参考にしてください。

Xamarin.Forms(Android)でコードだけで任意のViewにRippleEffectを追加する方法 - kamulog

基本的にはデフォルトの定義

あたりを見ながらこれをコードに起こして、それに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);

参考サイト

終わりに

今回からpreにはバグを恐れずにさっさと機能を追加する方向にしました。