予測: クラウド

マルチプラットフォーム Windows Azure ストレージ

Joseph Fultz

コード サンプルのダウンロード

Joseph FultzWindows Azure ストレージは、すばらしいことに、デバイスにはまったく縛られないテクノロジです。今月はこれを示すために、Windows Phone 7、jQuery、および Android という 3 つのモバイル プラットフォームでの開発を見ていきます。

そのため、プラットフォームごとにシンプルなアプリケーションを作成しましょう。作成するアプリケーションでは、1 回の REST 呼び出しで、あらかじめ決めた Windows Azure ストレージ コンテナーから画像の一覧を取得し、フィルムストリップにサムネイルを表示し、そこから選択した画像をバランスを取って画面に表示します (図 1 参照)。

Side-by-Side Storage Image Viewers

図 1 ストレージの画像ビューアーの比較

ストレージ コンテナーを準備する

まず、Windows Azure ストレージ アカウントと、アップロードに使用するツールのプライマリ アクセス キーが必要です。これは、開発しているクライアントからのアクセスを保護する場合にも必要になります。この情報は、Windows Azure Platform Management Portal で確認できます。

アップロードする画像は、インターネットと自分のコンピューターからいくつかランダムに選びました。今回は、アップロードのコードを作成する代わりに、Windows Azure Storage Explorer (azurestorageexplorer.codeplex.com (英語)) を使用します。理由は後で説明しますが、画像のサイズは約 1 MB 未満にする必要があります。実際にこのコードを使用する場合は、512 KB 未満に抑えることをお勧めします。Filecabinet というコンテナーを作成し、このコンテナーに画像をアップロードすれば、Windows Azure についての準備は完了です。

プラットフォームのパラダイム

プラットフォームにはそれぞれ一定の構造、機能、および制約があります。Silverlight クライアントと Android クライアントの場合は、入力データのマーシャリングを行い、UI ではオブジェクトのコレクションを使用するというおなじみのモデルを採用しました。jQuery にはテンプレートのサポートがありますが、今回は jQuery を使用して直接 XML を取得して必要な HTML を生成する方が簡単で、jQuery の実装がかなりフラットになるとわかりました。jQuery のフローについては詳しく説明しませんが、他の 2 つのサンプルについては、もう少し背景を説明しましょう。

Windows Phone 7 と Silverlight

Silverlight 開発に精通していれば、Visual Studio 2010 で新しい Windows Phone 7 アプリケーション プロジェクトを作成することは問題ないでしょう。詳しくなければ、今回のサンプルでは、識別可能なコレクション (bit.ly/18sPUF、英語)、一般的な XAML コントロール (StackPanel、ListBox など)、および WebClient について理解しておくことをお勧めします。

起動時に、アプリケーションのメイン ページで Windows Azure ストレージに対して REST 呼び出しを行います。設計上、この呼び出しは非同期です。Windows Phone 7 はこのパラダイムを強制し、アプリケーションがデバイスをブロックおよびロックしないようにします。この呼び出しで取得したデータをデータ コンテキストに配置します。このデータは、ObservableCollection<> 型になります。

このようにすると、コレクション内のデータを更新する際に、コンテナーが通知を受けて変更を取得でき、明示的に最新状態に更新する必要がありません。UI を複雑にすると、Silverlight 自体も非常に複雑になることがあるため、今回のようにかなりシンプルな処理には、この方法がちょっとした防止策にもなります。UI でのタッチと物理特性のサポート向けにバインドとサービス呼び出しの組み込みサポートが追加されているので、ビューをズームするためのフィルムストリップは、データがバインドされるグリッドを備えた ASP.NET ページを作成するだけの簡単な作業です。

Android の Activity

Android は独自のプラットフォーム パラダイムを導入しています。さいわい、このパラダイムは突飛なものでも、わかりにくいものでもありません。.NET を主に扱っている開発者であれば、このパラダイムを自身が使い慣れた構造、用語、およびテクノロジに簡単に対応付けることができます。Android では、ユーザーに関するほぼすべての処理を Activity オブジェクトとして表します。Activity は、アプリケーションを作成するために組み合わせる 4 つの基本要素 (Activity、Service、BroadcastReceiver、および ContentProvider) の 1 つです。

わかりやすくするため、ここではすべてのコードを Activity に含めています。現実的な実装では、Windows Azure ストレージから画像を取得して操作するコードは、Service に実装することになります。

