PR

キャラクターが声で天気予報を教えてくれるアプリを作る

2014年2月26日(水)
薬師寺 国安

Bing Maps上に今日の天気予報を表示する処理

新しいTextBlockのインスタンス、myDateTextBlockとmyTenkiTextBlockオブジェクトを作成します。また、新しいImageのインスタンスmyImageオブジェクトを作成します。
加えて、新しいTextBlockのインスタンス、maxTextBlockとminTextBlockオブジェクトを作成します。

メンバー変数myInfoCountに要素の個数を格納しておきます。

新しいStackPanelのインスタンス、myTenkiStackPanelオブジェクトを作成します。
Marginに「5」を指定して余白を設けます。背景色に「Navy」を指定します。
myDataTextBlockの文字色は「Gold」、文字サイズは24、パディングは5と指定し、Textプロパティに、1ずつ増減するメンバー変数noに位置する要素の属性”date”の値を指定します。

myImageオブジェクトのSourceプロパティに1ずつ増減するメンバー変数noに位置する要素の子要素の値を指定します。

myTenkiTexBlockの文字色を「Red」とします。文字サイズには「22」を指定し、パディングに「5」、Widthに「300」と指定します。
Textプロパティには、1ずつ増減するメンバー変数noに位置する要素の子要素の値を指定します。

myTenkStackPanelにAddメソッドで、myDateTextBlock、myTenkiTextBlock、myImageオブジェクトを追加します。

maxTextBlockオブジェクトの文字色を「Beige」、文字サイズを「22」、パディングを「5」、Widthを「300」と指定し、Textプロパティに、1ずつ増減するメンバー変数noに位置する要素の子要素の子要素の最初のノード(FirstNode)を指定します。

要素の全ての子孫要素である要素で、1ずつ増減するメンバー変数noに位置する要素の子要素の子要素内の値を、変数tempResultに格納しながら以下の処理を繰り返します。

minTextBlockの文字色を「Beige」とします。文字サイズを「22」、パディングを「5」、Widthに「300」と指定し、TextプロパティにtempResultの取得したノードのインデックスが0の値を指定します。変数lowTempにtempResult.Nodes(0).ToStringを格納しておきます。

キャラクターに喋らせる内容をreadingTenkiに指定します。

新しいButtonのインスタンスnextButtonオブジェクトを作成します。Contentプロパティに「次」と指定します。
メンバー変数noの値がmyInfoCount-1と等しいか大きかった場合は、nextButtonの使用を不可とします。それ以外は、使用を可とします。

新しいButtonのインスタンスprevButtonオブジェクトを作成します。Contentプロパティに「前」と指定します。
もしメンバー変数noの値が0ならprevButtonの使用を不可とします。それ以外は使用可とします。
新しいStackPanelのインスタンスButtonStackPanelオブジェクトを作成します。Orientationに「Horizontal」と指定します。ButtonStackPanelオブジェクトにAddメソッドでnextButtonとprevButtonオブジェクトを追加します。

新しいStackPanelのインスタンスsyokoStackPanelオブジェクトを作成します。Orientationには「Horizontal」と指定します。
新しいImageのインスタンスsyokoImageオブジェクトを作成します。Widthに「141」、Heightに「274」と指定し、Sourceプロパティにソリューション・アクスプロ—ラー内のImageフォルダにある「syoko1.png」を指定します。

syokoStackPanelにAddメソッドでsyokoImageオブジェクトを追加します。
AddHandlerステートメントで、syokoImageがタップされた時のイベントハンドラを追加します。
イベントハンドラ内では、syokoVoiceタスクを実行します。

myTenkiStackPanelオブジェクトにAddメソッドでmaxTextBlock、minTextBlock、ButtonStackPanelオブジェクトを追加します。

新しいStackPanelのインスタンス、resultStackPanelオブジェクトを作成します。Orientationには「Horizontal」を指定します。
resultStackPanelオブジェクトにAddメソッドで、myTenkiStackPanel、syokoStackPanelを追加します。

MapLayer.SetPositionで、指定した緯度と経度の位置にresultStackPanelオブジェクトをセットします。
myMapにAddメソッドでresultStackPanelオブジェクトを追加します。
これで、天気予報が表示された横にキャラクターの画像が表示されます。

