ぴよぴよエンジニアの日記

クラウドベンダーに勤める見習いSEの日記です。発言は私自身の見解であり、必ずしも所属組織の立場、戦略、意見を代表するものではありません。

Fall Madly In Love With These 10 Xamarin Charts!

昨晩、Syncfusion の "Fall Madly In Love With These 10 Xamarin Charts!" というWebセミナーがありました.
今回はこのWebセミナーの簡単なまとめです.

f:id:Santea:20170222145257p:plain


Syncfusion は .NET系の様々なコンポートを提供しています.
Xamarin.Traditional/Xamarin.Forms にも対応しています.

www.syncfusion.com
help.syncfusion.com


こちらのアプリでサンプルを見ることができます.

Syncfusion Controls Explorer - Google Play の Android アプリ
Syncfusion Controls Explorer on the App Store


ライセンスは Community license, Flat license の2つがあります.

f:id:Santea:20170222145907p:plain

  • 個人開発者
  • 年間売り上げ100万$以下の企業
  • 使用可能なユーザーは5人まで

上記の条件を満たしていれば「無償」で、「商用アプリも可能」な Community license が適用できます.
そうでない場合は Flat license が適用されます.

Unlimited flat fee license for Syncfusion products

ライセンスに関しての詳しい情報は上記ページをご覧ください.





紹介されていた10個のチャート

f:id:Santea:20170222150936p:plain

1つ目は Rader Chart です.

f:id:Santea:20170222151505p:plain

DrawType を指定で 線、面に描き分けできます.
また、凡例の表示・非表示も設定可能です.




f:id:Santea:20170222151801p:plain

2つ目は Range Area Chart です.

f:id:Santea:20170222152647p:plain

線と面の両方に色の指定が可能です.
また、EnableToolTip=True にすることでタップ時に値を表示できます.




f:id:Santea:20170222153046p:plain

3つ目は Pyramid です.

f:id:Santea:20170222154238p:plain

PyramidType の指定が可能で、Linear と Surface が選択可能.
色も自由にカスタマイズできます.

f:id:Santea:20170222154302p:plain

左が Linear で右が Surface です.
おそらく Linear は領域の高さで値を表しているのに対し、Surface は領域の面積で値を表しているのではないかと思います.




f:id:Santea:20170222154744p:plain

4つ目は Stacked area です.

f:id:Santea:20170222155229p:plain

Serieas ごとに値のバインディングができます!
今回の例ではコードビハインドで ItemSource を指定してそうですが、もちろん ViewModel でのバインドも可能です.
チャートの細かい設定は全部 XAML に追い出せるの、控えめに言って最高だろ!
(ちなみに、似たようなチャートライブラリである OxyPlot for Xamarin.Forms はあまりバインディング効きません...)



f:id:Santea:20170222155803p:plain

5つ目は 100% Stacked Column です.

f:id:Santea:20170222160243p:plain