このサンプルは、どちらかと言えばフォームの分離コードからデータベース アクセスを行う形式に似ています。Windows を基準にすると、Activity とは、状態の保存と復元を行う機能 ("必要性") がある Windows フォームと考えることができます。Activity には Windows フォームによく似たライフサイクルがあります。1 つのアプリケーションは複数の Activity から構成することもできますが、ユーザーが操作する Activity は一度に 1 つだけです。Android アプリケーションの基礎の詳細については、bit.ly/d3c7C を参照してください。

このサンプル アプリケーションは、1 つの Activity (bit.ly/GmWui、英語)、1 つの BaseAdapter (bit.ly/g0J2Qx、英語)、および画像に関する情報を保持する 1 つのカスタム オブジェクトから構成します。

UI を作成する

それぞれのパラダイムの UI に共通の処理としては、REST 呼び出し、有効なデータ型への戻り値のマーシャリング、バインドと表示、クリック イベントの処理による画像のズーム ビューの表示などがあります。まず、REST GET から返されるデータと、データをもっと使いやすくするために各サンプルに必要な処理について説明します。

データ

アプリケーション コード内では、オブジェクトを操作するようにし、絶えず XML を操作しなければならない状態を避けます。そこで、ストレージ呼び出しから返される XML に対応するオブジェクトを作成します。このオブジェクトは図 2 のコードのようになります。

図 2 ストレージ呼び出しから返される XML に対応するオブジェクト

<EnumerationResults ContainerName="http://jofultz.blob.core.windows.net/filecabinet">

  <Blobs>

  <Blob>

  <Name>2010-12-12+10.40.06.jpg</Name> 

  <Url>http://jofultz.blob.core.windows.net/filecabinet/2010-12-12+10.40.06.jpg</Url> 

  <LastModified>Sun, 02 Jan 2011 02:24:24 GMT</LastModified> 

  <Etag>0x8CD783B4E56116C</Etag> 

  <Size>263506</Size> 

  <ContentType>image/jpeg</ContentType> 

  <ContentEncoding /> 

  <ContentLanguage /> 

  </Blob>

  <Blob>

  <Name>cookie-monster.jpg</Name> 

  <Url>http://jofultz.blob.core.windows.net/filecabinet/cookie-monster.jpg</Url> 

  <LastModified>Mon, 27 Dec 2010 09:40:18 GMT</LastModified> 

  <Etag>0x8CD73C13542256C</Etag> 

  <Size>265995</Size> 

  <ContentType>image/jpeg</ContentType> 

  <ContentEncoding /> 

  <ContentLanguage /> 

  </Blob>

  <Blob>

次に、希望する情報を保持する代表的なオブジェクトを作成します。このオブジェクトについては主に Name フィールドと URL フィールドに注目します。図 3 に、各サンプルで使用するオブジェクトの宣言を示します。

図 3 各クライアントのサンプルで使用するオブジェクトの宣言

Silverlight Android jQuery
public static ObservableCollection<StorageImage>
  ImageInfoList {get;set;}
 
...
 
public class StorageImage
{
  public string Name{get;set;}
  public string Url {get;set;}
  public BitmapImage Image {get;set;}
}
public ArrayList<BlobInfo>
  BlobList= null;
 
...
 
public class BlobInfo {
  public String Name;
  public String Url;
  public Bitmap ImageBitmap;}

なし

XML を直接使用

UI の作成

さいわい、Android と Silverlight の UI 定義方法はマークアップの使い方が似ているため、一方を理解すればもう一方もほぼ確実に理解できます。また、どちらも UI の定義に XML マークアップを使用します。

jQuery の実装では、単に HTML を使用し、一部の要素は動的に生成します。3 つのプラットフォームの大きな違いは、使用するコントロールと要素をマークアップが認識するタイミングです。

まず、Windows Phone 7 の XAML の場合は、MainPage.xaml を開いて <Grid/> を追加します。このグリッドには、フィルムストリップ用に <ListBox /> を配置します。このリストボックスには、<ItemTemplate/> と <DataTemplate /> を配置し、データをフィルムストリップにレンダリングする方法を制御します。グリッドには、画像のズーム ビュー用に <Image/> コントロールも配置します。

Android の場合、Gallery と ImageView の 2 つのウィジェットを含む LinearLayout を使用して、同様の UI 構造を設定します。Silverlight または Windows Presentation Foundation (WPF) に精通していれば、LinearLayout は StackPanel のようなものとお考えください。ウィジェットとはコントロールのことで、今回の場合は、複数の画像を水平に並べた ListBox と Image コントロールの 2 つの要素から成る StackPanel にほぼ相当します。これは、Silverlight のサンプルで使用している構造とほぼ同じです。Gallery は、サムネイルのフィルムストリップ形式のビューを提供し、ImageView は選択したサムネイルをズームしたビューを表示します。

