ASUS ZenFone 4s MAXでXamarin.Androidの実機デバッグができない場合のトラブルシューティング
ASUS ZenFoneでXamarin.Androidアプリの実機デバッグをする際、「Mono Shared Runtimeのインストールがブロックされて失敗する」というのは既知の問題があります。
Can't deploy on device (Android MarshMallow) — Xamarin Forums
ZenFone 2や3では「Auto-start Manager」アプリのブロッキングを無効化すれば良いらしいです。
ZenFone 4sでも同様の問題が発生しますが、「Auto-start Manager」アプリが無くなっているので解決手順が異なります。
- 「モバイルマネージャー」アプリを開く。
- 「PowerMaster」を開く。
- 「節電オプション」を開く。
- 「自動軌道によりアプリを自動拒否する」をオフにする。
これで OK!
Visual Studio 2017で作成したXamarin.FormsプロジェクトをVisual Studio 2015でビルドする
さて、現時点のVisual Studio 2017(ver. 15.6.5)でXamarin.Formsプロジェクトを新規作成すると結構新しくてイケイケなプロジェクトをはいてくれます。
- packages.configファイルが無い
- 共通コードが.NET Standard 2.0(またはShared project)
これをVisual Studio 2015で開くと...
プロジェクトファイルが読み込めず、ビルドできません。
理由は...
- .NET Standard、ましてや2.0なんて対応してない。
- packages.configに代わり、
PackageReference
というフォーマットが使われている。VS 2015(というかMSBuild 14では読めない)
逆マイグレーション
VS 2015でビルドできるようにするために
- .NET StandardプロジェクトをPCLプロジェクトで作りなおす。
- iOSやAndroidのcsprojファイルから
PackageReference
要素を削る。(参照していたパッケージをメモしておく) - packagesフォルダの中身を削除する(だいたいslnファイルと同じ階層にあるやつ)
- 空のpackages.configファイルを追加する(必要ないかも)
PackageReference
で参照していたパッケージをNuGetパッケージマネージャで追加しなおす。- アセンブリの参照方法が変わって通らなくなったコードが出た場合は修正する。
まとめ
特別な理由がない限りVS 2015を投げ捨てましょう。そろそろ限界です。
Xamarin.Forms VisualStateManager Support
Xamarin.Forms 3.0.0からVisualStateManager、およびVisualStateが追加されます。
VisualStateManager.GoToState()
を呼ぶと対応するVisualStateのSetterが適用される機能です。
これにより、Viewの状態に対応する見た目を宣言的に扱うことが可能となります。
基本的な使い方
基本的な使い方を見てみましょう。入力された文字数に応じてEntryの背景色を変える、新規パスワード入力欄風のサンプルです。
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="VisualStateManagerSample.BasicSample"> <StackLayout VerticalOptions="Center" Padding="20"> <Label Text="enter new password" /> <Entry x:Name="_entry" IsPassword="true"> <VisualStateManager.VisualStateGroups> <VisualStateGroupList> <VisualStateGroup x:Name="LengthStates"> <VisualState x:Name="None" /> <VisualState x:Name="Short"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Red" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Medium"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Yellow" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Long"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Green" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </VisualStateManager.VisualStateGroups> </Entry> </StackLayout> </ContentPage>
VisualStateManager.VisualStateGroups
AttachedPropertyを通じてVisualState
をセットします。
VisualStateGroups
はStyleからセットすることが可能なので、VisualStateを含めてViewの規定のスタイルを作ることができます。
using System; using System.Collections.Generic; using Xamarin.Forms; namespace VisualStateManagerSample { [Xamarin.Forms.Xaml.XamlCompilation(Xamarin.Forms.Xaml.XamlCompilationOptions.Compile)] public partial class BasicSample : ContentPage { public BasicSample() { InitializeComponent(); VisualStateManager.GoToState(_entry, "None"); _entry.TextChanged += (s, e) => { if (string.IsNullOrEmpty(_entry.Text)) { VisualStateManager.GoToState(_entry, "None"); return; } var length = _entry.Text.Length; if (length <= 4) { VisualStateManager.GoToState(_entry, "Short"); return; } if (4 < length && length < 8) { VisualStateManager.GoToState(_entry, "Medium"); return; } if (length >= 8) { VisualStateManager.GoToState(_entry, "Long"); return; } }; } } }
コードビハインドではEntryのテキストが更新されるたびに、VisualStateManager.GoToState()
で長さに応じたVisualStateに遷移しています。
同じViusalStateに遷移した場合はSetter適用がスキップされるため、パフォーマンスを気にして厳密に管理する必要はありません。
このサンプル実行結果はこんな感じ。
VisualStateGroup
VisualState
はVisualStateGroup
にまとめられた中で排他的に遷移します。
<VisualStateGroupList> <VisualStateGroup x:Name="Gourp1"> <VisualState x:Name="On" /> <VisualState x:Name="Off" /> </VisualStateGroup> <VisualStateGroup x:Name="Gourp2"> <VisualState x:Name="None" /> <VisualState x:Name="Hightlited" /> </VisualStateGroup> </VisualStateGroupList>
上のようにVisualStateが設定されたViewに対して次の順にGoToSatateを実行した場合...
VisualStateManager.GoToState(view, "On")
VisualStateManager.GoToState(view, "Highlited")
VisualStateManager.GoToState(view, "Off")
結果は次のようになります。
VisualStateManager.GoToState(view, "On")
- Group1/OnのSetterが適用される。
- Group2のSetterは何も適用されない。
VisualStateManager.GoToState(view, "Highlited")
- Group2/HightlitedのSetterが適用される。
- Group1/OnのSetterは解除されない。
VisualStateManager.GoToState(view, "Off")
- Group1/OnのSetterが解除される。
- Group1/OffのSetterが適用される。
- Group2/HightlitedのSetterは解除されず、適用されたまま。
「Setterが適用されていない初期状態に戻す」ということがしたい場合は、Setterを持たないVisualStateを用意しておくと良いです。
Style、Trigger、VisualStateの比較
Xamarin.FormsにはSetter
クラスの集まりによってVisualElementのプロパティを方法として、Style
やTrigger
が以前から存在します。
これらとVisualStateにはどのような違いがあるでしょう。
(便宜上Setter
の塊をカタカナ表記でスタイル呼ぶことにします)
Style
- 特定のクラスに対して規定のスタイルを設定できる。
- 他のStyleを引き継ぐことができる。(
BasedOn
)
Trigger
- イベントの発火やプロパティの変化を契機にVisualElementのプロパティをセットする。
- EnterActoins、ExitActionsを実行できる。
VisualState
- プロパティの設定値をVisualStateという纏まった単位で管理できる。
- VisualStateを変更する判定処理とVisualStateによって変更されるスタイルが分離できる。
状態遷移の判定処理とスタイルの分離がVisualStateの大きな特徴と言えるでしょう。
例えば独自のViewを定義する場合、取りうるVisualStateの名前と条件を公開しておけば、利用者は自分のアプリに合ったスタイルを自由に設定することができます。
規定のVisualState
実は、全てのViewは暗黙的にNomal
、Disabled
、Focused
の3つのVisualStateに遷移します。
それぞれのVisualStateへ遷移するタイミングは以下の通り。
- Nomal
VisualStateManager.VisualStateGroups
AttachedPropertyが更新されたとき。- Viewの
IsEnabled
プロパティがtrueに変更されたとき。 - Viewの
IsFocused
プロパティがfalseに変更されたとき。
- Disabled
- Viewの
IsEnabled
プロパティがfalseに変更されたとき。
- Viewの
- Focused
- Viewの
IsFocused
プロパティがtrueに変更されたとき。
- Viewの
(フォーカス中にIsEnabled = false
したらマズいような……、最終的にDisabledになるのかNormalになるのか、Rendereの実装に依存する?)
本当にVisualStateが変更されているか確かめてみしょう。
こんな感じのXAMLを書きます、自分ではVisualStateManager.GoToState()
しません。
<?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:VisualStateManagerSample" x:Class="VisualStateManagerSample.CommonStatesSamplePage"> <ContentPage.Resources> <!-- Normal <-> Disabled 確認用 for Image --> <Style TargetType="Image" x:Key="StatusIconStyle"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="Source" Value="{local:ImageResource VisualStateManagerSample.Images.online.png}"/> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="Source" Value="{local:ImageResource VisualStateManagerSample.Images.offline.png}"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> <!-- Normal <-> Disabled 確認用 for Label --> <Style TargetType="Label" x:Key="StatusCaptionStyle"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="TextColor" Value="Green"/> </VisualState.Setters> </VisualState> <VisualState x:Name="Disabled"> <VisualState.Setters> <Setter Property="TextColor" Value="Gray"/> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> <!-- Normal <-> Focused 確認用 for Entry --> <Style TargetType="Entry" x:Key="EntryStyle"> <Setter Property="VisualStateManager.VisualStateGroups"> <VisualStateGroupList> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Green" /> </VisualState.Setters> </VisualState> <VisualState x:Name="Focused"> <VisualState.Setters> <Setter Property="BackgroundColor" Value="Orange" /> </VisualState.Setters> </VisualState> </VisualStateGroup> </VisualStateGroupList> </Setter> </Style> </ContentPage.Resources> <StackLayout Padding="20" Spacing="30"> <Label Text="CommonStates Sample" /> <!-- SwitchでImageとLabelのIsEnabledを切り替える --> <Grid ColumnSpacing="20"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Style="{DynamicResource StatusIconStyle}" HeightRequest="60" WidthRequest="60" IsEnabled="{Binding IsToggled, Source={x:Reference _onlineSwitch}}" /> <Label Grid.Row="0" Grid.Column="1" Style="{DynamicResource StatusCaptionStyle}" Text="@ticktackmobile" VerticalTextAlignment="End" IsEnabled="{Binding IsToggled, Source={x:Reference _onlineSwitch}}" /> <Switch x:Name="_onlineSwitch" Grid.Row="1" Grid.Column="1" IsToggled="true" /> </Grid> <Entry x:Name="_entry" Style="{DynamicResource EntryStyle}" Placeholder="IsFocused: true = Orange, false = Green" /> </StackLayout> </ContentPage>
local:ImageResource
はEmbeddedResourceから画像を読み込む
ためのXAMLマークアップ拡張です。(Guideに載ってるやつ)
実行結果がこちら。
Xamarin.FormsのVisualStateManagerで規定のVisualStateを確認するサンプル。 pic.twitter.com/ZJ6saTlTp8
— ざまりん.ふぉーむずマン🚲 (@ticktackmobile) March 22, 2018
Xamarin.Forms 3.0.0-pre2ではバグでIsFocusedを変更したときのVisualStateが逆になってます。(修正取り込み済み)
意図しないVisualStateの遷移を防ぐためNomal
、Disabled
、Focused
を使用しないのが無難ですかねー。
Xamarin.Forms CSS Support
Xamarin.Forms 3.0からCSS Supportが追加されます。(ASP .NET辺りの人たちにリーチしていく狙いらしい?)
一部制限はあるものの大体の機能がそのまま使えるようです、よってこの記事ではXamarin.Forms固有の事情を中心に説明します。 詳しく知りたい人は元のPull Requestを読んでください。
セレクタ関係
要素の指定
CSSにおけるHTML要素名の指定はC#クラス名の指定に置き換えられます。大文字小文字は無視されます。
/* CSS */ boxview { background-color: pink; }
BoxView
クラスの背景色がピンクになります。
StyleClass
CSSにおけるclass属性にはStyleClass
プロパティが対応します。(代わりにclass
プロパティでも良いです)
/* CSS */ .centerelement { background-color: red; }
<!-- XAML --> <StackLayout> <BoxView /> <BoxView StyleClass="centerelement" /> <BoxView /> </StackLayout>
StyleId
CSSにおけるid属性にはStyleId
プロパティが対応します。
/* CSS */ #speciallabel { height: 100; width: 100; background-color: blue; }
<!-- XAML --> <Label Text="I'm special!" StyleId="speciallabel" />
Xamarin.Forms固有のセレクタ
要素名(C#クラス名)の先頭に^
を付けるとそのクラス、およびその派生クラスへの指定となります。
/* CSS */ ^contentpage { padding: 20; background-color: orange; }
この場合、ContentPage
クラスとその派生クラスへの指定となります。通常Xamarin.Formsの各画面はContentPageの派生クラスとして作るため、このようにすれば汎用的なスタイルしてができます。
スタイルシートの読み込み方
XAML
ResourcesプロパティにStyleSheet要素を追加すると、そのViewと子要素にスタイルシートが適用されます。
<ContentPage x:Class="CssSample.CssSamplePage"> <ContentPage.Resources> <StyleSheet Source="/CSS/style.css" /> </ContentPage.Resources> </ContentPage>
ファイルからスタイルシートを読み込む場合、StyleSheet要素のSourceにCSSファイルのパスを記述します。このCSSファイルはEmbeddedResourceにする必要があります。
Sourceに指定するUriにはそのコントロールからの相対パス、または/
で始まるプロジェクトルートからの絶対パスが使用できます。
この画像の場合では../CSS/style.css
または/CSS/style.css
が有効となります。
<ContentPage x:Class="CssSample.CssSamplePage"> <ContentPage.Resources> <StyleSheet> <![CDATA[ ^contentpage { padding: 20; background-color: orange; } stacklayout > boxview { margin: 3; background-color: pink; } ]]> </StyleSheet> </ContentPage.Resources> </ContentPage>
CDATAセクションを使ってXAML内に記述することもできます。
C#
Xamarin.Forms.StyleSheets.StyleSheet.FromAssemblyResource()
を使うとEmbeddedResourceからCSSファイルを読み込めます。
StyleSheet styleSheet = StyleSheet.FromAssemblyResource(this.GetType().GetTypeInfo().Assembly, "CssSample.CSS.style.css"); Resources.Add(styleSheet);
StyleSheet.FromReader()
でTextReaderから読み込むことも可能。
using (var reader = new StringReader(cssString)) myPage.Resources.Add(StyleSheet.FromReader(reader));
CSSとFlexLayout
以前に紹介したFlexLayoutを使うとCSSでもレイアウト指定ができます。
/* CSS */ .section-title { color: #778; font-style: bold; font-size: 20; margin: 6 0 0 6; } .grid { flex-wrap: wrap; flex-direction: row; align-items: stretch; height: 180; } .grid-cell { flex-grow: 0; background-color: #eed; text-align: center; font-size: small; color: #556; } .full { flex-basis: 100%; } .half { flex-basis: 50%; background-color: cornflowerblue; } .third { flex-basis: 33.33%; background-color: pink; } .fourth { flex-basis: 25%; background-color: lightblue; } .auto { flex-grow: 1; background-color: coral; }
<!-- XAML --> <?xml version="1.0" encoding="UTF-8"?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="CssSample.FlexCssSamplePage"> <ContentPage.Resources> <StyleSheet Source="../CSS/flexstyle.css" /> </ContentPage.Resources> <ScrollView> <StackLayout> <Label Text="Basic Grids" StyleClass="section-title" /> <FlexLayout StyleClass="grid"> <Label StyleClass="grid-cell, full" Text="full" /> <Label StyleClass="grid-cell, half" Text="1/2" /> <Label StyleClass="grid-cell, half" Text="1/2" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, half" Text="1/2" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, third" Text="1/3" /> <Label StyleClass="grid-cell, third" Text="1/3" /> <Label StyleClass="grid-cell, third" Text="1/3" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, fourth" Text="1/4" /> <Label StyleClass="grid-cell, third" Text="1/3" /> <Label StyleClass="grid-cell, auto" Text="left-orver" /> </FlexLayout> </StackLayout> </ScrollView> </ContentPage>
上のスタイルシートとXAMLでこんなレイアウトが組めます。
Xamarin.AndroidでFindViewByIdを省略するやつ
が追加されるらしいです。(Kotlin Android Extensionsでできるアレ)
Slides from my presentation today. https://t.co/Pg8rjY9MKR pic.twitter.com/Pe46x3Yp1b
— Miguel de Icaza (@migueldeicaza) 2018年3月6日
(Miguelの資料より引用)
仕組みとしてはXamarin.Formsで行われている 「.xmalファイルが変更された際にmsbuildタスクが走り、x:Name
の付けられたView要素に対応するフィールドをコード生成する」 と同様なようです。
ちなみにmiguelのAndroidのスライドで説明されているやつの実体はこのPRにあるやつで、使い方はここに書いてある通りです https://t.co/WYodNPXObT たぶんここまで分かるやつおらんやろ
— Atsushi Eno (@atsushieno) 2018年3月6日
ファイルテンプレートも一緒に更新されると思いますが、xamlと違って必ずしもコードビハインドのファイルが1つに定まるわけではないので厄介そう🤔
Xamarin.Forms.FlexLayout
いつの間にかXamarin.FormsのmasterブランチにFlexLayoutが生えていたので試してみました。
いつの間にかXamarin.Flexなるディレクトリが生えてるhttps://t.co/Gk1qspwOy9
— ざまりん.ふぉーむずマン🚲 (@ticktackmobile) February 15, 2018
というかFlexLayoutが生えてるhttps://t.co/I7JW4HcbYS
— ざまりん.ふぉーむずマン🚲 (@ticktackmobile) February 15, 2018
ロードマップでは Q1 2018 Pre-Release - Q2 2018 Stable として予定されている機能です。もうすぐver.2.6.0-preとしてNuGetで普通にダウンロードできるようになると思います。
What is FlexLayout ?
端的に言うとCSSにおけるFlexboxのXamarin.Forms版がFlexLayout
です。
Xamarin.Forms標準ではこ要素を折り返して並べてくれるようなLayuoutが無いので、それだけの用途でも便利そうですね。
Xamarin.Forms.FlexLayoutちょっと使ってみた pic.twitter.com/LGgwlrWkEz
— ざまりん.ふぉーむずマン🚲 (@ticktackmobile) 2018年2月18日
内部的には
- xamarin/flexを移植した
Xamarin.Flex
名前空間 Xamarin.Flex.Item
を利用して子要素をレイアウトするFlexLayout
、レイアウト指定用のEnumやTypeConverter
といった構成になっています。
FlexLayoutのレイアウト指定
FlexLayoutがCSSに置けるFlexコンテナ、FlexLayoutの子要素がFlexアイテムに相当します。
Flexコンテナに設定するプロパティ
CSSでFlexコンテナに設定できるプロパティは、Xamarin.FormsではFlexLayouクラスのプロパティとして実装されています。
Flexbox(CSS) | FlaxLayout(XF) |
---|---|
flex-direction | Direction |
flex-wrap | Wrap |
justify-content | JustifyContent |
align-items | AlignItems |
align-content | AlignContent |
対応なし | Position |
Position
はCSSのFlexboxと異なる部分、Relative
かAbsolute
を指定できます。
- Relative:FlexLayoutのルールで配置される位置や大きさが決まる。
- Absolute:FlexアイテムのLeft,Top,Right,Bottomによって位置と大きさが決まる。(ようなんですが、それらを設定する手段が無いように見える...)
Flexアイテムに設定するプロパティ
CSSでFlexアイテムに設定できるプロパティは、Xamarin.FormsではFlexLayouクラスのAttachedPropertyとして実装されています。
Flexbox(CSS) | FlaxLayout(XF) |
---|---|
order | Order |
flex-grow | Grow |
flex-shrink | Shrink |
align-self | AlignSelf |
flex-basis | Basis |
参考
JXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました
2017/5/27(土)に開催されたJXUGC #23 Xamarin 無料化一周年記念勉強会!で喋ってきました。
Xamarin.Formsがさらにオープンになる変更がリリースノートでも特に言及される気配がないのでちょっと解説しました。
Xamarin.Forms 2.3.5(現時点ではプレビュー版)以降をターゲットにすれば、誰でも勝手に Xamarin.Forms.Platform.WinForms をリリースできるようになるステキな変更ですよ?