• 검색 결과가 없습니다.

10주차 - Chapter 04 윈도우폰 7 애플리케이션의 구성과 구현 (계속)

N/A
N/A
Protected

Academic year: 2021

Share "10주차 - Chapter 04 윈도우폰 7 애플리케이션의 구성과 구현 (계속)"

Copied!
92
0
0

로드 중.... (전체 텍스트 보기)

전체 글

(1)

10주차

강 대 기

(2)

위치서비스를 이용하는 애플리케이션 만들기

가속도 센서

(3)

위치서비스를 이용하는 애플리케이션 만들기

프로젝트 생성 및 화면 디자인

위치서비스 이용을 위한 환경설정

위치서비스 이용을 위한 변수 및 이벤트 핸들러 추가

StatusChanged 이벤트 핸들러 구현

PositionChanged 이벤트 핸들러 구현

위치서비스 중지 코드 추가

(4)

Global Positioning System (GPS)

GPS 소스가 어떤 것인가에 따라 해상도가 다르지만, 기본적으

로 10~25m 정도의 오차를 가짐

내비게이션 시스템은 자체 알고리즘으로 이를 보정함

그 외에도 Differential GPS 등과 같은 다양한 구조 개선이 있

어 왔음

2000년 5월 이전에는, 미국의 군사적 보안을 이유로, 민간용

은 일부러 100m 오차를 가지게 했었음 (Selective

Availability)

한국에서 위치정보를 제공하는 사업자에게는 국가 기관이 위

치 정보 내역을 요구할 수 있음

그 외의 위치 정보를 얻기 위한 소스(source)로는

무선 전화 모듈 – 인근 휴대폰 기지국

WiFi 핫스팟

(5)

윈도우폰은 Assisted GPS (A-GPS)를 사용함

GPS, 와이파이, 무선전화 모듈을 조합해서 사용

단말기의 전력 소비를 최소화하고, 적절한 정확도를 가진 위치

정보를 제공하기 위해, 반드시는 아니지만 대략 다음과 같은 전

략을 구사함

위치 정보의 정확도가 높지 않아도 될 경우 – 정확도가 떨어지지만 전력

소비가 상대적으로 적은 와이파이와 무선전화 모듈 사용

높은 정확도가 필요한 경우 – GPS 모듈 사용

전반적으로 각 모듈의 상태와 운영체제가 사용 가능한 조합 모듈을 고려

해서 사용함

위치 정보의 정확도에 대한 설정은 애플리케이션에서 하고, 이를

바탕으로 운영체제에게 위치 서비스 요청

질문

iPod touch 에는 GPS가 있는가?

iPad Wifi 버전에는 GPS가 있는가?

(6)

위치 서비스를 사용하는 애플리케이션은 위치 정보의 정확

성과 전력 소비량 사이의 균형을 유지하는 것이 중요함

높은 정확도의 위치 정보가 필요하기 전까지는 낮은 정확도

의 위치 정보를 요청함

위치 정보가 더 이상 필요하지 않을 시에는 위치 서비스의

사용을 중지시킴

위치 정보 서비스 사용시 두 가지 고려 사항

MovementThreshold 속성값 – 어느 정도의 최소 거리(미터 단위)

로 위치가 변경될 것인지를 인지하는 것에 대한 임계값

위치가 변경되었음을 알리는 이벤트를 최소 어느 정도 거리가 변경되

었을 때 발생시킬 것인가를 정함

Microsoft는 이 값을 적어도 20m 이상 설정할 것을 권장함

이렇게 하면, 노이즈로 발생되는 쓰레기 값의 영향을 덜 받고, 전력 소비도

낮추는 데 도움이 됨

유효하지 않은 위치 정보의 처리 방안 – 여러 개의 모듈로 높은 신

뢰성의 위치 정보를 제공하지만, 언제든지 유효하지 않은 위치 정

보를 줄 수 있음

(7)

프로젝트 생성

화면 디자인

비하인드 코드(코드 비하인드, 코드 숨김)에서 건

드려야 할 UI 개체들의 Name 속성 값을 적절히 변

경함

버튼의 Click 이벤트에 대한 이벤트 핸들러 구성

(8)
(9)
(10)

<!--ContentPanel - 여기에 추가 내용을 배치합니다.-->

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,38,0,0"

Name="textBlock1" Text="상태" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,74,0,0"

Name="

Status_TextBlock

" Text="위치 서비스 꺼짐" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,110,0,0"

Name="textBlock2" Text="위도" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,146,0,0"

Name="

Latitude_TextBlock

" Text="0.000" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,182,0,0"

Name="textBlock3" Text="경도" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="45,218,0,0"

Name="

Longitude_TextBlock

" Text="0.000" VerticalAlignment="Top" />

<Button Content="위치 서비스 시작" Height="72" HorizontalAlignment="Left"

Margin="88,282,0,0" Name="

StartLocationService_Button

"

VerticalAlignment="Top" Width="255" Click="

OnStartLocationService

" />

<Button Content="위치 서비스 중지" Height="72" HorizontalAlignment="Left"

Margin="88,0,0,175" Name="

StopLocationService_Button

"

VerticalAlignment="Bottom" Width="255" Click="

OnStopLocationService

" />

</Grid>

(11)

위치 서비스에 관련된 클래스들의 사용을 위해서

는 System.Device.dll 파일을 참조할 수 있어야 함

참조 추가 (Add Reference) 를 통해, 레퍼런스 파

일을 추가함

System.Device.Location 네임 스페이스 (이름 공

간)을 비하인드 코드에 추가함

using System.Device.Location;

(12)
(13)

위치 서비스는 GeoCoordinateWatcher 개체에 의해

접근할 수 있음

GeoCoordinateWatcher 변수 추가

