Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Xamarin逆引きTips

Xamarin逆引きTips

Xamarin.FormsでWebビューを使用するには?

2015年4月15日

外部のWebページやローカルに配置されたHTMLコンテンツを簡単に表示できるWebViewコントロールをXamarin.Formsで使う方法を説明する。

古谷 誠進(@furuya02
  • このエントリーをはてなブックマークに追加

 Xamarin.Formsで提供されている標準コントロールの一つに、WebViewコントロールがある。WebViewコントロールを使用すると、HTMLコンテンツを簡単に表示できる。なお、他のコントロールと同様に、このコントロールもネイティブなコントロールで構成されており、具体的には各プラットフォーム上のWebブラウザーコントロールとなっている。

 今回は、このWebViewコントロールの利用方法について解説する。

  • *1 なお本Tipsは、Windows上でVisual Studio 2013を使用してXamarin.Forms開発をすることを前提としている(編集部注: Mac上のXamarin Studioでも同様の手順で、本稿の内容が実現できることは確認している)。使用しているXamarin.Formsのバージョンは、プロジェクト作成時に利用されている「1.3.1.6296」である。

1. シナリオ

 WebViewコントロールは、URLを指定してWebページを表示したり、ローカルに配置されたリソースを表示したりできる。

 本Tipsでは最初に、URLを指定して、外部のWebページを表示する方法を解説し、その後、HTMLソース(文字列)を直接指定する方法を説明する。

2. Xamarin.Formsプロジェクトの作成

 メニューバーの[ファイル]-[新規作成]-[プロジェクト]から表示したダイアログで、[テンプレート]-[Visual C#]-[Mobile Apps]-[Blank App (Xamarin.Forms Portable)]を選択し、名前を「WebViewSample」として[OK]ボタンを押す。

図1 「Blank App (Xamarin.Forms Portable)」の新規作成

3. WebViewコントロールの表示: WebページのURLの指定

 WebViewコントロールを表示するには、App.csファイルを以下のように修正する。

C#
using Xamarin.Forms;

namespace WebViewSample {
  public class App : Application {
    public App(){
      MainPage = new MyPage();
    }
    ……省略……
  }

  class MyPage : ContentPage {
    public MyPage() {
      var webView = new WebView { // <-1
        Source = "http://xamarin.com" // <-2
      };
      Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
      Content = webView; // <-3
    }
  }
}
WebViewを表示するコード(App.cs)

 WebViewコントロールを生成して(1)、Xamarin.Formsのページの唯一のコンテンツとして設定している(3)。

 なお、WebViewコントロールのSourceプロパティに、URLを指定*1することで、任意のWebページを表示させることができる(2)。

  • *1 ここでのURL文字列は、UrlWebViewSourceオブジェクトに暗黙的に変換されている。

 このコードを実行すると、次のような画面になる。

図2 WebViewを表示した画面(iOS/Android) 図2 WebViewを表示した画面(iOS/Android)
図2 WebViewを表示した画面(iOS/Android)

4. WebViewコントロールの表示:単一ファイルのHTMLソース指定

 任意のHTMLソースをWebViewコントロールに表示するには、App.csファイルを以下のように修正する。

C#
……省略……
namespace WebViewSample {
  ……省略……

  class MyPage : ContentPage {
    public MyPage() {

      var html = new HtmlWebViewSource { // <-1
        Html = "<html><body><h1>TEST</h1>サンプルページ</body></html>", // <-2
      };
      var webView = new WebView {
        Source = html // <-3
      };

      Padding = new Thickness(0,Device.OnPlatform(20,0,0),0,0);
      Content = webView; 
    }
  }
}
単一ファイルで構成されたHTMLソースのWebページを表示するコード(App.cs)

 最初に、HtmlWebViewSourceクラスのインスタンスを生成する(1)。

 HtmlWebViewSourceクラスには、Htmlというプロパティがあり、ここにHTMLソースをそのまま指定できる(2)。

 WebViewコントロールのSourceプロパティに、今、作成したHtmlWebViewSourceオブジェクトを設定することで、今度は任意のHTMLソースのWebページを表示することになる(3)。

 このコードを実行すると、次のような画面になる。

図3 単一ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android) 図3 単一ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android)
図3 単一ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android)

5. WebViewコントロールの表示:複数ファイルのHTMLソース指定

 通常、HTMLソースは、画像やスタイルシートなどのリンクを含んで複数のファイルで構成されている。例えば先のHTMLソースをスタイルシートや画像などの複数ファイルで構成されたものに変更すると、次のようになる。

C#
……省略……
namespace WebViewSample{
  ……省略……

