Xamarin.Forms Webビュー

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 Webビューとは

Xamarin.Formsで提供されているWebViewを使用すると、URLを指定したり、ローカルに作成したWebページなどを表示することができます。

Xamarin Developers Guide 「Working with WebView」

元々、各プラットフォームには、Webページを表示するブラウザベースのコントロールが用意されています。iOSでは、UIWebView、Androidでは、WebView、そして、WindowsPhoneではWebBrowserがそれにあたります。 そして、Xamarin.Formsでは、これらを共通コードから利用できるようにしています。

2 Webページの表示

WebViewのSourceプロパティにURLを指定することで、Webページを表示することができます。

public class App : Application {
    public App() {
        MainPage = new MyPage();
    }
}

class MyPage : ContentPage {
    public MyPage() {
        // iOS の場合のみ、ステータスバーに重ならないように上に余白をとる
        Padding = new Thickness (0,Device.OnPlatform(20, 0, 0),0,0);
        // ページの唯一のコントロールとしてWebViewを配置する
        Content = new WebView {
            Source = "https://dev.classmethod.jp/" //表示するURLの指定
        };
    }
}
Android iOS Windows Phone
002 003 004

なお、iOS9以降では、ATS対応が必要です。下の図は、iOSプロジェクトのinfo.plist に「ATS を無効にする (非推奨)」という記述を追加した様子です。 ATSの扱いについては、下記をご参照ください。

[iOS 9] iOS 9 で追加された App Transport Security の概要

001

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

3 HTMLの表示

HTMLを表示させたい場合は、SourceプロパティにHtmlWebViewSourceオブジェクトを指定します。

public class App : Application {
    public App() {
        MainPage = new MyPage();
    }
}

class MyPage : ContentPage {
   public MyPage() {
        // iOS の場合のみ、ステータスバーに重ならないように上に余白をとる
        Padding = new Thickness (0,Device.OnPlatform(20, 0, 0),0,0);

        var source = new HtmlWebViewSource {
            Html = "<meta charset='UTF-8'><h1>Developers.IO</h1>AWS/iOS技術者の必読メディア"
        };

        // ページの唯一のコントロールとしてWebViewを配置する
        Content = new WebView {
            Source = source //HTMLを指定
        };
    }
}
Android iOS Windows Phone
005 006 007

HTML内からCSSや画像などの外部ファイルを指定したい場合は、基礎となるURL(プロパティBaseUrl)の指定が必要になります。

ディレクトリ構成は、ターゲットごと違うため、「ターゲット依存コード」としてこれを実装します。

参考:Xamarin.Forms トースト(DependencyService)

まずは、HTMLで、スタイルシートと画像を指定してみます。

public class App : Application {
    public App() {
        MainPage = new MyPage();
    }
}

class MyPage : ContentPage {
    public MyPage() {
        // iOS の場合のみ、ステータスバーに重ならないように上に余白をとる
        Padding = new Thickness (0,Device.OnPlatform(20, 0, 0),0,0);

        var source = new HtmlWebViewSource {
            Html = "<html><head><meta charset='UTF-8'><link rel='stylesheet' href='my.css'></head><body><h1>Developers.IO</h1><img src='Images/classmethod.png'><br>AWS/iOS技術者の必読メディア</body></html>"
        };

        // ページの唯一のコントロールとしてWebViewを配置する
        Content = new WebView {
            Source = source //HTMLを指定
        };
    }
}
body{
        font-size:20px;
        font-family:bold;
        text-shadow: 0 1px 0 #ccc,
               0 2px 0 #c9c9c9,
               0 3px 0 #bbb,
               0 4px 0 #b9b9b9,
               0 5px 0 #aaa,
               0 6px 1px rgba(0,0,0,.1),
               0 0 5px rgba(0,0,0,.1),
               0 1px 3px rgba(0,0,0,.3),
               0 3px 5px rgba(0,0,0,.2),
               0 5px 10px rgba(0,0,0,.25),
               0 10px 10px rgba(0,0,0,.2),
               0 20px 20px rgba(0,0,0,.15);
 }

h1 {
    font-size:50px;
}

当然ですが、スタイルシートと画像は適用されません。

Android iOS Windows Phone
008 009 010

そこで、HtmlWebViewSourceのBaseUrlを設定してみます。作業としては、ディレクトリ取得のメソッドをインターフェースとして定義し、それをDependencyService経由で使用します。

    //共通インターフェース
    public interface IBaseUrl{
        string Get();
    }
var source = new HtmlWebViewSource{
    Html = "   //・・・省略・・・
    
    //基礎となるURLを指定する
    BaseUrl = DependencyService.Get<IBaseUrl>().Get()
};

続いて、ターゲットごとにインターフェースの実装を行います。

(1) Androidの場合

Androidでは、生ファイル (raw asset files) を格納するためにAssetsフォルダを使用します。

Assetsフォルダに保存されたファイルは、そのままの形で.apkファイルに含まれ、ファイルシステムにアクセスするのと同様に、file:///android_asset/でアクセスできます。