위치 서비스를 시작하고 끝내는 두 개의 버튼에 대한

핸들러 추가

위치 서비스 시작 (OnStartLocationService)

GeoCoordinateWatcher 변수 초기화

기본은 낮은 정확도

특정한 정확도 (예를 들어 높은 정확도)를 요구하는 경우

GeoPositionAccuracy 개체의 열거값을 패러미터로 하는 생성자를

사용하여 초기화함

MovementThreshold 속성 설정

위치 서비스 중지 (OnStopLocationService)

GeoCoordinateWatcher 클래스의 이벤트에 대한 핸

들러 추가 – StatusChanged, PositionChanged

(14)

using System.Device.Location; namespace PhoneApp1

{

public partial class MainPage : PhoneApplicationPage {

private GeoCoordinateWatcher geoCoordinateWatcher;

// 생성자

public MainPage() {

InitializeComponent(); }

private void OnStartLocationService(object sender, RoutedEventArgs e) {

this.geoCoordinateWatcher = new GeoCoordinateWatcher(); this.geoCoordinateWatcher.MovementThreshold = 20;

} …

(15)

GeoCoordinateWatcher 클래스로 위치 서비스를

이용하기 위해 두 개의 이벤트 핸들러를 구성함

StatusChanged와 PositionChanged

StatusChanged – 위치 서비스의 상태가 변경되면

발생함

PositionChanged – 위치 서비스의

MovementThreshold 속성 값을 기반으로 위치가

변경되었음을 인지하면 발생함

(16)

private void OnStartLocationService(object sender, RoutedEventArgs e) {

this.geoCoordinateWatcher = new GeoCoordinateWatcher(); this.geoCoordinateWatcher.MovementThreshold = 20; this.geoCoordinateWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged); this.geoCoordinateWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_Positi onChanged); this.geoCoordinateWatcher.Start(); }

void geoCoordinateWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)

{

throw new NotImplementedException(); }

void geoCoordinateWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) {

throw new NotImplementedException(); }

(17)

EventHandler<GeoPositionStatusChangedEventArgs>(geoC

oordinateWatcher_StatusChanged)

이벤트 핸들러가 다양한 이벤트 아규먼트(EventArgs)들을 처리할 수 있

다는 것을 알 수 있음

GeoPositionStatusChangedEventArgs 를 내부 데이터 타입으로 하는

이벤트 핸들러를 구성함

EventHandler<GeoPositionChangedEventArgs<GeoCoordin

ate>>(geoCoordinateWatcher_PositionChanged)

GeoCoordinate을 내부 데이터로 하는 GeoPositionChangedEventArgs

를 내부 데이터로 하는 이벤트 핸들러를 구성함

이 코드들을 이해하기 위해서는 C#의 제네릭을 제대로 알아야

C# 텍스트북을 보는 것이 제일 좋으며, 관련 링크 중 몇 개는 다

음과 같음

http://msdn.microsoft.com/ko-kr/library/512aeb7t(v=vs.80).aspx

http://msdn.microsoft.com/ko-kr/library/ms379564(v=vs.80).aspx

(18)

“위치 서비스 시작” 위한 버튼을 눌렀을 때, 위치 서비

스를 시작하도록 비동기화 방식의 Start 메소드를 호출

대부분의 함수 호출은 동기화 방식

느린 I/O가 동반되는 경우, 비동기화 방식의 메소드가

제공됨

파일 I/O, 멀티미디어 파일 재생, 네트워크 I/O, 위치 서비스

연결, 등등

따라서, Start 메소드는 위치 정보의 수신 가능 여부와

상관없이 바로 리턴됨

동기화 방식의 메소드가 필요할 경우, TryStart 메소드

를 사용함 (타임 아웃 값도 설정할 수 있음)

(19)

private void OnStartLocationService(object sender, RoutedEventArgs e) {

this.geoCoordinateWatcher = new GeoCoordinateWatcher(); this.geoCoordinateWatcher.MovementThreshold = 20; this.geoCoordinateWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged); this.geoCoordinateWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_PositionChanged); this.geoCoordinateWatcher.TryStart(true, TimeSpan.FromMilliseconds(60000)); if (GeoPositionStatus.Ready == this.geoCoordinateWatcher.Status) { // 위치 데이터를 사용 가능함 } else { // 연결 타임 아웃. 위치 데이터 사용 불가능 } }

void geoCoordinateWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) {

throw new NotImplementedException(); }

void geoCoordinateWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) {

throw new NotImplementedException(); }

(20)

위치 서비스를 담당하는 GeoCoordinateWatcher의 변경

사항을 메인 쓰레드에서 받은 후, UI 쓰레드를 통해 화면의

내용을 변경하도록 하는 경우

메인 쓰레드에서 직접 UI의 업데이트를 시도하는 경우, UI 쓰레드

와 충돌하여, invalid cross-thread access exception이 발생함.

또한 synchronized를 통해 메인 쓰레드에서 직접 업데이트를 하

려 하면 메인 쓰레드의 다른 작업들은 기다려야 하는 블록 현상이

일어남

UI 쓰레드가 특정 코드를 실행하게 하려면,

System.Windows.Deployment 라는 네임스페이스에 포함된

Dispatcher 클래스의 BeginInvoke 메소드를 사용함

이 때 특정 코드를 넣기 위해 람다 식을 사용함.

다시 말하지만, System.Windows.Deployment 라는 네임스페이

스에 포함된 Dispatcher 클래스의 BeginInvoke 메소드를 사용하

지 않고 직접 업데이트를 시도하는 경우, invalid cross-thread

access exception이 발생함.

또한 “위치 서비스 시작” 버튼과 “위치 서비스 중지” 버튼을

활성화/비활성화하는 부분도 추가함

(21)

