rksoftware

Visual Studio とか C# とかが好きです

Xamarin Forms の ImageButton を試す

Xamarin.Forms に ImageButton というコントロールが追加されたので少し見てみました。
使い方は公式ドキュメントがあります。(機械翻訳がすごいですが)

■ これまでの方法との比較

ImageButton として(Button の様に)扱う方法はこれまでにもいくつかありました。

  • ButtonImage プロパティに画像を設定する
  • ImageTapGestureTapped イベントを使う

これらの方法と比べて ImageButton がどうななるのか見比べてみます。

単純に並べてみる

ImageButton という名前でソリューションを作成しました。
上から、

  • ButtonImage を設定。テキスト付
  • ButtonImage を設定。テキストなし
  • ImageButton
  • ImageTapGesture を設定

です。

<?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:ImageButton"
             x:Class="ImageButton.MainPage">
    <StackLayout VerticalOptions="Center">
        <Button Image="RedBull.png" Text="ボタン" HeightRequest="100"
                Clicked="OnTap"/>
        <Button Image="RedBull.png" HeightRequest="100"
                Clicked="OnTap"/>
        <ImageButton Source="RedBull.png" HeightRequest="100"
                     Clicked="OnTap"/>
        <Image Source="RedBull.png" HeightRequest="100">
            <Image.GestureRecognizers>
                <TapGestureRecognizer
                    Tapped="OnTap"/>
            </Image.GestureRecognizers>
        </Image>
    </StackLayout>
</ContentPage>


ButtonImage を設定するパターンは正しいのですが扱いが難しそうです。ImageButton はそれっぽいですね。

[ 補足 ] 画像ファイルの追加

画像ファイルは、Android プロジェクトでは、Resources/Drawable フォルダーに、iOS プロジェクトでは Resources フォルダーに追加します。

■ Aspect プロパティ

ImageButton には ButtonImage にはない Aspect プロパティがあります。

<?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:ImageButton"
             x:Class="ImageButton.MainPage">
    <StackLayout VerticalOptions="Center">
        <Button Image="RedBull.png" Text="ボタン" HeightRequest="100"
                Clicked="OnTap"/>
        <Button Image="RedBull.png" HeightRequest="100"
                Clicked="OnTap"/>
        <ImageButton Source="RedBull.png" HeightRequest="100"
                     Aspect="Fill"
                     Clicked="OnTap"/>
        <Image Source="RedBull.png" HeightRequest="100">
            <Image.GestureRecognizers>
                <TapGestureRecognizer
                    Tapped="OnTap"/>
            </Image.GestureRecognizers>
        </Image>
    </StackLayout>
</ContentPage>


Aspect="Fill" だと、画像の縦横の比率を維持せず領域いっぱいに表示するということなので、設定どおりの表示ですね。このあたりも ButtonImage プロパティよりも扱いやすそうです。

■ 横幅も指定

ButtonImage プロパティが扱いが難しそうなのは、画像の縦横と Button の縦横が合っていないからでは? という疑問もあるかもしれません。横幅も指定して試してみましょう。

<?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:ImageButton"
             x:Class="ImageButton.MainPage">
    <StackLayout VerticalOptions="Center">
        <Button Image="RedBull.png" Text="ボタン" HeightRequest="100"
                WidthRequest="100"
                HorizontalOptions="Center"
                Clicked="OnTap"/>
        <Button Image="RedBull.png" HeightRequest="100"
                WidthRequest="100"
                HorizontalOptions="Center"
                Clicked="OnTap"/>
        <ImageButton Source="RedBull.png" HeightRequest="100"
                     WidthRequest="100"
                     HorizontalOptions="Center"
                     Clicked="OnTap"/>
        <Image Source="RedBull.png" HeightRequest="100">
            <Image.GestureRecognizers>
                <TapGestureRecognizer
                    Tapped="OnTap"/>
            </Image.GestureRecognizers>
        </Image>
    </StackLayout>
</ContentPage>


やはり正しいのですが難しい感じです。

とりあえず、これから作るアプリであればこれまでのやり方は捨てて ImageButton を使っておくのが良さそうです。

■ [ おまけ ] 画像を共通化する

Xamarin.Forms では画像は、プラットフォームごとのプロジェクトのリソースを使用します。
これは、正しい仕様で確かにそうあるべきです。しかし現実には画像ファイルも共通化したいという要求も多いものです。方法はあります。
特に ImageButton だけの話でもないですが、ついでに書いておきます。
※.NET Standard 方式でソリューションを作成しているものとします。

コード

IMarkupExtension インタフェースを実装した ImageResourceExtension クラスを作ります。

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ImageButton
{
    // 共通プロジェクトのリソース画像を表示のためのマークアップ拡張
    [ContentProperty("Source")]
    public class ImageResourceExtension : IMarkupExtension
    {
        // リソースファイルのパスを取得または設定する
        public string Source { get; set; }

        // Source で指定されたリソース画像を取得する
        public object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Source == null)
                return null;
            // リソースから指定のパスの画像を読み込む
            var imageSource = ImageSource.FromResource(Source);

            return imageSource;
        }
    }
}

Source プロパティに ImageButton Source="{local:ImageResource ImageButton.RedBull.png}" の様に設定します。

<?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:ImageButton"
             x:Class="ImageButton.MainPage">
    <StackLayout VerticalOptions="Center">
        <Button Image="RedBull.png" Text="ボタン" HeightRequest="100"
                WidthRequest="100"
                HorizontalOptions="Center"
                Clicked="OnTap"/>
        <Button Image="RedBull.png" HeightRequest="100"
                WidthRequest="100"
                HorizontalOptions="Center"
                Clicked="OnTap"/>
        <ImageButton Source="{local:ImageResource ImageButton.RedBull.png}"
                     HeightRequest="100"
                     WidthRequest="100"
                     HorizontalOptions="Center"
                     Clicked="OnTap"/>
        <Image Source="RedBull.png" HeightRequest="100">
            <Image.GestureRecognizers>
                <TapGestureRecognizer
                    Tapped="OnTap"/>
            </Image.GestureRecognizers>
        </Image>
    </StackLayout>
</ContentPage>

共通部分のプロジェクトにリソースとして画像ファイルを追加します。この際、ビルドアクションを EmbeddedResource に設定する必要があります。忘れやすいので注意してください。

今回は、違いが分かるように画像に「Core」という文字を書き加えたものを追加しました。

実行するとこのように、共通部分のリソース画像が使われます。