  class MyPage : ContentPage {
    public MyPage() {

      var html = new HtmlWebViewSource {
        Html = "<html><head><link rel='stylesheet' href='css/my.css'></head><body><h1>TEST</h1><img src='Images/icon.png'>サンプルページ</body></html>", // <-1
      ……省略……
    }
  }
}
複数ファイルで構成されたHTMLソースのWebページを表示するコード(App.cs)

 Htmlプロパティに指定するHTMLソースに、スタイルシートへのリンクと画像ファイルのタグを追加した(1)。

 このコードを実行すると、次のような画面になり、画像ファイルやスタイルシートへのリンクが無効になっているのが分かる。

図4 複数ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android)【未完成】 図4 複数ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android)【未完成】
図4 複数ファイルで構成されたHTMLソースのWebページを表示した画面(iOS/Android)【未完成】

画像へのリンクは、無効になっており、またスタイルシートも適用されていない。

6. 複数ファイルのHTMLソース: 基準ディレクトリ

 通常、HTMLでは、他のファイルへのリンクは、相対パスや絶対パスを使用して指定される。そして、HtmlWebViewSourceクラスには、BaseUrlというプロパティがあり、相対パスを指定する場合の基準ディレクトリを設定できるようになっている*2

  • *2 BaseUrlプロパティに指定できるのは、当該HTMLソースからの相対パスを指定するための基準ディレクトリである。このため、HTMLソース内では“/Images/icon.png”のような絶対パスの指定はできない。

 しかし、基準ディレクトリとなる物理的な場所の指定は、プラットフォームごとに異なっており、残念ながらXamarin.Formsで共通的に記述する方法は提供されていない。このようなときの最も簡単な解決方法は、DependencyServiceを使用する方法である*3。DependencyServiceを使うことで、PCL(Portable Class Library)側ではインターフェースだけを定義して、その実体はプラットフォームごとに用意するのである。

 DependencyServiceを使用してBaseUrlプロパティを設定するには、App.csファイルを以下のように修正する。

C#
……省略……
namespace WebViewSample{
  ……省略……

  public interface IBaseUrl { string Get(); } // <-1

  class MyPage : ContentPage {
    public MyPage() {

      var html = new HtmlWebViewSource {
        Html = "<html><head><link rel='stylesheet' href='css/my.css'></head><body><h1>TEST</h1><img src='Images/icon.png'>サンプルページ</body></html>",
        BaseUrl = DependencyService.Get<IBaseUrl> ().Get(), // <-2

      };

      ……省略……
    }
  }
}
DependencyServiceを使用してBaseUrlプロパティを設定するコード(App.cs)

 1は、DependencyServiceで使用するインターフェースの定義である。任意のディレクトリ名を返すGetメソッドだけが定義されている。

 そして、これを利用して、BaseUrlプロパティに基準ディレクトリを指定している(2)。

7. 複数ファイルのHTMLソース: iOSにおける実装

 iOSでは、NSBundle.MainBundle.BundlePathプロパティでリソースのディレクトリを取得できる。これを使用して、インターフェースの実装を行うには、WebViewSample.iOSプロジェクトにBaseUrl.csファイルを追加して、次のように修正する。

C#
using Foundation;
using WebViewSample.iOS;
using Xamarin.Forms;

[assembly: Dependency(typeof(BaseUrl))] // <-1
namespace WebViewSample.iOS {
  public class BaseUrl : IBaseUrl { // <-2
    public string Get() { 
      return NSBundle.MainBundle.BundlePath + "/Content"; // <-3
    }
  }
}
DependencyServiceのiOS側を実装するコード(WebViewSample.iOS/BaseUrl.cs)

 1は、DependencyServiceの属性指定である。また、定義したクラス(ここではBaseUrlとした)は、PCL側で定義したインターフェースを継承した形で定義されている(2)。これらは、共にDependencyServiceを使用するための定型句である。

 NSBundle.MainBundle.BundlePathプロパティで、リソースディレクトリの物理的なパスを取得できるので、そこに、“/Content”(=図5で自作したフォルダー名)を追加して基準ディレクトリとして返している。

 基準ディレクトリに各ファイル配置した様子は次のようになる。

図5 基準ディレクトリに画像ファイルとスタイルシートを配置した(iOS)
図5 基準ディレクトリに画像ファイルとスタイルシートを配置した(iOS)

「Content」「css」「Images」フォルダーは手動で作成した。icon.pngファイルは、こちらを使用した。

 なお、スタイルシートである、my.cssファイルには、次の内容を定義する。