void geoCoordinateWatcher_StatusChanged(object

sender, GeoPositionStatusChangedEventArgs e)

{

Deployment.Current.Dispatcher.BeginInvoke(()

=> geoCoordinateWatcherStatusChanged(e));

}

private object

geoCoordinateWatcherStatusChanged(GeoPositionStat

usChangedEventArgs e)

{

throw new NotImplementedException();

}

(22)

Deployment.Current.Dispatcher.BeginInvoke(()

=> geoCoordinateWatcherStatusChanged(e));

이 한 줄의 코드를 이해하려면 C#의 람다 식(Lambda

Expression)과 델리게이트(Delegate, 대리자, 위임자)

을 이해하고 있어야 함

역시 완벽한 이해를 위해서는 기본적으로 C#의 텍스

트북을 정독해야 함

참고 URL들

람다 식

http://msdn.microsoft.com/ko-kr/library/bb397687.aspx

델리게이트 (대리자)

http://msdn.microsoft.com/ko-kr/library/ms173171(v=vs.80).aspx

(23)

실제 상태 체크는 geoCoordinateWatcherStatusChanged

메소드에서 이루어짐

상태는 GeoPositionStatusChangedEventArgs 개체로 반

환되는 패러미터인 e 의 Status 속성 값을 체크해서 알 수

있음

Disabled – 위치 서비스가 비활성화되어 있거나 지원 불가능한

상태

Ready – 위치 서비스가 동작하고 위치 정보를 수신하고 있는 상

NoData – 위치 서비스가 초기화되고 있는 상태

Initializing – 위치 서비스가 동작하고 있지만, 위치 정보는 수신

하지 못하는 상태

이 상태에 맞추어 Status_TextBlock 값을 바꾸어 주고, 또

한 각 상황에 맞게 위치 서비스에 대한 버튼들을 활성화 또

는 비활성화시킴

(24)

void geoCoordinateWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) {

Deployment.Current.Dispatcher.BeginInvoke(() => geoCoordinateWatcherStatusChanged(e)); }

private void geoCoordinateWatcherStatusChanged(GeoPositionStatusChangedEventArgs e) { switch (e.Status) { case GeoPositionStatus.Disabled: this.Status_TextBlock.Text = "위치 서비스가 사용불가능하거나, 지원되지 않음"; break; case GeoPositionStatus.Initializing: this.Status_TextBlock.Text = "위치 서비스 초기화"; this.StartLocationService_Button.IsEnabled = false; break; case GeoPositionStatus.NoData: this.Status_TextBlock.Text = "위치 데이터 사용 불가능"; this.StopLocationService_Button.IsEnabled = true; break; case GeoPositionStatus.Ready: this.Status_TextBlock.Text = "위치 데이터를 받고 있음"; this.StopLocationService_Button.IsEnabled = true; break; } }

(25)

앞의 StatusChanged와 비슷한 방법으로 UI 쓰레

드를 생성하여 이벤트를 처리함

GeoPositionChangedEventArgs<GeoCoordinat

e> 개체 패러미터를 통해 받게 되는 위치 정보를

Latitude_TextBlock 과 Longitude_TextBlock에

설정해 줌

Latitude – 위도, Longitude - 경도

(26)

void geoCoordinateWatcher_PositionChanged(object sender,

GeoPositionChangedEventArgs<GeoCoordinate> e)

{

Deployment.Current.Dispatcher.BeginInvoke(() =>

geoCoordinateWatcherPositionChanged(e));

}

private

void

geoCoordinateWatcherPositionChanged(GeoPositionChangedEven

tArgs<GeoCoordinate> e)

{

this.Latitude_TextBlock.Text =

e.Position.Location.Latitude.ToString("0.000");

this.Longitude_TextBlock.Text =

e.Position.Location.Longitude.ToString("0.000");

}

(27)

Stop 메소드로 위치 서비스 중지

Status_TextBlock 설정

Latitude_TextBlock 설정

Longitude_TextBlock 설정

위치 서비스 시작 버튼 활성화

this.StartLocationService_Button.IsEnabled = true;

위치 서비스 중지 버튼 비활성화

this.StopLocationService_Button.IsEnabled = false;

(28)

private void OnStopLocationService(object sender,

RoutedEventArgs e)

{

this.geoCoordinateWatcher.Stop();

this.Status_TextBlock.Text = "위치 서비스

꺼짐";

this.Latitude_TextBlock.Text = "0.000";

this.Longitude_TextBlock.Text = "0.000";

this.StartLocationService_Button.IsEnabled

= true;

this.StopLocationService_Button.IsEnabled

= false;

}

(29)

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using System.Device.Location; namespace PhoneApp1 {

public partial class MainPage : PhoneApplicationPage {

private GeoCoordinateWatcher geoCoordinateWatcher; // 생성자

public MainPage() {

InitializeComponent(); }

private void OnStartLocationService(object sender, RoutedEventArgs e) {

this.geoCoordinateWatcher = new GeoCoordinateWatcher(); this.geoCoordinateWatcher.MovementThreshold = 20;

this.geoCoordinateWatcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(geoCoordinateWatcher_StatusChanged);

this.geoCoordinateWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoCoordinateWatcher_PositionChanged); this.geoCoordinateWatcher.Start();

(30)

void geoCoordinateWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e) {

Deployment.Current.Dispatcher.BeginInvoke(() => geoCoordinateWatcherStatusChanged(e)); }

private void geoCoordinateWatcherStatusChanged(GeoPositionStatusChangedEventArgs e) { switch (e.Status) { case GeoPositionStatus.Disabled: this.Status_TextBlock.Text = "위치 서비스가 사용불가능하거나, 지원되지 않음"; break; case GeoPositionStatus.Initializing: this.Status_TextBlock.Text = "위치 서비스 초기화"; this.StartLocationService_Button.IsEnabled = false; break; case GeoPositionStatus.NoData: this.Status_TextBlock.Text = "위치 데이터 사용 불가능"; this.StopLocationService_Button.IsEnabled = true; break; case GeoPositionStatus.Ready: this.Status_TextBlock.Text = "위치 데이터를 받고 있음"; this.StopLocationService_Button.IsEnabled = true; break; } }

(31)

void geoCoordinateWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) {

Deployment.Current.Dispatcher.BeginInvoke(() => geoCoordinateWatcherPositionChanged(e)); }

