写真と現在位置を入れた画像日記アプリを作る
ページがアクティブになった時の処理
MainPageから送られた値はe.Parameterで取得できます。これはObject型であるため、DirectCastで文字列にキャストして、変数readXmlFileに格納しておきます。
ピクチャライブラリのImageDiaryXMLサブフォルダにアクセスします。
GetFileAsyncメソッドで、readXmlFileに格納されているXMLファイルを取得し、myFileで参照します。
読み込み専用モードで、OpenAsyncメソッドでmyFileを開き、myStream変数で参照します。
UTF-8でエンコードされ、myStreamで初期化された、新しいStreamReaderのインスタンスreaderオブジェクトを作成します。
ReadToEndメソッドでファイルの最後まで読み取り、変数resultXmlに格納します。
XElemet.ParseメソッドでresultXmlの値を文字列として読み込みます。
Descendantsメソッドで、全ての子孫要素
メンバ変数myImageFileに、
メンバ変数myLatitudeに
メンバ変数myCommentには、
メンバ変数delXmlFileにはmyImageFile変数が格納している値から20文字を取り出し、文字列「.xml」と連結して格納しておきます。
メンバ変数myUriに「Yahoo!ローカルサーチAPI」のアドレスを格納します。引数latにメンバ変数myLatitudeが格納している緯度の値を指定して、lonにメンバ変数myLongitudeが格納している経度の値を指定します。
AppidにはYahooのアプリケーションIDを格納しているメンバ定数変数AppIDを指定します。
新しいHttpClientクラスのインスタンスmyHttpClientオブジェクトを作成します。HttpClientクラスは、URIで識別されるリソースにHTTP要求を送信し、そのリソースからHTTP応答を受信するための基本クラスが含まれています。
GetStringAsyncメソッドでmyUriの結果を文字列として返し、変数resultAddressに格納します。GetStringAsyncメソッドは、指定URIにGET要求を送信し、非同期操作で応答本体を文字列として返すメソッドです。
返される文字列はXML形式になっています。このXMLのルート要素には、名前空間や不要な属性が定義されていて、内容を取得する際の邪魔(-_-;)になりますので、Replace関数で、ルート要素(
XElement.Parseメソッドで置換された結果のXMLが格納されている、resultAddressを文字列として読み込みます。
読み込んだXMLから
要素の内容を取得して、変数myAddressに格納しておきます。ピクチャライブラリのサブフォルダImageDiaryにアクセスします。GetFileAsyncメソッドで、メンバ変数myImageFileに格納されている画像ファイルを取得してmyPictureFileで参照します。
OpenReadAsyncメソッドでmyPictureFileのランダムアクセスストリームを開き、myPictureで参照します。
新しいBitmapImageのインスタンスmyBmpオブジェクトを作成します。
SetSourceメソッドにBitmapSourceのソースイメージにmyPictureを指定しておきます。
Locationをメンバ変数myLatitudeが格納している緯度と、myLongitudeが格納している経度で初期化し、myMapのCenterプロパティに指定します。
新しいPushpinクラスのインスタンスmyPinオブジェクトを作成します。
ピンの背景色をCrimsonに指定します。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。
Marginプロパティに5を指定して余白を設けます。背景色をNavyに指定します。
このStackPanelオブジェクトのインスタンスを、非表示としておきます。
新しいTextBlockのインスタンスmyTextBlockオブジェクトを作成します。文字色をRedに指定します。
文字サイズに24を指定し、パディングに10を指定します。
Textプロパティに住所を格納している変数myAddressと、文字列「辺り」を連結して指定します。
新しいTextBlockのインスタンスmyCommentTextBlockオブジェクトを作成します。
文字色をGoldに指定します。文字サイズに20を指定し、パディングに5を指定します。Textプロパティにメンバ変数myCommentを指定します。
新しいImageのインスタンスmyImageオブジェクトを作成します。Widthに160、Heightに120を指定し、Sourceプロパティに、先に作成しておいたmyBmpオブジェクトを指定します。
myStackPanelオブジェクトにAddメソッドで住所の設定された、myTextBlockを、画像の指定されたmyImageを、コメントの指定された、myCommentTextBlockを追加します。
Locationを、メンバ変数myLatitudeが格納している緯度と、myLongitudeが格納している経度で初期化します。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にピンの位置を設定します。
myMapにAddメソッドでピンを追加します。
SetViewメソッドで、指定された中心部に位置、ズーム レベル、方位、およびピッチにマップビューを設定します。この場合myLocationの位置に15レベルでズームインします。
MapLayerクラスのSetPositionメソッドで、マップレイヤー内にmyStackPanelの位置を設定します。
myMapにAddメソッドでmyStackPanelを追加します。
AddHandlerステートメントでピンをタップした時のイベントハンドラを追加します。
イベントハンドラ内では、myStackPanelオブジェクトを表示状態にします。
AddHandlerステートメントでmyStackPanelをタップした時のイベントハンドラを追加します。イベントハンドラ内では、myStackPanelオブジェクトを非表示状態にします。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) Dim readXmlFile = DirectCast(e.Parameter, String) Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim myXmlSubFolder = Await myStorageFolder.CreateFolderAsync("ImageDiaryXML", CreationCollisionOption.OpenIfExists) Dim myFile = Await myXmlSubFolder.GetFileAsync(readXmlFile) Dim resultXml As String Using myStream As IRandomAccessStream = Await myFile.OpenAsync(FileAccessMode.Read) Using reader As StreamReader = New StreamReader(myStream.AsStream, System.Text.Encoding.UTF8) resultXml = reader.ReadToEnd End Using End Using Dim xmldoc As XElement = XElement.Parse(resultXml) For Each result In From c In xmldoc.Descendants("Info") Select c myImageFile = IO.Path.GetFileNameWithoutExtension(result.Element("ImageName").Value) & ".jpg" myLatitude = result.Element("Latitude").Value myLongitude = result.Element("Longitude").Value myComment = result.Element("Comment").Value delXmlFile = myImageFile.Substring(0, 20) & ".xml" Next Dim myUri As String = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", myLatitude, myLongitude, AppID) Dim myHttpClient As New HttpClient Dim resultAddress = Await myHttpClient.GetStringAsync(myUri) resultAddress = resultAddress.Replace("<YDF firstResultPosition=" & ChrW(34) & "1" & ChrW(34) & " totalResultsAvailable=" & ChrW(34) & "1" & ChrW(34) & " totalResultsReturned=" & ChrW(34) & "1" & ChrW(34) & " xmlns=" & ChrW(34) & "http://olp.yahooapis.jp/ydf/1.0" & ChrW(34) & ">", "<YDF>") Dim httpDoc As XElement = XElement.Parse(resultAddress) Dim myAddress = httpDoc.Descendants("Address").Value Dim myFolder = KnownFolders.PicturesLibrary Dim mySubFolder = Await myFolder.CreateFolderAsync("ImageDiary", CreationCollisionOption.OpenIfExists) Dim myPictureFile = Await mySubFolder.GetFileAsync(myImageFile) Dim myPicture = Await myPictureFile.OpenReadAsync Dim myBmp As New BitmapImage myBmp.SetSource(myPicture) myMap.Center = New Location(CDbl(myLatitude), CDbl(myLongitude)) Dim myPin As New Pushpin myPin.Background = New SolidColorBrush(Colors.Crimson) Dim myStackPanel As New StackPanel myStackPanel.Margin = New Thickness(5) myStackPanel.Background = New SolidColorBrush(Colors.Navy) myStackPanel.Visibility = Xaml.Visibility.Collapsed Dim myTextBlock As New TextBlock myTextBlock.Foreground = New SolidColorBrush(Colors.Red) myTextBlock.FontSize = 24 myTextBlock.Padding = New Thickness(10) myTextBlock.Text = myAddress & " 辺り" Dim myCommentTextBlock As New TextBlock myCommentTextBlock.Foreground = New SolidColorBrush(Colors.Gold) myCommentTextBlock.FontSize = 20 myCommentTextBlock.Text = myComment myCommentTextBlock.Padding = New Thickness(5) Dim myImage As New Image With myImage .Width = 160 .Height = 120 .Source = myBmp End With myStackPanel.Children.Add(myTextBlock) myStackPanel.Children.Add(myImage) myStackPanel.Children.Add(myCommentTextBlock) Dim myLocation = New Location(CDbl(myLatitude), CDbl(myLongitude)) MapLayer.SetPosition(myPin, myLocation) myMap.Children.Add(myPin) myMap.SetView(myLocation, 15) MapLayer.SetPosition(myStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude))) myMap.Children.Add(myStackPanel) AddHandler myPin.Tapped, Sub() myStackPanel.Visibility = Xaml.Visibility.Visible End Sub AddHandler myStackPanel.Tapped, Sub() myStackPanel.Visibility = Xaml.Visibility.Collapsed End Sub End Sub
「Delete」アイコンがタップされた時の処理
ピクチャライブラリのサブフォルダImageDiaryXMLにアクセスします。
GetFileAsyncメソッドで、メンバ変数myImageFileの格納している画像を取得して、変数myDelImageFileで参照します。
DeleteAsyncメソッドで、画像ファイルを削除します。
GetFileAsyncメソッドで、メンバ変数delXmlが格納しているXMLファイルを、変数myDelXmlFileで参照します。
DeleteAsyncメソッドでXMLファイルを削除します。
削除した旨のメッセージを表示し、myMap内をクリアします。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。
Private Async Sub delButton_Click(sender As Object, e As RoutedEventArgs) Handles delButton.Click Dim myStorageFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary Dim myXmlSubFolder = Await myStorageFolder.CreateFolderAsync("ImageDiaryXML", CreationCollisionOption.OpenIfExists) Dim delImageSubFolder = Await myStorageFolder.CreateFolderAsync("ImageDiary", CreationCollisionOption.OpenIfExists) Dim myDelImageFile = Await delImageSubFolder.GetFileAsync(myImageFile) Await myDelImageFile.DeleteAsync() Dim myDelXmlFile = Await myXmlSubFolder.GetFileAsync(delXmlFile) Await myDelXmlFile.DeleteAsync() Dim message As New MessageDialog("削除しました!") Await message.ShowAsync myMap.Children.Clear() End Sub
次に、ソリューションエクスプローラー内のDataIchiranShowPage.xamlを展開して表示される、DataIchiranShowPage.xaml.vbをダブルクリックしてリスト6のコードを記述します。
ロジックコードを記述する
リスト6 (DataIchiranShowPage.xaml.vb)
Option Strict On Imports Windows.Storage Imports Windows.Storage.Streams Imports System.Net.Http Imports Windows.UI Imports Windows.UI.Popups Public NotInheritable Class DataIchiranShowPage Inherits Page
「YahooのアプリケーションID」で初期化された、文字列型の定数メンバ変数を宣言します。
Const AppID As String = "YahooのアプリケーションID"
文字列型の新しいリストである、dateListメンバ変数を宣言します。
Dim dateList As New List(Of String)
ページがアクティブになった時の処理
ピクチャライブラリのImageDiaryXMLサブフォルダにアクセスします。
GetFileAsyncメソッドで、ImageDiaryXMLサブフォルダ内に格納されているXMLファイルを取得し、コレクション変数myFileに格納します。
コレクション変数myFile内を変数resultで反復処理しながら、以下の処理を行います。
OpenReadAsyncメソッドでmyFileコレクション変数内のファイルを読み込み、変数myStreamで参照します。
UTF-8でエンコードされ、myStreamで初期化された、新しいStreamReaderのインスタンスreaderオブジェクトを作成します。ReadToEndメソッドでファイルの最後まで読み取り、変数resultXmlに格納します。
XElemet.ParseメソッドでresultXmlの値を文字列として読み込みます。
Descendantsメソッドで、全ての子孫要素
メンバ変数myImageFileに、
メンバ変数myLatitudeに
メンバ変数myCommentには、
ピクチャライブラリ内のImageDiaryサブフォルダにアクセスします。GetFileAsyncメソッドで、変数myImageFileが格納しているJPG画像を取得し、変数myPictureFileで参照します。
myPictureFileをOpenReadAsyncメソッドで、ランダムアクセスストリームを開き、myPictureで参照します。
新しいBitmapImageのインスタンスmyBmpオブジェクトを作成し、SetSourceメソッドで、myPictureオブジェクトをソースイメージに指定します。
変数myUriに「Yahoo!ローカルサーチAPI」のアドレスを格納します。
引数latに変数myLatitudeが格納している緯度の値を指定して、lonに変数myLongitudeが格納している経度の値を指定します。
AppidにはYahooのアプリケーションIDを格納しているメンバ定数変数AppIDを指定します。
新しいHttpClientクラスのインスタンスmyHttpClientオブジェクトを作成します。HttpClientクラスは、URIで識別されるリソースにHTTP要求を送信し、そのリソースからHTTP応答を受信するための基本クラスが含まれています。
GetStringAsyncメソッドでmyUriの結果を文字列として返し、変数resultAddressに格納します。GetStringAsyncメソッドは、指定URIにGET要求を送信し、非同期操作で応答本体を文字列として返すメソッドです。
返される文字列はXML形式になっています。このXMLのルート要素には、名前空間や不要な属性が定義されていて、内容を取得する際の邪魔(-_-;)になりますので、Replace関数で、ルート要素(
XElement.Parseメソッドで置換された結果のXMLが格納されている、resultAddressを文字列として読み込みます。
読み込んだXMLから
要素の内容を取得して、変数myAddressに格納しておきます。コレクション変数myFile内のXMLファイルの、完全なファイルシステムパスをResultプロパティで取得し、IO.Path.GetFileNameWithoutExtensionメソッドで、パスと拡張子を除いたファイル名を取得し、変数myDateに格納します。
dateListメンバ変数に、変数myDateから11文字分取りだした値を、Addメソッドで追加します。2013年01月01日という11文字の文字列が追加されます。
新しいTextBlockのインスタンスmyDateTextBlockを作成します。文字色にCrimson、文字サイズに28、Textプロパティに変数myDateの値を指定します。
新しいTextBlockのインスタンスmyAddressTextBlockを作成します。文字色にNavy、文字サイズに20、文字の回り込みを可、Textプロパティに変数myAddressの値と文字列「辺り」を連結して指定します。
新しいImageのインスタンスmyImageオブジェクトを作成します。Widthに384、Heightに288、SourceプロパティにmyBmpオブジェクトを指定します。
新しいListBoxのインスタンスmyListBoxオブジェクトを作成します。WidthとHeightを指定し、AddメソッドでmyComment変数の値を追加します。
新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。Widthを指定し、背景色にPink、Marginに10を指定して余白を設けます。
AddメソッドでmyStackPanelオブジェクトに、myDateTextBlock、myAddressTextBlock、myImage、myListBoxオブジェクトを追加します。
最後に、myStackPanelオブジェクトをGridViewに追加します。これで、日付、住所、画像、コメント付きのStackPanelがGridView内に一覧で表示されます。
非同期処理で行われるためメソッドの先頭にAsyncを追加します。
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs) Dim xmlFolder As StorageFolder = KnownFolders.PicturesLibrary Dim myXmlSubFolder As StorageFolder = Await xmlFolder.CreateFolderAsync("ImageDiaryXML", CreationCollisionOption.OpenIfExists) Dim myFile = Await myXmlSubFolder.GetFilesAsync Dim resultXml As String For Each result In myFile Using myStream As IRandomAccessStream = Await result.OpenReadAsync Using reader As StreamReader = New StreamReader(myStream.AsStream, System.Text.Encoding.UTF8) resultXml = reader.ReadToEnd Dim xmldoc As XElement = XElement.Parse(resultXml) For Each myContents In From c In xmldoc.Descendants("Info") Select c Dim myImageFile = IO.Path.GetFileNameWithoutExtension(myContents.Element("ImageName").Value) & ".jpg" Dim myLatitude = myContents.Element("Latitude").Value Dim myLongitude = myContents.Element("Longitude").Value Dim myComment = myContents.Element("Comment").Value Dim mySubFolder = Await xmlFolder.CreateFolderAsync("ImageDiary", CreationCollisionOption.OpenIfExists) Dim myPictureFile = Await mySubFolder.GetFileAsync(myImageFile) Dim myPicture = Await myPictureFile.OpenReadAsync Dim myBmp As New BitmapImage myBmp.SetSource(myPicture) Dim myUri As String = String.Format("http://reverse.search.olp.yahooapis.jp/OpenLocalPlatform/V1/reverseGeoCoder?lat={0}&lon={1}&appid={2}", myLatitude, myLongitude, AppID) Dim myHttpClient As New HttpClient Dim resultAddress = Await myHttpClient.GetStringAsync(myUri) resultAddress = resultAddress.Replace("<YDF firstResultPosition=" & ChrW(34) & "1" & ChrW(34) & " totalResultsAvailable=" & ChrW(34) & "1" & ChrW(34) & " totalResultsReturned=" & ChrW(34) & "1" & ChrW(34) & " xmlns=" & ChrW(34) & "http://olp.yahooapis.jp/ydf/1.0" & ChrW(34) & ">", "<YDF>") Dim httpDoc As XElement = XElement.Parse(resultAddress) Dim myAddress = httpDoc.Descendants("Address").Value Dim myDate = IO.Path.GetFileNameWithoutExtension(result.Path) dateList.Add(myDate.Substring(0, 11)) Dim myDateTextBlock As New TextBlock With myDateTextBlock .Foreground = New SolidColorBrush(Colors.Crimson) .FontSize = 28 .Text = myDate End With Dim myAddressTextBlock As New TextBlock With myAddressTextBlock .Width = 384 .Foreground = New SolidColorBrush(Colors.Navy) .FontSize = 20 .TextWrapping = TextWrapping.Wrap .Text = myAddress & "辺り" End With Dim myImage As New Image With myImage .Width = 384 .Height = 288 .Source = myBmp End With Dim myListBox As New ListBox myListBox.Width = 384 myListBox.Height = 150 myListBox.Items.Add(myComment) Dim myStackPanel As New StackPanel With myStackPanel .Width = 384 .Background = New SolidColorBrush(Colors.Pink) .Margin = New Thickness(10) End With myStackPanel.Children.Add(myDateTextBlock) myStackPanel.Children.Add(myAddressTextBlock) myStackPanel.Children.Add(myImage) myStackPanel.Children.Add(myListBox) GridView1.Items.Add(myStackPanel) Next End Using End Using Next End Sub
「Yes」ボタンがタップされた時の処理
日付を入力するsearchTextBoxに何も入力されていなければ、処理を抜けます。日付が入力された時は、日付を格納しているメンバ変数dateListオブジェクトのCountプロパティで、格納されている日付の個数分、変数iで反復処理を行います。
変数iに該当するdateListの値が、searchTextBoxに入力された値と同じなら、ScrollIntoViewメソッドで、指定された項目が表示されるよう、リストをスクロールします。同じ日付が見つかった場合は、その位置を表示し、反復処理を抜けます。
Private Sub okButton_Click(sender As Object, e As RoutedEventArgs) Handles okButton.Click If searchTextBox.Text = String.Empty Then Exit Sub Else GridView1.SelectedIndex = -1 For i As Integer = 0 To dateList.Count - 1 If dateList(i) = searchTextBox.Text Then GridView1.SelectedIndex = i GridView1.ScrollIntoView(GridView1.SelectedItem) Exit For End If Next End If End Sub End Class End Class
アイコンの作成
ソリューションエクスプローラーのAssetsフォルダ内には、4つのpngファイルが入っています(表1)。
表1:Assetsフォルダ内に入っているpngファイルの種類
ファイル名 | サイズ |
---|---|
Logo.png | 150×150 |
SmallLogo.png | 30×30 |
SplashScreen.png | 620×300 |
StoreLogo.png | 50×50 |
表1の画像はデフォルトでは、□に×の画像になっています。ストアの審査では、このままの画像では審査に受かりませんので、4種類のアイコンを作る必要があります。このサンプルは実際にストアで審査の通ったアプリですので、Assetsフォルダ内には筆者の作成したアイコンが収められていますので、見てみてください。
SplashScreen.pngはアプリを起動した際に、一瞬最初に表示される画像です。スタート画面にピン止めされる画像はデフォルトでは150×150のLogo.pngが使用されます。これを長方形の画像にしたい場合は、310×150サイズのpng画像を作成し、WideLogo.png(任意の名前でOK)としてAssetsフォルダに追加しておきます。
詳細については、「自分の現在位置を取得して表示するサンプルプログラム」の記事を参照してください。
今回はここまでです。ありがとうございました。
画像日記を作るプログラム
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Bing Maps上に地震の震源地を表示するプログラムを作る
- 場所と写真を記録するプログラムを作って思い出のシーンを保存しよう
- PCで撮影した写真を並べて最適な1枚を選べるプログラムをつくる
- 現在位置近くの病院を素早く検索するサンプルプログラム
- 自分の現在位置を取得して表示するサンプルプログラム
- Webカメラで撮影した写真をセピア調に演出するアプリを作る
- 近くにある病院の場所をキャラクターが音声で教えてくれるアプリを作る
- フリーハンドで書いた住所を認識してBing Map上に表示する
- 現在位置の近くにある宿を検索するサンプルプログラム
- Bing Maps上の好きな場所をマークして情報を表示するプログラムを作る