shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Formsでリング状にボタンを表示させる

UIのアニメーションについていくつか記事をかきました.

shuhelohelo.hatenablog.com

今回作ったのはボタンを押すと,その周りに複数のボタンが表示される,たまに見るやつです.

f:id:shuhelohelo:20200326201820g:plain

アニメーション自体はScaleTo,FadeTo,TranslateToの3つを組み合わせた簡単なものです.

ソースコード

github.com

Xamarin.Formsで下のレイヤーのコントロールを操作できるようにする

どういうことかというと,画面の表面に透明なレイヤーを設けて,そこにメッセージを重ねるといった表現をしたい場合,上(表)のレイヤーがユーザーの操作を拾うため,下のレイヤーのコントロールを操作できません.

例えば以下のようにテキストボックスやボタンを配置したレイヤー(例えばGridなど)の上に,画像やテキストを重ねて表示させるためのレイヤーを配置した場合,上のレイヤーを透明にして下のレイヤーが見えるようにしても,下のレイヤーのボタンを押したりテキストボックスに入力したりすることができません.

f:id:shuhelohelo:20200321000052p:plain

このとき,ユーザーの操作が上のレイヤーを透過するようにする設定が,コントロールには設けられています.

それがInputTransparentです.

以下のように上のレイヤーとなるGridコントロールInputTransparent="True"を設定すると,ユーザーの操作は上のGridを透過して下のボタンやテキストボックスに届くようになります.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XFInputTransparent.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid>
        <StackLayout>
            <!--  Place new controls here  -->
            <Label
                HorizontalOptions="Center"
                Text="Welcome to Xamarin.Forms!"
                VerticalOptions="CenterAndExpand" />
            <Entry Placeholder="入力できる" VerticalOptions="CenterAndExpand" />
            <Button Text="押せる" VerticalOptions="CenterAndExpand" />
        </StackLayout>
        <Grid
            BackgroundColor="Pink"
            InputTransparent="True"
            Opacity="0.5">
            <Label Text="これはオーバーレイしたGridに表示させているメッセージです" />
        </Grid>
    </Grid>
    
</ContentPage>

今回のコードはこちら.

github.com

C#である値以上またはある値以下に変数の値を維持する

ある変数があって,それは例えばスクロールの位置だったり,増減する数値を格納されるものとする.

で,ある値以上または以下に値が行かないようにしたい,つまり下限や上限を決めたいとする.

このとき,値が変化するたびに発生するイベントを利用し,イベントハンドラで値を制限する処理を行うとする.

この例ではSliderコントロールを使って,0~100の間で変化するスライダーの値を,表示時に20~80に制限する.

if文を使う

        private void Slider_ValueChanged(object sender, ValueChangedEventArgs e)
        {
            double val = e.NewValue;

            //e.NewValueは0~100で変化するが
            //表示させる値としては上限80,下限20に制限する
            if(val>80)
            {
                val = 80;
            }
            if(val<20)
            {
                val = 20;
            }

            SliderValue = val;
        }

Math.Max(),Min()メソッドを使う

        private void Slider_ValueChanged(object sender, ValueChangedEventArgs e)
        {
            double val = e.NewValue;

            //e.NewValueは0~100で変化するが
            //表示させる値としては上限80,下限20に制限する
            val = Math.Max(20, Math.Min(80, val));

            SliderValue = val;
        }

こちらのほうがシンプルに書ける.

実行

f:id:shuhelohelo:20200319104952g:plain

ソースコード

github.com

Xamarin.FormsのLabelは色々できることを知った

docs.microsoft.com

Labelに収まらない文字列を「...」で省略する3種類のTruncationがとても便利.

f:id:shuhelohelo:20200315231212p:plain

        <StackLayout>
            <Frame
                HorizontalOptions="Center"
                VerticalOptions="Center"
                WidthRequest="200">
                <Label Text="何も指定しない場合は,テキストは折り返される." />
            </Frame>
            <Frame
                HorizontalOptions="Center"
                VerticalOptions="Center"
                WidthRequest="200">
                <Label LineBreakMode="TailTruncation" Text="Truncationを指定すると,収まらない分は「...」で省略される." />
            </Frame>
            <Frame
                HorizontalOptions="Center"
                VerticalOptions="Center"
                WidthRequest="200">
                <Label LineBreakMode="HeadTruncation" Text="Truncationを指定すると,収まらない分は「...」で省略される." />
            </Frame>
            <Frame
                HorizontalOptions="Center"
                VerticalOptions="Center"
                WidthRequest="200">
                <Label LineBreakMode="MiddleTruncation" Text="Truncationを指定すると,収まらない分は「...」で省略される." />
            </Frame>
        </StackLayout>

