×
Namespaces

Variants
Actions

Perguntas NFC - um jogo baseado em texto para Windows Phone

From Nokia Developer Wiki
Jump to: navigation, search

Este artigo explica como utilizar a Near Field Communication (NFC) para criar um jogo basedo em texto no Windows Phone 8.

WP Metro Icon Joystick.png
WP Metro Icon NFC.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Code ExampleTested with
SDK: Windows Phone 8.0 SDK
Devices(s): Nokia Lumia 920
Compatibility
Platform(s):
Windows Phone 8
Platform Security
Capabilities: ID_CAP_PROXIMITY, ID_CAP_SENSORS
Article
Translated:
By saramgsilva
Last edited: hamishwillee (18 Nov 2013)

Contents

Introdução

Este exemplo de código mostra como usar NFC no Windows Phone para ler e escrever perguntas e respostas de/para tag NFC baseados em texto. Esta aplicação pode ser usado como um jogo - o primeiro jogador pode guardar uma pergunta e respostas numa tag NFC, os jogadores seguintes podem escrever suas próprias perguntas na tag NFC se eles sabem a resposta certa.

A aplicação contém as páginas: Read, Question e Write.

Note.pngNote: Pode aprender o básico de NFC a partir de Use NFC tags with Windows Phone 8(*).

(*) Disponível apenas em inglês.

Lendo questões de uma tag NFC

Layout (MainPage.xaml)

A primeira página da aplicação contém um StackPanel que contém uma Image e uma TextBlock (InfoTextBlock). Esta InfoTextBlock solicita aos usuários de aplicativos para mover telefone próximo ao tag NFC. A mesma TextBlock será utilizada para mostrar outra informação.

Ler

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Top" Height="100" Width="100" Source="/Assets/icon.png"/>
<TextBlock Grid.Row="1"
x:Name="InfoTextBlock"
TextWrapping="Wrap"
Text="Move your phone near to NFC tag to read question."
VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="30"
TextAlignment="Center"
Foreground="White"
/>
</StackPanel>
</Grid>

Programando (MainPage.xaml.cs)

No método OnNavigatedTo primeiro verificamos se o dispositivo do utilizador final suporta NFC e inicializamos os "event's handles" para o dispositivo chegada e o dispositivo de partida. Este exemplo guarda a questão na tag NFC com o método PublishBinaryMessage usando Windows:WriteTag.NFCQuestions como protocolo e sub-type. Tentamos ler esta mesma informação de tag NFC usando o método SubscribeForMessage.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
 
InfoTextBlock.Text = "Move your phone near to NFC tag to read question.";
NfcTagIndicator.Background = new System.Windows.Media.SolidColorBrush(Colors.Red);
// check if device has NFC
device = ProximityDevice.GetDefault();
if (device != null)
{
timerDisposed = false;
questionFound = false;
timerStarted = false;
device.DeviceArrived += device_DeviceArrived;
device.DeviceDeparted += device_DeviceDeparted;
questionSubscribeId = device.SubscribeForMessage("Windows.NFCQuestions", NFCQuestionsHandler);
}
else
{
MessageBox.Show("Your phone has no NFC or NFC is disabled");
return;
}
}

O método device_DeviceArrived será chamado quando o dispositivo está perto de uma tag NFC. Primeiro, o texto de informações no ecrã será alterado, de modo que o utilizador final sabe que tag NFC será lido.

A aplicação usa um "timer" para verificar/esperar se a questão está disponivel ou não. O "timer" terá que esperar 2 segundos e se NFCQuestionHandler não é chamado nesse tempo, então a primeira questão poderá ser escrita na tag NFC.

// NFC tag is near
private void device_DeviceArrived(ProximityDevice sender)
{
Dispatcher.BeginInvoke(() =>
{
InfoTextBlock.Text = "NFC Tag found, read question please wait...";
NfcTagIndicator.Background = new System.Windows.Media.SolidColorBrush(Colors.Green);
if (!questionFound && !timerStarted)
{
timer = new Timer(MyTimerCallback, InfoTextBlock, 2000, Timeout.Infinite);
timerStarted = true;
}
});
}