AddHandlerステートメントでnextButtonがクリックされた時に、nextButton_Clickイベントハンドラを実行します。
AddHandlerステートメントでprevButtonがクリックされた時に、prevButton_Clickイベントハンドラを実行します。

天気予報をキャラクターが喋るsyokoVoiceタスクを実行します。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

    Private Async Function DataShow() As Task
    myDateTextBlock = New TextBlock
    myTenkiTextBlock = New TextBlock
    myImage = New Image
    maxTextBlock = New TextBlock
    minTextBlock = New TextBlock
    
    myInfoCount = query.Descendants("info").Count

    myTenkiStackPanel = New StackPanel
    myTenkiStackPanel.Children.Clear()
    myTenkiStackPanel.Margin = New Thickness(5)
    myTenkiStackPanel.Background = New SolidColorBrush(Colors.Navy)
 
    With myDateTextBlock
      .Foreground = New SolidColorBrush(Colors.Gold)
      .FontSize = 24
      .Padding = New Thickness(5)
      .Text = query.Descendants("info")(no).Attribute("date").Value
    End With

    myImage.Stretch = Stretch.None
    myImage.Source = New BitmapImage(New Uri(query.Descendants("info")(no).Element("img").Value, UriKind.Absolute))

    myTenkiTextBlock.Foreground = New SolidColorBrush(Colors.Red)
    myTenkiTextBlock.FontSize = 22
    myTenkiTextBlock.Padding = New Thickness(5)
    myTenkiTextBlock.Width = 300
  
    myTenkiTextBlock.TextWrapping = TextWrapping.Wrap
    myTenkiTextBlock.Text = "【天気】=" & query.Descendants("info")(no).Element("weather").Value
    myTenkiStackPanel.Children.Add(myDateTextBlock)
 
    myTenkiStackPanel.Children.Add(myTenkiTextBlock)
    myTenkiStackPanel.Children.Add(myImage)
    maxTextBlock.Foreground = New SolidColorBrush(Colors.Beige)
    maxTextBlock.FontSize = 22
    maxTextBlock.Padding = New Thickness(5)
    maxTextBlock.Width = 300
    maxTextBlock.TextWrapping = TextWrapping.Wrap
    maxTextBlock.Text = "【最高気温】=" & query.Descendants("info")(no).Element("temperature").Element("range").FirstNode.ToString & "度"
 
    Dim lowTemp As String = String.Empty
    For Each tempResult In query.Descendants("info")(no).Elements("temperature").Elements("range")
      minTextBlock.Foreground = New SolidColorBrush(Colors.Beige)
      minTextBlock.FontSize = 22
      minTextBlock.Padding = New Thickness(5)
      minTextBlock.Width = 300
      minTextBlock.TextWrapping = TextWrapping.Wrap
      minTextBlock.Text = "【最低気温】=" & tempResult.Nodes(0).ToString & "度"
      lowTemp = tempResult.Nodes(0).ToString
    Next
 
    readingTenki = areaData & "の" & myDateTextBlock.Text & "の天気予報。" & "天気は" & query.Descendants("info")(no).Element("weather").Value & "です。" & "最高気温は" & query.Descendants("info")(no).Element("temperature").Element("range").FirstNode.ToString & "度で" & "最低気温は" & lowTemp & "度です。"
    nextButton = New Button
    nextButton.Content = "次"
    If no >= myInfoCount - 1 Then
      nextButton.IsEnabled = False
    Else
      nextButton.IsEnabled = True
    End If

    prevButton = New Button
    prevButton.Content = "前"
    If no = 0 Then
      prevButton.IsEnabled = False
    Else
      prevButton.IsEnabled = True
    End If
 
    Dim ButtonStackPanel As New StackPanel
    ButtonStackPanel.Orientation = Orientation.Horizontal
    ButtonStackPanel.Children.Add(nextButton)
    ButtonStackPanel.Children.Add(prevButton)
  
    Dim syokoStackPanel As New StackPanel
    syokoStackPanel.Orientation = Orientation.Horizontal
    Dim syokoImage As New Image
    With syokoImage
      .Width = 141
      .Height = 274
      .Source = New BitmapImage(New Uri("ms-appx:///Image/syoko1.png", UriKind.Absolute))
      .Stretch = Stretch.None
    End With
    syokoStackPanel.Children.Add(syokoImage)
 
    AddHandler syokoImage.Tapped, Async Sub()
                                    Await syokoVoice()
                                  End Sub

    myTenkiStackPanel.Children.Add(maxTextBlock)
    myTenkiStackPanel.Children.Add(minTextBlock)
    myTenkiStackPanel.Children.Add(ButtonStackPanel)
 
    Dim resultStackPanel As New StackPanel
    resultStackPanel.Orientation = Orientation.Horizontal
    resultStackPanel.Children.Add(myTenkiStackPanel)
    resultStackPanel.Children.Add(syokoStackPanel)

    MapLayer.SetPosition(resultStackPanel, New Location(CDbl(myLatitude), CDbl(myLongitude)))
    myMap.Children.Add(resultStackPanel)
 
 
   
    AddHandler nextButton.Click, AddressOf nextButton_Click
    AddHandler prevButton.Click, AddressOf prevButton_Click
    Await syokoVoice()
  End Function

