写真と現在位置を入れた画像日記アプリを作る

ページがアクティブになった時の処理
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上の好きな場所をマークして情報を表示するプログラムを作る