こちらもバインディング効きます.
控えめに言って(ry




f:id:Santea:20170222160542p:plain

6つ目は Multiple Axis Column です.

f:id:Santea:20170222161241p:plain

Chart の PrimaryAxis(X軸) SecondaryAxis(Y軸) の他に、Series ごとに Axis を設定してあげることで複数軸のチャートが作成できます.
複数のグラフを比較することはよくあることなので、とても実用的ですね.




f:id:Santea:20170222161651p:plain

7つ目は Candle です.
日本語では箱ひげ図が一般的でしょうか?最大値・最小値・四分位数を表すグラフですね.
株取り引きなどでよく使われるグラフだと思います.

f:id:Santea:20170222162128p:plain

こちらもバインディングが効きます.
IsTransposed = True にすると横向きにもできます.




f:id:Santea:20170222162557p:plain

8つ目は Columns です.
一般的な棒グラフですね.

f:id:Santea:20170222163110p:plain
f:id:Santea:20170222163349p:plain

DataMarker に任意のテンプレートを適用できることを紹介していました.
カスタマイズ性が高くて良いですね.




f:id:Santea:20170222163625p:plain

9つ目は Doughnut です.

f:id:Santea:20170222163903p:plain

色々な属性を指定できるようです.
例えば、開始角度などがあるようです.

項目をタップすると少し大きくなったり、凡例がスクロールできたりと、細かいところまでサポートされています.




f:id:Santea:20170222164202p:plain

最後は Spline A です.

f:id:Santea:20170222164436p:plain

f:id:Santea:20170222164710p:plain

こちらもテンプレートの例が紹介されていました.





感想

バインディングが効くの最高!これですね.
Event も Behavior 使えば問題なくバインディングできるのでほぼほぼ XAML で書けるかと.

各チャートのプロパティも色々なものがあり、多彩なグラフを比較的簡単に作れると思います.

また、テンプレートを用いたカスタマイズ性の高さも良いですね.
これは実際に使ってみたいと思いました.

録画もそのうち見られるようになるようなので、興味のある方はぜひご覧いただければと思います.

以上です.

Xamarin.Forms の タブをカスタマイズしてみた

Xamarin.Forms の TabbedRenderer(iOS)/TabbedPageRenderer(Android) をカスタマイズして、デザインを変えてみました.

f:id:Santea:20170221150019p:plain

タブの背景色が選択/非選択で切り替わるデザインです.
Android, iOS どちらとも標準デザインとは外れるのでカスタマイズが必要です.
デザインをO統一するために、Android は画面下端にタブを配置しています.


TabbedPage をカスタマイズするには Android では TabbedPageRenderer(FormsAppCompatActivityを使用時)、iOS では TabbedRenderer を用います.

なんで iOS には TabbedPageRenderer が無いのだろうかと思ったら、AppCompat 用に別途用意されたのが TabbedPageRenderer のようです.





PCLプロジェクト

using System;
using Xamarin.Forms;

namespace FinancialRecommend.Controls
{
	public class CustomTabbedPage : TabbedPage
	{
	}
}

CustomTabbedPage を定義します.

<?xml version="1.0" encoding="UTF-8"?>

<controls:CustomTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
            xmlns:views="clr-namespace:FinancialRecommend.Views;assembly=FinancialRecommend"
            xmlns:controls="clr-namespace:FinancialRecommend.Controls;assembly=FinancialRecommend"
            prism:ViewModelLocator.AutowireViewModel="True"
            x:Class="FinancialRecommend.Views.RootTabbedPage"
			Title="タブ">

	<views:DiagnosticTopPage />
	<views:MediaPage />

</controls:CustomTabbedPage>

XAML での使い方は TabbedPage と同じです.



Androidプロジェクト

Android の TabbedPage は internal でやばいらしいんですが、こちらに裏技的解決法がありました.

Bottom Tabs for Xamarin.Android (in Xamarin.forms app) - Stack Overflow



using System;
using Android.Support.Design.Widget;
using Android.Support.V4.View;
using FinancialRecommend.Controls;
using FinancialRecommend.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android.AppCompat;

[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(CustomTabbedPageRenderer))]
namespace FinancialRecommend.Droid.Renderers
{
	public class CustomTabbedPageRenderer : TabbedPageRenderer
	{
	    protected override void OnLayout(bool changed, int l, int t, int r, int b)
	    {
	        InvertLayoutThroughScale();
	        base.OnLayout(changed, l, t, r, b);
	    }

	    private void InvertLayoutThroughScale()
	    {
	        ViewGroup.ScaleY = -1;

	        TabLayout tabLayout = null;
	        ViewPager viewPager = null;

	        for (var i = 0; i < ChildCount; ++i)
	        {
	            var view = (Android.Views.View)GetChildAt(i);
	            if (view is TabLayout) tabLayout = (TabLayout)view;
	            else if (view is ViewPager) viewPager = (ViewPager)view;
	        }

	        tabLayout.ScaleY = viewPager.ScaleY = -1;
	        tabLayout.GetTabAt(0).SetIcon(Resource.Drawable.image_diag_tab);
	        tabLayout.GetTabAt(1).SetIcon(Resource.Drawable.image_media_tab);
	        viewPager.SetPadding(0, -tabLayout.MeasuredHeight, 0, 0);
	    }
	}
}

参考ページをベースに、最後のアイコンをセットするところだけ追記しています.
これでタブを下端に配置し、アイコンを設定できました.

細かい色の設定などは、xml でやっていきます.


Resource.layout.tabs.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.TabLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/sliding_tabs"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?attr/colorPrimary"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    app:tabIndicatorColor="@android:color/transparent"
    app:tabBackground="@drawable/tab_color_selector"
    app:tabTextAppearance="@style/TabText"
    app:tabGravity="fill"
    app:tabMode="fixed"
    android:elevation="4dp" />