private void geoCoordinateWatcherPositionChanged(GeoPositionChangedEventArgs<GeoCoordinate> e) {

this.Latitude_TextBlock.Text = e.Position.Location.Latitude.ToString("0.000"); this.Longitude_TextBlock.Text = e.Position.Location.Longitude.ToString("0.000"); }

private void OnStopLocationService(object sender, RoutedEventArgs e) { this.geoCoordinateWatcher.Stop(); this.Status_TextBlock.Text = "위치 서비스 꺼짐"; this.Latitude_TextBlock.Text = "0.000"; this.Longitude_TextBlock.Text = "0.000"; this.StartLocationService_Button.IsEnabled = true; this.StopLocationService_Button.IsEnabled = false; } } }

(32)
(33)

윈도우폰 SDK 7.0 – GPS 센서를 테스트할 방법이 기본적

으로 지원되지 않으므로, 3

rd

party Window Phone GPS

Emulator 사용함

예 :

http://windowsteamblog.com/windows_phone/b/wpdev/ar

chive/2011/01/28/windows-phone-gps-emulator.aspx

윈도우폰 SDK 7.1 – 기본적으로 가속도 센서, 위치 센서를

위한 (그다지 좋지 않은) 지도 서비스, 스크린샷 기능이 추

가됨

동서대학교의 (위도, 경도) = (35.1462115,

129.0084867)

maps.naver.com이나 maps.google.com에서 쉽게 알아낼 수 있

(34)
(35)

LocationManager 개체를 getSystemService()를 통해서 얻음

private LocationManager mgr

mgr = (LocationManager) getSystemService(LOCATION_SERVICE)

안드로이드 에뮬레이터는 기본적으로 가짜 (Fake) GPS 제공자를

사용함

Dalvik Debug Monitor Service (DDMS) 를 이용하는 이클립스

의 에뮬레이터 컨트롤에서 위도와 경도 입력

창 > 보기 뷰 > 기타 > 안드로이드 > 에뮬레이터 컨트롤

Window > Show View > Other > Android > Emulator Control

안드로이드 에뮬레이터 컨솔 (telnet 127.1 5554)

에뮬레이터 컨트롤에서 Google Earth 에서 출력된 KML 파일 입

(36)
(37)
(38)

앞의 슬라이드에서 소개된 3

rd

party Window Phone

GPS Emulator를 사용하여 GPS를 에뮬레이트 해보자.

앞에서처럼 단순히 위도와 경도를 출력하는 게 아니라,

Window Phone 에뮬레이터에 지도 정보를 같이 표시

할 수 있다. 이를 위해서는 어떤 방법들이 있는가?

보다 구체적으로, 빙맵, Microsoft Research Maps (MSR

Maps) Service 또는 구글맵이나 심지어는 네이버맵, 다음맵,

야후맵을 이용하는 방법을 조사 및 연구해 보자.

서버에 가입한 사용자들의 닉네임과 현재 위치를 기록

하고, 자신이 있는 위치에서 일정한 반경에 있는 사람

들의 닉네임을 출력하는 프로그램을 설계해 보자.

(39)

앞의 코드들을 완전히 이해하려면, C#의 다음 특징들을 이해해야 한다.

이를 위해 이들에 대해 조사해 보고 예제를 실행해 보자.

제네릭 (Generic)

델리게이트 (대리자, Delegate)

람다 식 (Lambda Expression)

무명 메소드 (Anonymous Method)

앞의 StatusChanged 이벤트 핸들러 코드는

새로운 UI 쓰레드를 생성한

후, 델리게이트로 람다식을 사용하여 다른 메소드를 호출한 경우

이다.

이를 다음과 같은 경우로 바꿔어 보라.

새로운 UI 쓰레드를 생성하지 않고 해당 메소드에서 바로 사용한 경우

새로운 UI 쓰레드를 생성하지 않고 해당 메소드에서 다른 메소드를 호출한 경우

새로운 UI 쓰레드를 생성하지 않고 해당 메소드에서 델리게이트로 무명 메소드를

호출한 경우

새로운 UI 쓰레드를 생성하지 않고 해당 메소드에서 델리게이트로 람다 식을 호출

한 경우 (다른 메소드를 사용한 경우와 그렇지 않은 경우)

새로운 UI 쓰레드를 생성하고, 델리게이트로 다른 메소드를 호출한 경우

새로운 UI 쓰레드를 생성해서, 델리게이트로 무명 메소드를 호출한 경우

새로운 UI 쓰레드를 생성해서, 델리게이트로 람다 식을 사용한 경우 (다른 메소드

호출 안함)

(40)

가속도 센서

가속도 센서를 이용하는 애플리케이션 만들기

프로젝트 생성 및 화면 디자인

가속도 센서를 이용하기 위한 환경 설정

가속도 센서를 이용하기 위한 변수 및 이벤트 핸들러 추가

가속도 센서의 시작/중지 버튼에 대한 이벤트 핸들러 구현

ReadingChanged 이벤트 핸들러 구현

(41)

가속도 센서 – 물체의 가속도, 진동, 충격 등의 동

적 힘을 측정하는 센서

단말기를 기울거나 흔들 때, 어느 정도 기울어졌는

(42)