CSS
body{
  background-color: #0C555D;
  color: #F5F1E9;
}
h1 {
  font-size: 80px;
  color: #FF6860;
}
スタイルシートの内容(my.css)

 このコードを実行すると、次のような画面になり、今度は、画像ファイルやスタイルシートへのリンクも有効になっているのが確認できる。

図6 複数ファイルで構成されたHTMLソースのWebページを表示した画面(iOS)【完成版】
図6 複数ファイルで構成されたHTMLソースのWebページを表示した画面(iOS)【完成版】

8. 複数ファイルのHTMLソース: Androidにおける実装

 Androidでは、ファイル(raw asset files)を格納するために「Assets」フォルダーを使用する。Assetsフォルダーに置かれたファイルは、そのままの形で.apkファイルに含まれ、ファイルシステムにアクセスするのと同様に、「file:///android_asset/」でアクセスできる。

 これを使用して、インターフェースの実装を行うには、WebViewSample.DroidプロジェクトにBaseUrl.csファイルを追加して、次のように修正する。

C#
using WebViewSample.Droid;
using Xamarin.Forms;

[assembly: Dependency(typeof(BaseUrl))] // <-1
namespace WebViewSample.Droid { 
  public class BaseUrl : IBaseUrl { // <-2
    public string Get() {
      return "file:///android_asset/Content/"; // <-3
    }
  }
}
DependencyServiceのAndroid側を実装するコード(WebViewSample.Droid/BaseUrl.cs)

 1および2は、iOSの場合と同様で、DependencyServiceを使用するための定型句である。

 物理的なディレクトリとして、Assetsフォルダーの下に“/Content”を追加して基準ディレクトリとして返している。

 基準ディレクトリに各ファイル配置した様子は次のようになる。

図7 基準ディレクトリに画像ファイルとスタイルシートを配置した(Android)
図7 基準ディレクトリに画像ファイルとスタイルシートを配置した(Android)

iOSで使用した内容(図5)と同じものを配置している。

 このコードを実行すると次のような画面になり、Androidでも、複数ファイルで構成されるHTMLソースのWebページが表示できることが確認できる。

図8 複数ファイルで構成されたHTMLソースのWebページを表示した画面(Android)【完成版】
図8 複数ファイルで構成されたHTMLソースのWebページを表示した画面(Android)【完成版】

9. まとめ

 WebViewコントロールを使用すると、Webページや、用意したHTMLコンテンツを表示するだけなら、非常に簡単に実装できることが分かる(しかし、イベントやメソッドの実装に関しては、逐次、進化してはいるようだが、まだまだ非力であることは否定できない)。

 WebViewコントロールの今後の拡充が楽しみである。

【コラム】iOSにおいても、BaseUrlプロパティは、正常に動作する

 Xamarinの開発者用ドキュメントでは、今回紹介したHtmlWebViewSourceクラスのBaseUrlプロパティが、iOSで正常に動作しないため、レンダラーを書くことで回避するようにアナウンスされている(次のリンク先)。

 確かに、当初、そのようなバグがあったようだが、2015年4月14日現在、この問題は解消されている(図9)。このため、上記リンク先の回避策を講ずる必要はない。

図9 Xamarinの開発者ページのアナウンス
図9 Xamarinの開発者ページのアナウンス

※以下では、本稿の前後を合わせて5回分(第44回~第48回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

Xamarin逆引きTips
44. Xamarin.FormsでListViewのコンテキストアクションを使用するには?

リストの1つをスライド(iOS)もしくは長押し(Android)されたらメニューを表示する「コンテキストアクション」の基本的な使い方を説明する。

Xamarin逆引きTips
45. Xamarin.Formsでトリガーを使用するには?

イベントやプロパティの変化に応じたコントロールの外観の変更をXAMLだけで実装できるトリガーの基本的な使用方法を解説する。

Xamarin逆引きTips
46. 【現在、表示中】≫ Xamarin.FormsでWebビューを使用するには?

外部のWebページやローカルに配置されたHTMLコンテンツを簡単に表示できるWebViewコントロールをXamarin.Formsで使う方法を説明する。

Xamarin逆引きTips
47. MvvmCrossで画面遷移するには?

MvvmCrossでiOS/Androidアプリの画面遷移をするための基本的な実装方法を説明する。

Xamarin逆引きTips
48. Xamarin.Formsでプラットフォームごとの微調整を行うには?

カスタムレンダラーやDependencyServiceの仕組みを使わず、Deviceクラスを利用してプラットフォーム間で異なる部分を微調整する方法を説明する。

サイトからのお知らせ

Twitterでつぶやこう!