Xamarin.Formsでコントロールの見た目をResourceDictionaryで指定する

CSSと同様に,コントロールの見た目を予め定義し,それを各コントロールに指定することで,見た目を変更することができます.

基本的にはResourceDictionaryにStyleとして定義して使用します.

これによって同じ見た目の定義を1回行うだけで,同じ見た目のコントロールを作成することができ,UI作成,編集の効率が上がります.

例えば,以下のような見た目のラベルを作る場合,このコントロール1つだけであれば以下のようにするでしょう.

f:id:shuhelohelo:20200315115518p:plain

        <Label
            BackgroundColor="LightBlue"
            FontSize="Large"
            HorizontalOptions="CenterAndExpand"
            Text="Label1"
            TextColor="LightPink"
            VerticalOptions="CenterAndExpand" />

しかし,同じ見た目のラベルを複数用意する場合,すべてのラベルに上記の指定を行うのは手間です.

また,見た目を変更する際にすべてのラベルに対して変更を行わなければならず,手間であるとともに変更の見落としも発生しやすくなります.

そこで,以下のようにStyleとして予め定義しておくことで,見た目の指定が容易になります.

<ContentPage
    x:Class="XFStylePractice.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <ContentPage.Resources>
        <ResourceDictionary>
            <Style x:Key="labelStyle" TargetType="Label">
                <Setter Property="BackgroundColor" Value="LightBlue" />
                <Setter Property="FontSize" Value="Large" />
                <Setter Property="HorizontalOptions" Value="CenterAndExpand" />
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
                <Setter Property="TextColor" Value="LightPink" />
            </Style>
        </ResourceDictionary>
    </ContentPage.Resources>

    <StackLayout>
        <Label
            BackgroundColor="LightBlue"
            FontSize="Large"
            HorizontalOptions="CenterAndExpand"
            Text="Label1"
            TextColor="LightPink"
            VerticalOptions="CenterAndExpand" />

        <Label Style="{StaticResource labelStyle}" Text="Label2" />
        <Label Style="{StaticResource labelStyle}" Text="Label3" />
        <Label Style="{StaticResource labelStyle}" Text="Label4" />
        <Label Style="{StaticResource labelStyle}" Text="Label5" />
    </StackLayout>

</ContentPage>

f:id:shuhelohelo:20200315120641p:plain

Style指定の種類

Styleの定義,指定の仕方にはざっくりわけて明示的な指定と暗黙的な指定の2種類があります.

明示的な指定

ResourceDictionaryにStyleとして定義し,そのStyleに名前(x:Key)をつけておき,コントロール側で適用したいStyleを名前で指定します.対象とするコントロールの種類(TargetType)は必須です.

CSSで言えばclass属性でスタイルを指定するのと同じです.

上の例は,この明示的な指定になります.

docs.microsoft.com

暗黙的な指定

ResourceDictionaryにStyleを定義しますが,ここでは対象とするコントロールの種類だけを指定し,名前はつけません.指定したコントロールに自動的にそのStyleが適用されます.

docs.microsoft.com

使い方

明示的な指定

冒頭の例のとおりです.

暗黙的な指定

こちらはResourceDictionary内でのStyleの定義の際にx:Keyを指定しません.

            <Style TargetType="Label">
                <Setter Property="BackgroundColor" Value="LightBlue" />
                <Setter Property="FontSize" Value="Large" />
                <Setter Property="HorizontalOptions" Value="CenterAndExpand" />
                <Setter Property="VerticalOptions" Value="CenterAndExpand" />
                <Setter Property="TextColor" Value="LightPink" />
            </Style>

そして,Labelを配置する際に,明示的な指定とは異なり,適用するStyleを指定しません.

    <StackLayout>
        <Label Text="Label1" />
        <Label Text="Label2" />
        <Label Text="Label3" />
        <Label Text="Label4" />
        <Label Text="Label5" />
    </StackLayout>

明示的な指定はTargetTypeで指定したコントロールの種類に対して一律で適用されます.

f:id:shuhelohelo:20200315120852p:plain