jQuery のサンプルの場合、空の <p/> を配置し、"output" という名前を割り当てます。この要素には、jQuery を通じてサムネイルのビューが動的に追加されます。

Silverlight のマークアップは、マークアップ内でバインドとレンダリングを扱う必要があるため、少し長くなります。Android の場合は、データの読み込みを扱うコードが少し増加します。それぞれのマークアップを図 4 に示します。

図 4 マークアップの違い

要素 Silverlight Android jQuery
フィルムストリップ <ListBox /> <Gallery /> <p />
ズーム ビュー <Image /> <ImageView /> <img />

マークアップの初期状態: Silverlight

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  <ListBox Name="ImageList" ItemsSource="{Binding}" MaxWidth="400"
    Background="#00C23838" Margin="28,16,28,577" BorderBrush="#32D4BA29"
    ScrollViewer.HorizontalScrollBarVisibility="Auto"
    SelectionChanged="ImageList_SelectionChanged">
    <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
        <StackPanel Orientation="Horizontal">
        </StackPanel>              
      </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
    <ListBox.ItemTemplate>
      <DataTemplate>
        <StackPanel Orientation="Horizontal" >
          <Image Source="{Binding Image}" Height="110" Width="150"
            Name="{Binding Name}" ></Image>
        </StackPanel>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
  <Image Height="447" HorizontalAlignment="Left" Margin="28,123,0,0"
    Name="ImageViewer" Stretch="Fill" VerticalAlignment="Top" Width="400" />
  </Grid>
</Grid>

マークアップの初期状態: Android

<LinearLayout android:id="@+id/LinearLayout01"android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical">
              <Gallery xmlns:android="http://schemas.android.com/apk/res/android"
 
              android:id="@+id/Thumbnails" android:layout_width="fill_parent"
 
              android:layout_height="wrap_content" />
              <ImageView android:id="@+id/ZoomView"
 
              android:layout_width="wrap_content" android:layout_height="wrap_content"/>
</LinearLayout>

マークアップの初期状態: jQuery

<p id="output">
 
</p>
<p>
  <img id="zoomview" height="480" width="320"  />
</p>

メイン コード

次は、Windows Azure ストレージに BLOB の情報を問い合わせるコードを作成します。取得要求は、"http://<ストレージ アカウント名>.blob.core.windows.net/<コンテナー名>?comp=list" の形式なので、今回の要求は "http://jofultz.blob.core.windows.net/filecabinet?comp=list" となります。

結果が XML 形式になるため、Silverlight では LINQ を使用して適切なオブジェクト グラフとアクセス パターンを提供します。Java でも同様の機能があれば良かったのですが、結局は、慣れないテクノロジのデバッグを省略するために、DocumentBuilder API で提供されるドキュメント オブジェクト モデル (DOM) 形式のインターフェイスで作業することにしました。これは、わずかな要素に対する読み取り専用の操作になるため、比較的簡単でした。jQuery の場合は、単なるセレクター形式のクエリで、基本的には DOM を操作するだけです。

図 5 に示す各プラットフォームの取得メソッドはきわめて単純です。コンテナーをパブリックにしているため、プロキシや特殊な認証を処理する必要がありません。

図 5 取得メソッド

Silverlight

WebClient StorageClient = new WebClient();
          StorageClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(GetBlobsCompleted);
          StorageClient.DownloadStringAsync(new Uri(AZURE_STORAGE_URL, UriKind.Absolute));

Android

