Xamarin.Forms の タブをカスタマイズしてみた
Xamarin.Forms の TabbedRenderer(iOS)/TabbedPageRenderer(Android) をカスタマイズして、デザインを変えてみました.
タブの背景色が選択/非選択で切り替わるデザインです.Android, iOS どちらとも標準デザインとは外れるのでカスタマイズが必要です.デザインをO統一するために、Android は画面下端にタブを配置しています.
TabbedPage をカスタマイズするには Android では TabbedPageRenderer(FormsAppCompatActivityを使用時)、iOS では TabbedRenderer を用います.
なんで iOS には TabbedPageRenderer が無いのだろうかと思ったら、AppCompat 用に別途用意されたのが TabbedPageRenderer のようです.
@Santea3173 もともとはどっちもTabbedRendererだったのがAppCompat用にTabbedPageRendererが増えたんだったと思う。
— かむ (@muak_x) 2017年2月20日
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プロジェクト
iOS は Android と違い 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 を使うと細かいところまでカスタマイズできますが、ネイティブの微妙に違う場合に注意ですね.
以上です.
参考
ディスカッション
コメント一覧
まだ、コメントがありません