以下のことを設定しています.

  • tabIndicatorColor を透明色にして、タブ選択時に下線が付かないようにする
  • tabBackground に選択/非選択で可変な背景を設定
  • tabTextAppearance にスタイルを適用


Resource.drawable.tab_color_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@color/appBlue" android:state_selected="true"/>
    <item android:drawable="@color/appBlueWhite"/>
</selector>

タブの背景色は selector で変えてあげます.


Resources.values.styles.xml

<resources>
    <style name="TabText" parent="TextAppearance.Design.Tab">
        <item name="android:textSize">12sp</item>
    </style>
</resources>

テキストサイズをここで設定します.


以上が Android の実装です.



iOSプロジェクト

iOSAndroid と違い Renderer だけで実装しています.
(xib に追い出せるならそれもそれでいいんですが、やり方あるんですかね?)

using System;
using System.Linq;
using CoreGraphics;
using FinancialRecommend.Controls;
using FinancialRecommend.iOS.Renderers;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(CustomTabbedPage), typeof(CustomTabbedPageRenderer))]

namespace FinancialRecommend.iOS.Renderers
{
    public class CustomTabbedPageRenderer : TabbedRenderer
    {
        public override void ViewWillAppear(bool animated)
        {
            base.ViewWillAppear(animated);

            var numberOfItems = TabBar.Items.Count();
            var tabBarItemSize = new CGSize()
            {
                Width = TabBar.Frame.Width / numberOfItems,
                Height = TabBar.Frame.Height
            };

            TabBar.SelectionIndicatorImage
                = ImageWithColor(
                        new UIColor(red: (nfloat) 0.02, green: (nfloat) 0.18, blue: (nfloat) 0.45, alpha: (nfloat) 1.0),
                        tabBarItemSize)
                    .CreateResizableImage(UIEdgeInsets.Zero);

            TabBar.Frame = new CGRect()
            {
                X = TabBar.Frame.X - 2,
                Y = TabBar.Frame.Y,
                Width = View.Frame.Width + 4,
                Height = TabBar.Frame.Height
            };

            TabBar.TintColor = UIColor.White;
            TabBar.BarTintColor = new UIColor(red: (nfloat) 0.58, green: (nfloat) 0.69, blue: (nfloat) 0.77,
                alpha: (nfloat) 1.0);

            var fontFamily = UIFont.SystemFontOfSize(10);
            var attributes = new UITextAttributes() {Font = fontFamily, TextColor = UIColor.White};
            UITabBarItem.Appearance.SetTitleTextAttributes(attributes, UIControlState.Normal);
            var assets = new string[] {"image_diag_tab", "image_media_tab.png"};

            TabBar.Items[0].Image = UIImage.FromBundle(assets[0])
                .ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
            TabBar.Items[1].Image = UIImage.FromBundle(assets[1])
                .ImageWithRenderingMode(UIImageRenderingMode.AlwaysOriginal);
        }

        private UIImage ImageWithColor(UIColor color, CGSize size)
        {
            var rect = new CGRect {X = 0, Y = 0, Width = size.Width, Height = size.Height};
            UIGraphics.BeginImageContextWithOptions(size, false, 0);
            color.SetFill();
            UIGraphics.RectFill(rect);
            var image = UIGraphics.GetImageFromCurrentImageContext();
            UIGraphics.EndImageContext();
            return image;
        }
    }
}

iOS はタブ選択時の背景色を設定する項目が無いので、色を付けた UIImage を TabBar.SelectionIndicatorImage に設定することで背景色を変えています.

この辺はネイティブのお作法だと思いますが一つハマったのが、これらの一連の処理を ViewWillLoad に書くとクラッシュします.

xamarin.ios - How do I override the Xamarin Forms TabbedPage item fonts for iOS? - Stack Overflow

ネイティブと Xamarin.Forms のライフサイクルの微妙な違いのせいだと思います.
気付くのにだいぶ掛かりました(スタックトレース読んでもよくわからなかった;).



まとめ

今回は Android/iOS の TabbedPage をカスタマイズしてみました.
Android は上手く xml を使ってあげると楽かなと思います.

Renderer を使うと細かいところまでカスタマイズできますが、ネイティブの微妙に違う場合に注意ですね.


以上です.



参考