O método MyTimerCallback será chamado depois de 2 segundos quando a tag NFC está disponível e vazio será usado. Primeiro o "timer" será "disposed", a aplicação não está mais à espera da messagem da tag NFC e aplicação navega para a página "Write question".

private void MyTimerCallback(object state)
{
Dispatcher.BeginInvoke(() =>
{
// dispose timer, there is no question in tag
if (!timerDisposed)
{
timer.Dispose();
timerDisposed = true;
}
// stop subscribing to read question
device.StopSubscribingForMessage(questionSubscribeId);
MessageBox.Show("There is no question in the NFC tag. You can now set the first question.");
NavigationService.Navigate(new Uri("/WritePage.xaml?answersCount=-1", UriKind.Relative));
});
}

O método NFCQuestionHandler será chamado se existir questões disponíveis na tag NFC. Primeiro, o "timer" será "disposed" (caso isto ainda esteja a correr) e porque a questão é encontrada nós iremos parar de ler isto. Perguntas e respostas seão guardadas em uma "string" e "Regex" será usado para obter as respostas e questões de uma "string". A aplicação irá navegar para página "Answer" caso esteja tudo bem.

// get question data from NFC tag
private void NFCQuestionsHandler(ProximityDevice sender, ProximityMessage message)
{
questionFound = true;
// release timer (question found)
if (timerStarted && !timerDisposed)
{
timer.Dispose();
timerDisposed = true;
}
// stop subscribing to read question
device.StopSubscribingForMessage(questionSubscribeId);
// get question and answers
string question = message.DataAsString;
// store to strings
string[] strings = Regex.Split(question, delimeter);
// check that there is que,del,ans1,del,ans2,del,ans3,del,rightans
if (strings.Count() < 13)
{
InfoTextBlock.Text = "Cannot read question and answers from NFC Tag.";
return;
}
// got question data -> got to answer page
Dispatcher.BeginInvoke(() =>
{
NavigationService.Navigate(new Uri("/AnswerPage.xaml?q=" + question, UriKind.Relative));
});
}

Respondendo questões

Layout (AnswerPage.xaml)

A página questão é desenhada para suportar todos os tamanhos de ecrã no Windows Phone 8. Poderá ler mais sobre aplicações multi-resolução para Windows Phone 8 aqui.

A página contém 7 linhas: TextBlock, 3 RadioButtons, TextBlock, Button e 1 TextBlock. Cada linha tem altura igual a "Auto" e as margens não são definidas.

Ler

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="QuestionTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap"
VerticalAlignment="Top" Width="Auto" Height="Auto"
Text="Question text" FontSize="24" Foreground="White"/>
<RadioButton Grid.Row="1" x:Name="Answer1RadioButton" Content="Answer 1" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="White"/>
<RadioButton Grid.Row="2" x:Name="Answer2RadioButton" Content="Answer 2" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="White" />
<RadioButton Grid.Row="3" x:Name="Answer3RadioButton" Content="Answer 3" HorizontalAlignment="Left" VerticalAlignment="Top" Foreground="White" />
<TextBlock Margin="10" Grid.Row="4" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Click Answer button to see is your answer correct or not. If your answer is right at the first guess, you can leave your nickname with your question." VerticalAlignment="Top" Foreground="White"/>
<Button Grid.Row="5" x:Name="AnswerButton" Content="Answer" HorizontalAlignment="Left" VerticalAlignment="Top" Click="AnswerButton_Click" Foreground="White"/>
<TextBlock Margin="10" Grid.Row="6" x:Name="InfoTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" FontStyle="Italic" Foreground="White"/>
</Grid>

Programando (AnswerPage.xaml.cs)

O método OnNavigatedTo irá fazer um "parse" das questões e das respostas para o ecrã. A questão será passada por parâmetro para a MainPage.

// page is navigated to
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
 