윈도우폰 애플리케이션 프로젝트 생성

이제 어떻게 가속도 센서 값을 받아오는지를 알아

보도록 함

이를 위해 우선 비하인드 코드에서 읽어들이는 UI

(43)
(44)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">

<TextBlock Height="30" HorizontalAlignment="Left" Margin="21,19,0,0" Name="textBlock1" Text="상태 : " VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="21,55,0,0" Name="textBlock3" Text="X : " VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="22,91,0,0" Name="textBlock5" Text="Y : " VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="22,127,0,0" Name="textBlock7" Text="Z : " VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="84,19,0,0" Name="Status_TextBlock" Text="가속도 센서 정지" VerticalAlignment="Top" /> <TextBlock Height="30" HorizontalAlignment="Left" Margin="60,55,0,0"

Name="xAxis_TextBlock" Text="0.00" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="60,91,0,0" Name="yAxis_TextBlock" Text="0.00" VerticalAlignment="Top" />

<TextBlock Height="30" HorizontalAlignment="Left" Margin="60,127,0,0" Name="zAxis_TextBlock" Text="0.00" VerticalAlignment="Top" />

<Button Content="가속도 센서 시작" Height="72" HorizontalAlignment="Left" Margin="22,198,0,0" Name="Start_Button" VerticalAlignment="Top" Width="416" Click="OnStartAccelerometer" />

<Button Content="가속도 센서 정지" Height="72" HorizontalAlignment="Left" Margin="22,276,0,0" Name="Stop_Button" VerticalAlignment="Top" Width="416" Click="OnStopAccelerometer" />

(45)

가속도 센서에 관련된 클래스는

Microsoft.Devices.Sensors.dll 파일을 참조하도

록 해야 함

참조 추가 (Add Reference)를 통해 해당 파일을

추가함

Microsoft.Devices.Sensors 네임 스페이스

(Name Space, 이름 공간) 추가

using Microsoft.Devices.Sensors;

(46)
(47)

가속도 센서는 Accelerometer 개체를 통해 접근

하므로, 이를 위한 변수를 선언함

가속도 센서를 위한 이벤트 핸들러

윈도우폰 SDK 7.0

ReadingChanged 이벤트

윈도우폰 SDK 7.1

CurrentValueChanged 이벤트

ReadingChanged 이벤트는 더 이상 사용 안함 (deprecated

– “중요도가 떨어져 더 이상 사용되지 않고 앞으로는 사라지

게 될”이라는 의미임)

(48)

using Microsoft.Devices.Sensors;

public partial class MainPage :

PhoneApplicationPage