public InputStream GetBlobs()        {
         HttpURLConnection uc;
         InputStream returnStream=null;
try{
URL u = new URL(AZURE_STORAGE_URL);
uc = (HttpURLConnection) u.openConnection(); //u.openConnection(proxy);
uc.setRequestProperty("Accept", "*/*");
uc.setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
uc.setRequestProperty("Accept-Language", "en-us,en;q=0.5");
uc.setRequestProperty("Keep-Alive", "300");    
uc.setRequestProperty("ucection", "keep-alive");   
returnStream = uc.getInputStream();

jQuery

$(document).ready(function () {
  $.ajax({
    type: "GET",
    url: "http://jofultz.blob.core.windows.net/filecabinet?comp=list",
    dataType: "xml",
    success: RenderData
  });
});

Silverlight でも jQuery でも、応答を処理するコールバックを用意します。これは、要求を非同期に行い、応答をコールバックに渡して処理するためです。Android の場合は、同期をとって要求を発行します。jQuery と Silverlight では、それぞれもう 1 つサポート メソッドを追加して、データを処理し、使用できる形式にする (jQuery の場合は直接レンダリングする) 必要があります。

応答を取得したら、各サンプルで応答を処理する必要があります。できる限り理解しやすいよう、各サンプルを 1 つずつ説明しましょう。まず、Windows Phone 7 のサンプルでは、GetBlobsCompleted メソッドを実装します。LINQ 構文を使用して、XML をカスタム オブジェクトの列挙可能なリストに変換します (図 6 参照)。

図 6 GetBlobsCompleted メソッド

IEnumerable<StorageImage> ImageList = from Blob in xDoc.Descendants("Blob")

select new StorageImage()

  {

    Name = Blob.Descendants("Name").First().Value,

    Url = Blob.Descendants("Url").First().Value,

    Image = new BitmapImage(

      new Uri(Blob.Descendants("Url").First().Value, UriKind.Absolute)) 

  };



MainPage.ImageInfoList.Clear();

foreach (StorageImage CurImg in ImageList)

{

  MainPage.ImageInfoList.Add(CurImg);

}

オブジェクトは ObservableCollection<StorageImage> に移動する必要があります。リストをクリアしてから、LINQ の結果をループ処理してオブジェクトをこの ObservableCollection に追加します。コレクションを DataContext に配置し、ListBox を DataContext にバインドしているため、変更が自動的に取得されて ListBox の内容が更新されます。

次に、Java と Android では、LINQ ほど使いやすいテクノロジがないため、DOM からデータを手動で抽出するのでもっと手間がかかります。MarshalToBlobInfos メソッドでは、XML ドキュメント オブジェクトを取得して解析し、BlobInfo の ArrayList を構築します。

MarshalToBlobInfos メソッドは、.NET や JavaScript などで使用する他の DOM 形式のアクセスによく似ています (図 7 参照)。

図 7 MarshalToBlobInfos メソッド

public ArrayList<BlobInfo> MarshalToBlobInfos(Document doc)

  {

    ArrayList<BlobInfo> returnBlobs = new ArrayList<BlobInfo>();



  try {

    Element root = doc.getDocumentElement();

    NodeList items = root.getElementsByTagName("Blob");

    for (int idxBlobs=0;idxBlobs<items.getLength();idxBlobs++){

      BlobInfo blob = new BlobInfo();

      Node item = items.item(idxBlobs);

      NodeList blobProperties = item.getChildNodes();

      for (int idxProps=0;idxProps<blobProperties.getLength();idxProps++){

        Node property = blobProperties.item(idxProps);

        String name = property.getNodeName();

          if (name.equalsIgnoreCase("Name"))

            blob.Name = property.getFirstChild().getNodeValue();

          } else if (name.equalsIgnoreCase("Url")){

              blob.Url = property.getFirstChild().getNodeValue();

          }

      }

      returnBlobs.add(blob);

    }

  } catch (Exception e) {

      throw new RuntimeException(e);

  } 

  return returnBlobs;

  }

最後に、結果の処理と必要な HTML のレンダリングに使用する、次のような jQuery を記述します。これは他のサンプルと比べてはるかに短いコードです。しかし、目標とする快適さのレベルによっては、最も不便なコードと言えます。

function RenderData(xml) {



  $(xml).find("Blob").each(

    function () {

      $("#output").append("<img margin=50 height=70 width=60 src=" + 

      $(this).find("Url").text() +

      " title=" + $(this).find("Url").text() + " id=" + 

      $(this).find("Name").text() + ") /> ");



  });   

$("img").each(

  function () {

    $(this).click(

      function () {

        $("#zoomview").attr("src", $(this).attr("src"));

      });

  });

}

このコードでは、XML の "Blob" 要素を 1 つずつ要求します。Blob 要素を 1 つ取得するたびに、id 属性に "output" という値を割り当てている要素に <img /> タグを 1 つ書き込みます。同様のセレクター構文を使用して、現在の "Blob" 要素に含まれる、<img /> タグを正しくレンダリングするために必要な各要素の値を抽出します。続いて、追加した各 <img /> 要素のセレクターを作成し、クリック ハンドラーを追加します。

ここまでの Silverlight コードでは Windows Azure ストレージに対する REST 呼び出しを行い、結果を解析し、フィルムストリップを表示しました。最後の手順では、フィルムストリップのクリック ハンドラーを追加して、画像のズーム ビューを表示します。SelectionChanged イベントのハンドラーを追加して、現在選択している StorageImage を取得し、Image コントロールの Source プロパティに StorageImage の Image プロパティを割り当てます。

private void ImageList_SelectionChanged(object sender, SelectionChangedEventArgs e)

{

  ListBox lb = (ListBox)sender;

  StorageImage img = (StorageImage) lb.SelectedItem;

  ImageViewer.Source = img.Image;

}

Windows Phone 7 の Silverlight についてはこれで完了です。Android ではさらに作業が必要です。

Windows Azure ストレージの REST インターフェイスとわかりやすい XML 応答のおかげで、.NET、JavaScript、またはこの場合のような Android の Java のいずれを使用する場合でも、簡単に呼び出しを行い、結果を解析できます。2 つの主なサポート メソッドを作成したら、Activity の onCreate メソッドをコードに追加します。Activity 内で、既存の状態を復元するコードの後に数行のコードを追加して、Windows Azure ストレージへの呼び出しを行い、結果を解析します。

InputStream blobstream = GetBlobs();

DocumentBuilder docBuilder = 

  DocumentBuilderFactory.newInstance().newDocumentBuilder();

Document doc = docBuilder.parse(blobstream);

BlobList = MarshalToBlobInfos(doc);

この時点でコードを実行しても UI は何もありません。Windows Phone 7 のサンプルと同様のフィルムストリップが必要です。次は、UI 要素をフックします。リソース ID を渡して、setContentView を呼び出します。この場合、渡すのは main.xml のリソース ID です。

Activity の外観を変更する必要がある場合は、別のリソースを渡して呼び出すこともできます。先ほど示した main.xml のマークアップの例では、Gallery と ImageView の ID はそれぞれ Thumbnails と ZoomView です。これらの ID を使用してウィジェット (コントロール) を参照し、参照ごとにローカル変数を割り当てます。先ほどのコードの直後に、次のコードを追加します。

setContentView(R.layout.main);    

mZoomView = (ImageView)findViewById(R.id.ZoomView);    

mThumbnails = (Gallery) findViewById(R.id.Thumbnails);

続いて、コレクション内の各項目を取得、書式設定、およびレイアウトできるようにするアダプターを作成する必要があります。サムネイル Gallery のアダプターを設定するには、アダプターを作成しておく必要があります。BaseAdapter を拡張する、ImageAdapter という新しいクラスをプロジェクトに追加します。ImageAdapter を Activity のアダプターとして設定するときに、項目をカウントし、その後項目ごとにその項目の位置を渡して getView メソッドを呼び出します。項目の位置は、ArrayList のインデックスとして使用します。コンテキスト情報が必要になる、つまり、画像を表す ArrayList が必要になるため、Activity へのポインターを ImageAdapter に渡します。ImageAdapter の宣言とコンストラクターは、次のようになります。

public class ImageAdapter extends BaseAdapter 

{    

  private Context mContext;    

  private ArrayList<BlobInfo> mBlobList=null;

  public ImageAdapter(Context c) 

  {      

    mContext = c; 

    mBlobList = ((AzureImageViewer) mContext).BlobList;

   

  }    

  public int getCount() 

  {        

    int count = mBlobList.size();

    return count;



  }

アダプターの一部として実装する getView メソッドでは、画像を取得し、サムネイルを作成して、元の画像を関連する BlobInfo オブジェクトに保存します。実際の実装では、多数の画像を事前に取得し、ユーザーのスクロールに応じて画像をクリーンアップして、使用できる画像の移動可能なウィンドウをフィルムストリップの位置に基づいて表示するだけで十分な場合もあります。その場合、Silverlight や jQuery のサンプルで必要なパターンと同様に、サムネイルをバックグラウンド スレッドで取得することになります。

また、項目の取得コードは特に、getItem オーバーライドに配置する方が適していることがあります。このような注意点に基づいて、図 8 に getView メソッドの実装を示します。

図 8 getView の実装

public View getView(int position, View convertView, ViewGroup parent) 

{

  ImageView imageView;

  if (convertView == null) 

  {  

    imageView = new ImageView(mContext);

    imageView.setLayoutParams(new Gallery.LayoutParams (85, 70));

    imageView.setScaleType(ImageView.ScaleType.FIT_XY);

    imageView.setPadding(4, 4, 4, 4);

  } 

  else 

  {

    imageView = (ImageView) convertView;

  }

  BlobInfo CurrentBlob = mBlobList.get(position);

        

  if (CurrentBlob.ImageBitmap == null)

  {

    Bitmap bm = getImageBitmap(CurrentBlob.Url);

    imageView.setImageBitmap(bm);

    CurrentBlob.ImageBitmap = bm;

  }

  else

  {

    imageView.setImageBitmap(CurrentBlob.ImageBitmap)

  }     

  return imageView;    

}

ビューの初期状態を設定するには、最初の条件付きステートメントで、状態を構成するか関数に渡されたビューを使用します。if-else 構文の処理が終了すると、ビューの準備は完了です。ただし、画像はまだ表示されません。画像を表示するには、position パラメーターを使用して適切な BlobInfo オブジェクトを取得し、オブジェクトの URL プロパティを使用して画像 (今回はすべて .jpg) を取得します (図 9 参照)。

図 9 画像の取得

private Bitmap getImageBitmap(String url) { 

  Bitmap ImageBitmap = null; 

  try { 

    URL ImageURL = new URL(url); 

    URLConnection ImageConnection = ImageURL.openConnection(); 

    ImageConnection.connect(); 

    InputStream ImageStream = ImageConnection.getInputStream(); 

    BufferedInputStream BufferedStream = new BufferedInputStream(ImageStream); 

    Log.e("Available buffer: ", String.valueOf(BufferedStream.available()));

    ImageBitmap = BitmapFactory.decodeStream(BufferedStream); 

    BufferedStream.close(); 

    ImageStream.close(); 

 } catch (Exception e) { 

    Log.e("Error getting bitmap", e.getMessage()); 

 } 

 return ImageBitmap; 

}

画像を取得するには、URL に基づいて新しい接続を作成し、BufferedStream オブジェクトを作成して BitmapFactory.decodeStream メソッドに渡します。ただし、decodeStream メソッドと、BufferedStream オブジェクトの使用には、問題があるようです。画像サイズが 1 MB を超えるとまったく機能しなくなるうえにエラー メッセージも表示されないこともわかっていますが、このサンプルではそのまま使用することにします。

このような問題を回避するお勧めの方法は、手動で入力ストリームを読み取るコードを作成し、入力ストリームを BitmapFactory オブジェクトに渡すことです。BitmapFactory オブジェクトでの処理が完了したら、ビットマップを返し、ImageBitmap を設定して、ビットマップを関連する BlobInfo オブジェクトの ImageBitmap プロパティに割り当てます。既にビットマップを取得している場合は、BlobInfo オブジェクトの現在値を使用します。

サンプルを実行すると、画像のフィルムストリップが表示されます。ただし、この時点では任意の画像にズームできず、サムネイルを選択しても何も起こりません。そのため、サムネイルのクリック リスナーを追加して、選択した項目の位置に応じて BlobInfo の ArrayList を参照し、参照した BlobInfo の ImageBitmap プロパティをパラメーターに使用して (ImageView 型の) ZoomView の setImageBitmap メソッドを呼び出します。

mThumbnails.setOnItemClickListener(new OnItemClickListener()

{

    public void onItemClick (AdapterView<?> parent, View v, int position, long id)

    {

      BlobInfo CurrentBlob = BlobList.get(position);

      mZoomView.setImageBitmap(CurrentBlob.ImageBitmap);

    }

  });

どのプラットフォームにも効果がある

Windows Phone 7 や jQuery のサンプルが簡単で単純だったのに比べて、Android のサンプルは非常に複雑な実装でした。これは習熟度の問題とも考えられますが、3 つのプラットフォームで作成する必要があったコードを考えると、原因はプログラミングの快適さだけではないと思います。実際のところ、Windows Azure を使用すると、デバイスのプラットフォームに関係なくアプリケーションの魅力が増します。

Windows Azure ストレージとインターフェイスを取るには、やり取りを代行するサービスを利用するのが最も簡単です。今回は、REST 経由で一覧を取得し、以降の HTTP GET を戻り値の内容に応じて発行し、項目を直接取得するところまでアクセス方法を簡略化できました。

Joseph Fultz は、AMD のソフトウェア アーキテクトとして、ポータル インフラストラクチャやサービス インフラストラクチャとその実装について、総合的なアーキテクチャと戦略の策定を支援しています。以前は、マイクロソフトのソフトウェア アーキテクトとして、一流企業や ISV の顧客のためにアーキテクチャの定義とソリューション設計を行っていました。