Xamarin.FormsでNavigationPageのナビゲーションバーの背景色や文字色を変更する

Setting the NavigationBar colors in&nbsp;Xamarin.Formstheconfuzedsourcecode.wordpress.com

NavigationPageのBarBackgroundColorBarTextColorというプロパティで変更することができます.

指定の方法は,C#であればApp.xaml.csでNavigationPageをインスタンス化する際に指定する事ができます.

            MainPage = new NavigationPage(new MainPage())
            {
                BarBackgroundColor = Color.Green[f:id:shuhelohelo:20200313181801p:plain],
                BarTextColor = Color.Yellow,
            };

または,Xaml側でResourceDictionaryにStyleを設定します.

        <ResourceDictionary>
            <Style TargetType="NavigationPage">
                <Setter Property="BarBackgroundColor" Value="Green" />
                <Setter Property="BarTextColor" Value="Yellow" />
            </Style>
        </ResourceDictionary>

これで以下のようにナビゲーションバーの背景色,文字色が変更されます.

f:id:shuhelohelo:20200313181801p:plain

Xamarin.FormsでAndroidのステータスバーを半透明にする

追記

この記事はNavigationPageを使う場合には有効だけれども,Shellを使う場合は有効ではない.

ShellのNavigationBarの色を変更するには以下の記事にあるように,Android側のMainActivity.csでWindow.SetStatusBarColor(Android.Graphics.Color.Argb(255, 44, 0, 0));というように色を指定することができるけれど,透明にはできない...

Jamesさんのこの記事が参考になるだろうか...

montemagno.com

追記前

stackoverflow.com

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            global::Xamarin.Forms.FormsMaterial.Init(this, savedInstanceState);

            Window.SetStatusBarColor(Android.Graphics.Color.Argb(0, 44, 0, 0));//これ

            LoadApplication(Startup.Init(ConfigureServices));
        }

forums.xamarin.com

Androidにおいて画面上部の時刻やバッテリー残量などの情報を表示する領域がステータスバーです.

デフォルトではステータスバーは以下のように,透過率0で表示されます.(ステータスバーがわかりやすいようにナビゲーションバーの色を変えてあります)

f:id:shuhelohelo:20200313174132p:plain

しかし,アプリケーションの中にはこのステータスバーが半透明になっているものがあります.

f:id:shuhelohelo:20200313174526g:plain

この記事は,Androidアプリでステータスバーを半透明にする方法です.

AndroidプロジェクトのResources > values > styles.xml内に次の1行を追加します.

<item name="android:windowTranslucentStatus">true</item>

どこに追加するのが正しい作法なのか不勉強でわかっていませんが,私は以下のとおりにして期待どおりの結果になりました.

<?xml version="1.0" encoding="utf-8" ?>
<resources>

  <style name="MainTheme" parent="MainTheme.Base">
    <item name="android:windowTranslucentStatus">true</item>
  </style>

...略

f:id:shuhelohelo:20200313175137p:plain

ナビゲーションバーが表示されている場合は,これで問題ありません.

ナビゲーションバーを非表示のときにステータスバーを半透明にするときは注意が必要です.

まずはナビゲーションバー表示の場合.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XFStatusBar.Views.SecondPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="ナビゲーションバー"
    mc:Ignorable="d">
    <ContentPage.Content>
        <StackLayout>
            <Frame BackgroundColor="Pink">
                <Label Text="Frameだよ" />
            </Frame>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

f:id:shuhelohelo:20200313175834p:plain

このように上から順にステータスバー,ナビゲーションバー,ページのコンテンツが表示されています.

では次にナビゲーションバーを非表示にします.NavigationPage.HasNavigationBar="False"を追加します.

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="XFStatusBar.Views.SecondPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    NavigationPage.HasNavigationBar="False"
    Title="ナビゲーションバー"
    mc:Ignorable="d">
    <ContentPage.Content>
        <StackLayout>
            <Frame BackgroundColor="Pink">
                <Label Text="Frameだよ" />
            </Frame>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

f:id:shuhelohelo:20200313180235p:plain

ナビゲーションバーがなくなった分,ページのコンテンツが上に来ましたが,ご覧のとおりステータスバーとページのコンテンツが重なるように表示されています.

このように,ステータスバーを半透明にし,ナビゲーションバーを非表示にするときはステータスバーとの重なりを考慮してページを作る必要があります.

ソースコード

github.com