{

(49)

// 생성자

public MainPage()

{

InitializeComponent();

this.accelerometer = new Accelerometer();

this.accelerometer.CurrentValueChanged += new

EventHandler<SensorReadingEventArgs<AccelerometerReading>

>(accelerometer_CurrentValueChanged);

}

void accelerometer_CurrentValueChanged(object sender,

SensorReadingEventArgs<AccelerometerReading> e)

{

throw new NotImplementedException();

}

(50)

// 생성자

public MainPage()

{

InitializeComponent();

this.accelerometer = new Accelerometer();

this.accelerometer.ReadingChanged += new

EventHandler<AccelerometerReadingEventArgs>(accelero

meter_ReadingChanged);

}

void accelerometer_ReadingChanged(object sender,

AccelerometerReadingEventArgs e)

{

throw new NotImplementedException();

}

(51)

가속도 센서의 시작 및 중지 버튼에 대한 이벤트

핸들러 구현

가속도 센서 시작 – OnStartAccelerometer

(52)

private void OnStartAccelerometer(object

sender, RoutedEventArgs e)

{

this.accelerometer.Start();

this.Status_TextBlock.Text

= "가속도 센서 시작";

this.Start_Button.IsEnabled = false;

this.Stop_Button.IsEnabled = true;

}

(53)

private void OnStopAccelerometer(object sender,

RoutedEventArgs e)

{

this.accelerometer.Stop();

this.Status_TextBlock.Text = "가속도 센서

정지";

this.xAxis_TextBlock.Text = "0.00";

this.yAxis_TextBlock.Text = "0.00";

this.zAxis_TextBlock.Text = "0.00";

this.Start_Button.IsEnabled = true;

this.Stop_Button.IsEnabled = false;

}

(54)

CurrentValueChanged 이벤트 핸들러는 두 개의 패

러미터를 가짐

object sender

SensorReadingEventArgs<AccelerometerReading> e

이 두 개의 패러미터들 중 두 번째인

SensorReadingEventArgs<AccelerometerReading

> 개체인 e 의 e.SensorReading.Acceleration 은

Microsoft.Xna.Framework.Vector3 형식을 사용함

이를 위해 Microsoft.Xna.Framework 참조 추가

또한 bStopped라는 상태를 나타내기 위한 멤버 변수

추가

(55)

this.accelerometer.State 는 SensorState 개체임

SensorState 값

Disabled – 센서를 사용하지 않도록 설정됨, 비활성화된 상태

Initializing – 센서가 사용 가능한 상태이며, 동작하여 초기화

NoData – 센서가 데이터를 가져올 수 없음, 센서가 동작하고

있으나, 센서 정보를 얻을 수 없음

NoPermissions – 호출한 프로그램에게 센서의 데이터에 액세

스할 수 있는 권한이 없음

NotSupported – 센서 하드웨어를 사용할 수 없음

Ready – 센서가 사용 가능한 상태이며, 센서 정보를 읽어 데이

터를 처리 중

(56)
(57)

void accelerometer_CurrentValueChanged(object sender,

SensorReadingEventArgs<AccelerometerReading> e)

{

Deployment.Current.Dispatcher.BeginInvoke(()=>accelerometerCurrentValueChan

ged(e));

}

private void

accelerometerCurrentValueChanged(SensorReadingEventArgs<AccelerometerReadi

ng> e)

{

if (bStopped) return;

this.Status_TextBlock.Text = this.accelerometer.State.ToString();

this.xAxis_TextBlock.Text = e.SensorReading.Acceleration.X.ToString("0.00”);

this.yAxis_TextBlock.Text = e.SensorReading.Acceleration.Y.ToString("0.00”);

this.zAxis_TextBlock.Text = e.SensorReading.Acceleration.Z.ToString("0.00”);

}

(58)

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Devices.Sensors; namespace PhoneApp2 {

public partial class MainPage : PhoneApplicationPage {

private Accelerometer accelerometer; private bool bStopped;

// 생성자

public MainPage() {

InitializeComponent();

this.accelerometer = new Accelerometer(); this.accelerometer.CurrentValueChanged += new

EventHandler<SensorReadingEventArgs<AccelerometerReading>>(accelerometer_CurrentValueChanged); this.bStopped = true;

(59)

private void OnStartAccelerometer(object sender, RoutedEventArgs e) { this.accelerometer.Start(); this.Status_TextBlock.Text = "가속도 센서 시작"; this.Start_Button.IsEnabled = false; this.Stop_Button.IsEnabled = true; this.bStopped = false; }

private void OnStopAccelerometer(object sender, RoutedEventArgs e) { this.bStopped = true; this.accelerometer.Stop(); this.Status_TextBlock.Text = "가속도 센서 정지"; this.xAxis_TextBlock.Text = "0.00"; this.yAxis_TextBlock.Text = "0.00"; this.zAxis_TextBlock.Text = "0.00"; this.Start_Button.IsEnabled = true; this.Stop_Button.IsEnabled = false; }

(60)

void accelerometer_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e) { Deployment.Current.Dispatcher.BeginInvoke(()=>accelerometerCurrentValueChanged(e)); } private void accelerometerCurrentValueChanged(SensorReadingEventArgs<AccelerometerReading> e) { if (bStopped) return; this.Status_TextBlock.Text = this.accelerometer.State.ToString(); this.xAxis_TextBlock.Text = e.SensorReading.Acceleration.X.ToString("0.00”); this.yAxis_TextBlock.Text = e.SensorReading.Acceleration.Y.ToString("0.00”); this.zAxis_TextBlock.Text = e.SensorReading.Acceleration.Z.ToString("0.00”); } } }

(61)

ReadingChanged 이벤트 핸들어의 패러미터인

AccelerometerReadingEventArgs 개체의 X,Y,Z

속성 값을 읽어들여 출력함

(62)

void accelerometer_ReadingChanged(object sender,

AccelerometerReadingEventArgs e)

{

Deployment.Current.Dispatcher.BeginInvoke(() =>

accelerometerReadingChanged(e));

}

private void

accelerometerReadingChanged(AccelerometerReadingEventArgs e)

{

if (bStopped) return;

this.Status_TextBlock.Text = this.accelerometer.State.ToString();

this.xAxis_TextBlock.Text = e.X.ToString("0.00");

this.yAxis_TextBlock.Text = e.Y.ToString("0.00");

this.zAxis_TextBlock.Text = e.Z.ToString("0.00");

}

(63)

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; using Microsoft.Devices.Sensors; namespace PhoneApp3 {

public partial class MainPage : PhoneApplicationPage {

private Accelerometer accelerometer; private bool bStopped;

// 생성자

public MainPage() {

InitializeComponent();

this.accelerometer = new Accelerometer();

this.accelerometer.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(accelerometer_ReadingChanged); this.bStopped = true;

(64)

private void OnStartAccelerometer(object sender, RoutedEventArgs e) { this.accelerometer.Start(); this.Status_TextBlock.Text = "가속도 센서 시작"; this.Start_Button.IsEnabled = false; this.Stop_Button.IsEnabled = true; this.bStopped = false; }

private void OnStopAccelerometer(object sender, RoutedEventArgs e) { this.bStopped = true; this.accelerometer.Stop(); this.Status_TextBlock.Text = "가속도 센서 정지"; this.xAxis_TextBlock.Text = "0.00"; this.yAxis_TextBlock.Text = "0.00"; this.zAxis_TextBlock.Text = "0.00"; this.Start_Button.IsEnabled = true; this.Stop_Button.IsEnabled = false; }

(65)

void accelerometer_ReadingChanged(object sender,

AccelerometerReadingEventArgs e)

{

Deployment.Current.Dispatcher.BeginInvoke(() =>

accelerometerReadingChanged(e));

}

private void accelerometerReadingChanged(AccelerometerReadingEventArgs e)

{

if (bStopped) return;

this.Status_TextBlock.Text = this.accelerometer.State.ToString();

this.xAxis_TextBlock.Text = e.X.ToString("0.00");

this.yAxis_TextBlock.Text = e.Y.ToString("0.00");

this.zAxis_TextBlock.Text = e.Z.ToString("0.00");

}

}

}

(66)
(67)

SensorManager 개체를 사용하며, getSystemService() 메

서드를 사용해서 얻음

private SensorManager mgr;

mgr = (SensorManager) getSystemService(SENSOR_SERVICE);

에뮬레이터의 경우, www.openintents.org 에서 대체 센서

API를 제공하는 데, 이는 코드 구글 프로젝트의 일부임

OpenIntents Sensor Simulator

http://code.google.com/p/openintents/wiki/SensorSimulat

or

www.openintents.org 의 Sensor Simulator 를 다운받아

에뮬레이터와 연결하면, 시뮬레이터에서는 가상 폰의 이미

지를 보여주고, 마우스로 움직이게 해주며, 그 움직임을 에

뮬레이터에 있는 안드로이드 프로그램에 넘김

(68)
(69)

가속도 센서를 이용하여, 공이 굴러가는 효과를 내

는 프로그램을 만들어 보자. 프로그램을 실행하면

화면에 공이 하나 있고, 윈도우폰을 기울이면 그

방향으로 공이 굴러가도록 한다.

이와 비슷한 아이폰 용 게임으로는 Labyrinth가 있음

휴대폰을 바닥이 평평한지를 검사할 때 사용하는

수준기 (수평기, Spirit level, Bubble level)로 사용

할 수 있는 프로그램을 작성해 보자.

(70)

웹브라우저

WebBrowser 컨트롤

WebBrowser 컨트롤의 특징

WebBrowser 컨트롤을 이용한 애플리케이션 만들기

프로젝트 생성 및 화면 디자인

버튼 컨트롤에 대한 이벤트 핸들러 추가

WebBrowser 컨트롤에 대한 이벤트 핸들러 추가

Back 버튼에 대한 핸들링 추가

(71)

웹 브라우저를 활용하는 두 가지 방법

인터넷 익스플로러를 실행

런처의 WebBrowserTask – 네비게이션에서 이미 다루었음

(72)

인터넷 익스플로러과 비교할 때, WebBrowser 컨트롤의 차

이점

애플리케이션에서 탐색한 웹페이지의 컨텐츠에 대한 캐시 부분을

고려해야 함

탐색한 웹 페이지의 히스토리 및 컨텐츠에 대한 부분을 컨트롤에서 관

리해 주지 않음

탐색되는 URL 주소와 보안 자물쇠 아이콘을 컨트롤 내에서 볼 수

있게 제공되지 않음

HTML 페이지의 탐색 기능이 제공되지 않음

애플리케이션에서 인터넷 익스플로러와 쿠키를 공유할 수 없음

스크립트가 실행되지 않도록 기본적으로 설정되어 있으며, 스크

립트가 실행되기 위해서는 IsScriptEnabled 속성 값을 설정해야

이 속성이 설정되면 다음 페이지가 로드되는 시점부터 스크립트가 실

행됨

HtmlBrush 가 포함되어 있지 않음

ActiveX 컨트롤이 허용되지 않음

(73)

프로젝트 생성 및 화면 디자인

URL 주소를 입력받는 TextBox 컨트롤과 입력받

은 주소로 탐색시키는 Button 컨트롤의 Name 속

성 값을 변경시킴 – 여기서는 귀찮아서 안했음

(74)
(75)

<TextBox Height="72" HorizontalAlignment="Left"

Margin="7,15,0,0" Name="textBox1"

Text="about:blank"

VerticalAlignment="Top"

Width="360" />

<Button

Content="Go"

Height="72"

HorizontalAlignment="Left" Margin="362,15,0,0"

Name="button1" VerticalAlignment="Top"

Width="88"

Click="button1_Click"

/>

<phone:WebBrowser HorizontalAlignment="Left"

Margin="12,93,0,0" Name="webBrowser1"

VerticalAlignment="Top" Height="508"

Width="438" />

(76)

버튼 컨트롤에 대해 이벤트 핸들러 추가

WebBrowser 컨트롤을 활용해서 TextBox 컨트롤

을 통해 입력된 URL 주소로 탐색하는 코드

탐색을 위해서는 Navigate 메소드를 사용함

(77)

private void button1_Click(object sender,

RoutedEventArgs e)

{

this.webBrowser1.Navigate(new

Uri(this.textBox1.Text, UriKind.Absolute));

}

(78)

TextBox 컨트롤을 통해 입력된 URL 주소는 확인할 수

있으나, 해당 웹페이지의 링크를 통해서 다른 웹페이

지로 이동하게 되면, 이에 대한 URL 주소는 현재 상태

에서는 확인할 수 없음

WebBrowser 컨트롤에서는 탐색되는 URL 주소에 대해 컨트롤

내부에서 보여줄 방법이 제공되지 않음

리디렉션되는 URL 주소나 링크된 URL 주소를 보기 위

해서는 WebBrowser 컨트롤의 이벤트를 활용한 별도

의 코드를 작성해야 함

Navigating 이벤트 - URL 주소의 리디렉션과 같이 탐색 중일

때 발생함

패러미터는 NavigatingEventArgs

Navigated 이벤트 – 탐색이 완료된 후에 발생함

패러미터는 System.Windows.Navigation.NavigationEventArgs

(79)

// 생성자 public MainPage() { InitializeComponent(); this.webBrowser1.Navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Navigating); this.webBrowser1.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(webBrowser1_Navigated); }

void webBrowser1_Navigated(object sender,

System.Windows.Navigation.NavigationEventArgs e) {

throw new NotImplementedException(); }

void webBrowser1_Navigating(object sender, NavigatingEventArgs e) {

throw new NotImplementedException(); }

(80)

void webBrowser1_Navigated(object sender,

System.Windows.Navigation.NavigationEventArgs

e

)

{

this.textBox1.Text = e.Uri.ToString();

}

void webBrowser1_Navigating(object sender,

NavigatingEventArgs

e)

{

this.textBox1.Text = e.Uri.ToString();

(81)
(82)

이전 페이지로 돌아가는 기능은 아직 구현되지 않

았음 – Back 버튼에 대한 핸들링으로 구현

URL 주소를 기억하기 위한 string 배열 변수

URL 주소의 개수를 50개 미만으로 설정

현재 탐색된 URL 주소를 가리키기 위한 인덱수 변

수 - nForwardIndex

(83)

private string[] strHistory;

private int nForwardIndex;

// 생성자

public MainPage()

{

InitializeComponent();

this.webBrowser1.Navigating += new

EventHandler<NavigatingEventArgs>(webBrowser1_Naviga

ting);

this.webBrowser1.Navigated += new

EventHandler<System.Windows.Navigation.NavigationEven

tArgs>(webBrowser1_Navigated);

this.strHistory = new string[50];

this.nForwardIndex = -1;

(84)

void webBrowser1_Navigated(object sender,

System.Windows.Navigation.NavigationEventA

rgs e)

{

this.textBox1.Text = e.Uri.ToString();

this.strHistory[++this.nForwardIndex]

= e.Uri.ToString();

}

(85)

OnBackKeyPress 메소드를 오버라이드함

nForwardIndex를 체크

이전 주소가 있으면 이전 페이지를 로드해서 가고

(e.Cancel을 true로)

아니면 애플리케이션을 종료시킴(e.Cancel을 false로 해

서 운영체제에서 핸들하도록 함)

(86)

protected override void

OnBackKeyPress(System.ComponentModel.CancelEventArgs e)

{

if (0 < this.nForwardIndex)

{

this.webBrowser1.Navigate(new

Uri(this.strHistory[--this.nForwardIndex], UriKind.Absolute));

e.Cancel = true;

}

else

{

e.Cancel = false;

}

base.OnBackKeyPress(e);

}

(87)

여기까지만 하면, Back 버튼을 눌러도 같은 URL 주소

를 계속 탐색하는 문제가 발생함

그 이유는 Back 버튼에 대한 이벤트 핸들러에서

Navigate 메소드를 호출하면 Navigated 이벤트 핸들

러가 다시 호출되면서 계속 같은 URL 주소가 저장되기

때문임

즉, Navigated 이벤트 핸들러에서는 Back 버튼으로

인해 발생하는 탐색의 경우, 해당 URL 주소를 저장하

지 않도록 처리해야 함

이를 위해서는 Back 버튼에 의한 Navigate 메소드 호

출인지를 구별하기 위한 변수 하나를 추가함

(88)

using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Shapes; using Microsoft.Phone.Controls; namespace PhoneApp4 {

public partial class MainPage : PhoneApplicationPage {

private string[] strHistory; private int nForwardIndex; private bool bNavigatedByBack; // 생성자

public MainPage() {

InitializeComponent();

this.webBrowser1.Navigating += new EventHandler<NavigatingEventArgs>(webBrowser1_Navigating);

this.webBrowser1.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(webBrowser1_Navigated); this.strHistory = new string[50];

this.nForwardIndex = -1; this.bNavigatedByBack = false; }

(89)

void webBrowser1_Navigated(object sender,

System.Windows.Navigation.NavigationEventArgs e)

{

this.textBox1.Text = e.Uri.ToString();

if (this.bNavigatedByBack)

this.bNavigatedByBack = false;

else

this.strHistory[++this.nForwardIndex] =

e.Uri.ToString();

}

void webBrowser1_Navigating(object sender,

NavigatingEventArgs e)

{

this.textBox1.Text = e.Uri.ToString();

}

(90)

private void button1_Click(object sender, RoutedEventArgs e) {

this.webBrowser1.Navigate(new Uri(this.textBox1.Text, UriKind.Absolute)); }

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) { if (0 < this.nForwardIndex) { this.bNavigatedByBack = true; this.webBrowser1.Navigate(new Uri(this.strHistory[--this.nForwardIndex], UriKind.Absolute)); e.Cancel = true; } else { e.Cancel = false; } base.OnBackKeyPress(e); } } }

(91)
(92)

앞의 예제 코드의 문제점 중 하나는 50 개의 주소만을 히스토리

로 저장할 수 있다는 것이다. 웹 서핑 중 50 개 이상의 페이지를

탐색한 경우, nForwardIndex 값은 50보다 크거나 같게 되고 이

는 배열의 범위값을 넘어서는 예외(exception)를 발생하게 된다.

이를 해결한 코드를 작성하라.

앞의 예제에서는 과거의 URL로 Back 버튼을 눌러서 돌아가다가,

처음 페이지에서 Back 버튼을 누르면, 어플리케이션이 자동으로

종료한다. 이 때, 자동으로 종료하지 않고, 어플리케이션이 끝낼

것인지를 물어보도록 하는 코드를 작성하라.

앞의 예제에는 주소들만 가지고 있다가 새롭게 로드하는 방법이

다. WebBrowser 개체의 SaveToString 메소드와

NavigateToString 메소드를 사용하면 해당 웹 컨텐츠를 스트링

으로 바꾸거나 웹 페이지에 스트링을 집어넣을(inject) 수 있다.

이를 이용해서 웹 컨텐츠도 저장할 수 있도록 예제를 확장해 보

자.

참조

관련 문서

도서관

7) 두 변수 간 동시성이 존재할 가능성을 감안하여 당기 부실기업 비중과 전기 지원금액 규모를 비교하였음 또한 부실기업 비중이 경기변동에

 백분위수는 가장 작은 값부터 가장 큰 값 사이에 자료가 어떻게 퍼져 있는지에 대한 정보를 제공한다..  대학의

자유회상에서의 계열 위치 효과 b.. 자유회상에서의 계열 위치

(Relationship Between Conservative Forces and Potential Energy) 보존력이 계 내부에서 한 일과 위치 에너지의 감소가 같도록 위치 에너지 함수(potential energy

 위성에서 발사되는 전파를 수신하여 측점에 대한 삼차원 위치, 속도 및 시간정보를 제공하도록 고안된

임의의 모양을 가진 물체와 입자 사이에 작용하는 만유인력을 구하려 면 물체의 미소 질량이 입자에 작용하는 미소 힘을 구한 후 이들의 합

- 즉, 싱크로의 최소 단위는 싱크로 발신기(transmitter)와 싱크로 수신기 (receiver)의 결합으로 구성되며, 발신기 측에서 처음 만들어진 각 변위 신 호는