012

そこで対象となるファイルを図のように配置して、次のようにインターフェースを実装します。

using App1.Droid;
using Xamarin.Forms;

[assembly: Dependency(typeof(BaseUrl))]
namespace App1.Droid {
    public class BaseUrl : IBaseUrl {
        public string Get() {
            return "file:///android_asset/";
        }
    }
}

スタイルシートと画像のパスが成立して、適用されていることを確認できます。

011

(2) iOSの場合

iOSにおいては、NSBundle.MainBundle.BundlePathから、ルート及びResourceの下にあるファイルにアクセスできます。

今回は、Contentというフォルダを作成しその中にリソースを置いてみました。


014

インターフェースの実装は、次のようになります。

[assembly:Dependency(typeof(BaseUrl))]
namespace App1.iOS {
    class BaseUrl:IBaseUrl {
        public string Get(){
            return NSBundle.MainBundle.BundlePath+"/Content";
        }
    }
}

実行結果は、次のとおりです。当初、バグのためレンダラー拡張によりLoadHtmlStringを修正する必要があるとドキュメントに記載されていたのですが、現在は問題なくなっています。

013

(3) WindowsPhoneの場合

WindowsPhoneの場合、カレントフォルダにそのままアクセスできるので、ファイルの配置及び、インターフェースの実装は、下記のようになります。

016

using App1.WinPhone;
using Xamarin.Forms;

[assembly:Dependency(typeof(BaseUrl))]
namespace App1.WinPhone {
    class BaseUrl : IBaseUrl {
        public string Get() {
            return "";
        }
    }
}

ちょっとスタイルの扱いが違ってるようですが、一応実行できてます。なお、ドキュメントにはWindowsPhone8.1や10では、動作できないような記述があったのですが、WindowsPhone10のシュミレータでは問題を確認できませんでした。

015

4 ナビゲーション及びイベント

ナビゲーションには、次の機能が利用可能です。

GoForward() 進む
GoBack() 戻る
CanGoForward 「進む」ことが可能かどうかのフラグ
CanGoBack 「戻る」ことが可能かどうかのフラグ

また、イベントとしては、次のものが利用可能です。

Navigating ページのロードが開始した時
Navigated ページのロードが完了した時

ナビゲーション及びイベントを処理して、サンプルとして簡単なブラウザを作成してみました。

Android iOS Windows Phone
017 018 019

書いたコードは、下記で全てです。

public class App : Application {
    public App() {
        MainPage = new MyPage();
    }
}

class MyPage : ContentPage {
    public MyPage() {
        // iOS の場合のみ、ステータスバーに重ならないように上に余白をとる
        Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);

        var webView = new WebView() {
            Source = "https://dev.classmethod.jp/", //起動時に表示するURLの指定
            VerticalOptions = LayoutOptions.FillAndExpand // ナビゲーション部分以外は、全部WebViewのエリアとする
        };

        //「戻る」ボタン
        var back = new Button {
            Text = " <<",
            IsEnabled = false // デフォルトで無効
        };
        back.Clicked += (s, e) => { // ボタンが押された時の処理
            webView.GoBack(); // 戻る
        };

        //「進む」ボタン
        var forword = new Button {
            Text = ">>",
            IsEnabled = false // デフォルトで無効
        };
        forword.Clicked += (s, e) => { // ボタンが押された時の処理
            webView.GoForward(); // 進む
        };

        // 検索バー
        var entry = new Entry {
            HorizontalOptions = LayoutOptions.FillAndExpand
        };
        // 検索バーへの入力が完了した時
        entry.Completed += (s, e) => {
            webView.Source = entry.Text;//入力されたURLへジャンプする
        };

        // 上段のナビゲーション部分
        var toolbar = new StackLayout {
            Orientation = StackOrientation.Horizontal,
            Children = {
                back,forword,entry // 「戻る」ボタン、「進む」ボタン、検索バーを横に配置する
            }
        };

        Content = new StackLayout() {
            Children = { toolbar,webView}  // ナビゲーション部分とWebViewを縦に配置する
        };

        // ロード完了時のイベント処理
        webView.Navigated += (s, e) => {
            back.IsEnabled = webView.CanGoBack; // CanGoBack==true の時、「戻る」ボタンを有効にする
            forword.IsEnabled = webView.CanGoForward;// CanGoForward==true の時、「 進む」ボタンを有効にする
        };
    }
}

5 まとめ

今回は、Xamarin.FormsのWebViewについて、その機能を一通り紹介しました。

Xamarin.Formsの誕生当初、このWebViewは、非常に機能が乏しく、いろいろとレンダラー拡張しないと使えないという印象がありましたが、最近では、かなり拡充されてきて、この悪いイメージも払拭されつつあると思います。

6 参考資料


この辺でXamarin導入による 効果と限界をしっかり把握してみよう MVP Community Camp 2015
Xamarin記事一覧(SAPPOROWORKSの覚書)