string question = "";
if (NavigationContext.QueryString.TryGetValue("q", out question))
{
string[] strings = Regex.Split(question, delimeter);
QuestionTextBlock.Text = strings[0];
Answer1RadioButton.Content = strings[2];
Answer2RadioButton.Content = strings[4];
Answer3RadioButton.Content = strings[6];
correctAnswer = strings[8];
InfoTextBlock.Text = "Question written by " + strings[10] + Environment.NewLine +strings[12];
}
}

O método AnswerButton_Click será chamado quando o utilizador responder à pergunta. A aplicação irá navegar para a página WritePage caso a resposta esteja certa. AnswerCount será passado como parâmetro para a página WritePage. Os utilizadores poderão guardar o "nickname" com a questão, caso a questão esteja correcta na primeira vez como desconhecido.

private void AnswerButton_Click(object sender, RoutedEventArgs e)
{
bool correct = false;
answersCount++;
 
if (Answer1RadioButton.IsChecked == true && correctAnswer == "1")
{
correct = true;
}
else if (Answer2RadioButton.IsChecked == true && correctAnswer == "2")
{
correct = true;
}
else if (Answer3RadioButton.IsChecked == true && correctAnswer == "3")
{
correct = true;
}
else
{
correct = false;
}
 
if (correct)
{
MessageBoxResult m = MessageBox.Show("You answer is correct, do you want to insert your own question to NFC tag now?", "Correct answer", MessageBoxButton.OKCancel);
if (m == MessageBoxResult.OK)
{
NavigationService.Navigate(new Uri("/WritePage.xaml?answersCount="+answersCount, UriKind.Relative));
}
}
else
{
MessageBox.Show("Sorry, but your answer is incorrect.");
}
}

Escrever uma nova questão

Layout (WritePage.xaml)

A página escrever questão é desenhada para suportar todos os tamanhos de ecrã suportados pelo Windows Phone ( assim como todas as páginas desta aplicação ). Existe 11 linhas, cada uma tem altura com valor "Auto". Alguns controlos são posicionados na mesma linha. Isto pode ser feito usando ColumnDefinitons e atribuindo o valor 1 ao Grid.Column. A questão TextBox irá levar ambas as colunas de espaço, através da atribuição do Grid.ColumnSpan com valor 2.


Ler

<Grid x:Name="ContentPanel" Margin="12,133,12,0" Grid.RowSpan="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Question:" VerticalAlignment="Top"/>
<TextBox Grid.Row="1" Grid.ColumnSpan="2" MaxLength="200" x:Name="QuestionTextBox" HorizontalAlignment="Left" Height="143" TextWrapping="Wrap" VerticalAlignment="Top" Width="436"/>
<TextBlock Grid.Row="2" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Answer 1:" VerticalAlignment="Top" Width="187"/>
<TextBlock Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" TextWrapping="Wrap" Text="Correct:" VerticalAlignment="Top" Width="83"/>
<TextBox Grid.Row="3" MaxLength="50" x:Name="Answer1TextBox" HorizontalAlignment="Left" Height="72" TextWrapping="Wrap" VerticalAlignment="Top" Width="363" RenderTransformOrigin="0.479,0.153"/>
<RadioButton Grid.Row="3" Grid.Column="1" IsChecked="True" x:Name="A1RadioButton" Content="" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="0.426,0.194"/>
<TextBlock Grid.Row="4" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Answer 2:" VerticalAlignment="Top" Width="187"/>
<TextBox Grid.Row="5" MaxLength="50" x:Name="Answer2TextBox" HorizontalAlignment="Left" Height="72" TextWrapping="Wrap" VerticalAlignment="Top" Width="363"/>
<RadioButton Grid.Row="5" Grid.Column="1" x:Name="A2RadioButton" Content="" HorizontalAlignment="Left" VerticalAlignment="Top" RenderTransformOrigin="0.574,0.097"/>
<TextBlock Grid.Row="6" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Answer 3:" VerticalAlignment="Top" Width="187"/>
<TextBox Grid.Row="7" MaxLength="50" x:Name="Answer3TextBox" HorizontalAlignment="Left" Height="72" TextWrapping="Wrap" VerticalAlignment="Top" Width="363"/>
<RadioButton Grid.Row="7" Grid.Column="1" x:Name="A3RadioButton" Content="" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBlock Grid.Row="8" HorizontalAlignment="Left" TextWrapping="Wrap" Text="Your nickname:" VerticalAlignment="Top" Width="187"/>
<TextBox Grid.Row="9" MaxLength="50" x:Name="NameTextBox" HorizontalAlignment="Left" Height="72" TextWrapping="Wrap" VerticalAlignment="Top" Width="363"/>
<Button Grid.Row="10" x:Name="WriteButton" Content="Write to NFC tag" HorizontalAlignment="Left" VerticalAlignment="Top" Click="WriteButton_Click"/>
</Grid>