[次]ボタンがタップされた時の処理

myTenkiStackPanelを表示状態にします。

メンバー変数noがmyInfoCount-1より小さかった場合は、メンバー変数noを1ずつ加算します。[前]ボタンの使用を可能にし、DataShowタスクを実行します。
そうでない場合は、[次]ボタンの使用を不可とし、[前]ボタンの使用を可とします。メンバー変数noをmyInfoCount-1で初期化します。処理を抜けます。

非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub nextButton_Click(sender As Object, e As RoutedEventArgs)
    myTenkiStackPanel.Visibility = Xaml.Visibility.Visible
 
    If no < myInfoCount - 1 Then
      no += 1
      prevButton.IsEnabled = True
      Await DataShow()
    Else
      nextButton.IsEnabled = False
      prevButton.IsEnabled = True
      no = myInfoCount - 1
      Exit Sub
    End If

End Sub

[前]ボタンがタップされた時の処理

myTenkiStackPanelを表示状態にします。

メンバー変数noが0か0より小さかった場合は、noを0で初期化します。[前]ボタンの使用を不可とします。
それ以外は、メンバー変数noの値を1ずつ減少させます。[次]、[前]ボタンの使用を可とします。
DataShowタスクを実行して処理を抜けます。

  Private Async Sub prevButton_Click(sender As Object, e As RoutedEventArgs)
    myTenkiStackPanel.Visibility = Xaml.Visibility.Visible
   
    If no <= 0 Then
      no = 0
      prevButton.IsEnabled = False
 
    Else
      no -= 1
      nextButton.IsEnabled = True
      prevButton.IsEnabled = True
      Await DataShow()
      Exit Sub
    End If

  End Sub

天気予報をキャラクターが音声で喋る処理

[次]、[前]ボタンを非表示にします。

MediaElement型のmyMedia変数を宣言し、MediaElement1で初期化しておきます。

音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成します。

SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成します。

SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生します。

音声にどんな言語で、どのような声で喋らすかは、SpeechSynthesizerのVoiceプロパティで参照できます。下記のURLを参照してください。Japanese JA は性別が「Female」で、名前は「Haruka」という女性が読み上げるようです。
> SpeechSynthesizer.Voice | voice property

非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Function syokoVoice() As Task
    nextButton.Visibility = Xaml.Visibility.Collapsed
    prevButton.Visibility = Xaml.Visibility.Collapsed
    Dim myMedia As MediaElement = Me.MediaElement1
    Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer
    Dim stream = Await synth.SynthesizeTextToStreamAsync(readingTenki)
    myMedia.SetSource(stream, stream.ContentType)
    myMedia.Play()
  End Function

キャラクターが天気予報を喋り終わった時の処理

[次]、[前]ボタンを表示します。

  Private Sub MediaElement1_MediaEnded(sender As Object, e As RoutedEventArgs) Handles MediaElement1.MediaEnded
    nextButton.Visibility = Xaml.Visibility.Visible
    prevButton.Visibility = Xaml.Visibility.Visible
  End Sub
End Class

今回はここまでです。また次回の記事でお会いしましょう。

Think IT会員限定特典
  • キャラクター音声天気予報アプリのサンプル

    『Windows 8.1+Visual Studio 2013によるWindows ストア・アプリ開発実例集』 第8回のサンプルプログラムです。
薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think IT会員サービス無料登録受付中

Think ITでは、より付加価値の高いコンテンツを会員サービスとして提供しています。会員登録を済ませてThink ITのWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスの概要とメリットをチェック

他にもこの記事が読まれています