Programando (WritePage.xaml.cs)

o método OnNavigatedTo e OnNavigatedFrom usam a coleção "State" para guardar e ler os valores da TextBox. Procure mais informações a partir dos códigos-fonte.

o método WriteButton_Click será chamado quando o utilizador final quer escrever perguntas e respostas para a tag NFC (questão, resposta e "nickname" não podem estar vazios). A classe DataWriter é usado para guardar texto em formato UTF-8. Windows:WriteTag.NFCQuestions protocolo é usado para guardar o conteúdo tag NFC.

private void WriteButton_Click(object sender, RoutedEventArgs e)
{
if (QuestionTextBox.Text.Length == 0)
{
MessageBox.Show("Question is empty!");
return;
}
if (Answer1TextBox.Text.Length == 0)
{
MessageBox.Show("Answer one is empty!");
return;
}
if (Answer2TextBox.Text.Length == 0)
{
MessageBox.Show("Answer two is empty!");
return;
}
if (Answer3TextBox.Text.Length == 0)
{
MessageBox.Show("Answer three is empty!");
return;
}
if (NameTextBox.Text.Length == 0)
{
MessageBox.Show("Name is empty!");
return;
}
// check correct answer
string correct = "";
if (A1RadioButton.IsChecked == true) correct = "1";
else if (A2RadioButton.IsChecked == true) correct = "2";
else if (A3RadioButton.IsChecked == true) correct = "3";
 
// write question
string date = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();
var dataWriter = new DataWriter() { UnicodeEncoding = UnicodeEncoding.Utf8 };
dataWriter.WriteString(QuestionTextBox.Text + "xPTMx" +
Answer1TextBox.Text + "xPTMx" +
Answer2TextBox.Text + "xPTMx" +
Answer3TextBox.Text + "xPTMx" +
correct + "xPTMx" +
NameTextBox.Text + "xPTMx" +
date
);
publishId = device.PublishBinaryMessage("Windows:WriteTag.NFCQuestions", dataWriter.DetachBuffer(), QuestionTransmitted);
WriteButton.IsEnabled = false;
}

QuestionTransmitted método será chamado após os dados serem gravados em tag NFC. Aqui podemos parar de publicar perguntas e navegar de volta para a primeira página da aplicação (a questão pode ser lida novamente para testar se funciona).

private void QuestionTransmitted(ProximityDevice sender, long messageId)
{
// stop publishing question
device.StopPublishingMessage(publishId);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
MessageBox.Show("Question, answers and your nickname are now written to the NFC tag. Application first page will be shown again.");
if (!fromMain) NavigationService.RemoveBackEntry(); // remove answer page if it exists
NavigationService.GoBack();
});
}

Sumário

Este exemplo de código mostra como usar NFC no Windows Phone 8 (e posterior). Espero que ache este artigo útil e o ajude a trabalhar com NFC nas suas aplicações Windows Phone. O código fonte pode ser obtido aqui: File:PTMNFCQuestions.zip

This page was last modified on 18 November 2013, at 04:29.
135 page views in the last 30 days.