Xamarin.Form : 빠른 시작(첫 앱 만들기)
작성 배경
Xamarin.Form가 나온지 시간이 괘 지났음에도 한글문서가 많지 않은 것같다. 사실 튜토리얼에 있는 영문문서가 읽기에 어려운 편은 아니다. 그러나 단의 영어로 되어 있다는 점때문에 진입장벽이 생기는 것 같아서 틈틈히 정리를 한다. 물론 정리하다가 중단하게 될 수 도 있으리라...
본문의 내용은 Xamarin.form 공식 웹페이지의 Getting Started -> Hello, Xamarin.Form -> part 1: Quickstart를 약간의 번역과 의역을 첨가하여 작성한다.
Xamain.Forms 빠른 시작
프로젝트 만들기
새프로젝트 항목 중 Visual C# 하위 목록중에서 Cross-platform를 선택을 하면 우측 괄호에 Xamarin.Form이 보이는 항목을 클릭한다.
프로젝트 이름은 "Phoneword" 로 한다.
실제 여러개의 프로젝트가 하나의 앱을 만듣는 것이기 때문에 가급적 솔루션용 폴더를 체크하도록 한다.
확인 버튼을 클릭을 하면, 크로스플래폼 앱에 대해서 세부 설정을 할 수 있다. "Blank App"을 선택하고 .NET Standard를 선택을 하고 프로젝트를 생성한다.
(먼저 Androd 프로젝트의 MainActivity.cs 파일을 열어보고 에러 메시지가 뜨는지 확인을 해야한다. Xamarin.Form이 메이저 버전이 업데이트 되면서 일부 호환이 되지 않아서 에러가 나는 경우가 있는데, 이때는 미련없이 재설치를 하자.)
여러개의 프로젝트중에서 Phoneword 프로젝트내의 MainPage.xaml을 열고 다음과 같이 수정한다.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Phoneword.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="20, 40, 20, 20" />
<On Platform="Android, WinPhone, Windows" Value="20" />
</OnPlatform>
</ContentPage.Padding>
<StackLayout>
<Label Text="Enter a Phoneword:" />
<Entry x:Name="phoneNumberText" Text="1-855-XAMARIN" />
<Button x:Name="translateButon" Text="Translate" Clicked="OnTranslate" />
<Button x:Name="callButton" Text="Call" IsEnabled="false" Clicked="OnCall" />
</StackLayout>
</ContentPage>
변경이 끝나면, 저장(Ctrl + s)을 한다.
이제 솔루션 탐색기(Solution Explorer)의 (+)버튼을 누르면 같은 이름의 MainPage.xaml.cs 파일이 보인다(혹은 소스코드 열기로 확인할 수 있음). 해당 파일을 수정한다.
단, 주의 해야 할점이 에러가 발생하는 코드가 있는데, 이는 진행하면서 해결하니 조급해하지 말자.
MainPage.xaml.cs 입력받은 숫자문자를 숫자로 변환을 해주는 기능이다.
using System;
using Xamarin.Forms;
namespace Phoneword
{
public partial class MainPage : ContentPage
{
string translatedNumber;
public MainPage ()
{
InitializeComponent ();
}
void OnTranslate (object sender, EventArgs e)
{
translatedNumber = Core.PhonewordTranslator.ToNumber (phoneNumberText.Text);
if (!string.IsNullOrWhiteSpace (translatedNumber)) {
callButton.IsEnabled = true;
callButton.Text = "Call " + translatedNumber;
} else {
callButton.IsEnabled = false;
callButton.Text = "Call";
}
}
async void OnCall (object sender, EventArgs e)
{
if (await this.DisplayAlert (
"Dial a Number",
"Would you like to call " + translatedNumber + "?",
"Yes",
"No")) {
var dialer = DependencyService.Get<IDialer> ();
if (dialer != null)
dialer.Dial (translatedNumber);
}
}
}
}
에러 메시지에 겁먹지 말고 Ctrl + s 를 클릭하여 변경사항을 저장한다.
같은 솔루션 탐색기에서 같은 프로젝트의 App.xaml 하위에 있는 App.xaml.cs를 열고 아래와 같이 수정을 한다.
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Phoneword
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
역시 변경사항을 저장한다.
솔루션 탐색기에서 Phoneword 프로젝트에서 마우스 우클릭을 하여 추가(Add)에서 새항목(New Item)을 선택한다.
Visual C#의 code 항목에서 클래스(Class)를 선택하고 이름을 "PhoneTranslator"로 작성하고 추가(Add)한다.
생성된 PhoneTranslator.cs를 다음과 같이 수정한다.
(이때 주의 할 점이 namespace 이름을 바꾼다는 것을 간과 하지 말자)
using System.Text;
namespace Core
{
public static class PhonewordTranslator
{
public static string ToNumber(string raw)
{
if (string.IsNullOrWhiteSpace(raw))
return null;
raw = raw.ToUpperInvariant();
var newNumber = new StringBuilder();
foreach (var c in raw)
{
if (" -0123456789".Contains(c))
newNumber.Append(c);
else
{
var result = TranslateToNumber(c);
if (result != null)
newNumber.Append(result);
// Bad character?
else
return null;
}
}
return newNumber.ToString();
}
static bool Contains(this string keyString, char c)
{
return keyString.IndexOf(c) >= 0;
}
static readonly string[] digits = {
"ABC", "DEF", "GHI", "JKL", "MNO", "PQRS", "TUV", "WXYZ"
};
static int? TranslateToNumber(char c)
{
for (int i = 0; i < digits.Length; i++)
{
if (digits[i].Contains(c))
return 2 + i;
}
return null;
}
}
}
작성이 완료되었으면, 역시 저장한다.
Phoneword 프로젝트에 이번에는 Interface를 추가한다. 방법은 방금전과 마찬가지로 솔루션 탐색기에서 Phoneword 프로젝트에서 "우클릭 >> 추가 >> 새 항목" Visual C#의 code 인터페이스(interface)를 선택한뒤 IDialer로 이름을 적고 생성한다.
코드는 다음과 같이 수정한다.
namespace Phoneword
{
public interface IDialer
{
bool Dial(string number);
}
}
이 코드는 나중에 다른 프로젝트들에서 상속한다. 역시 저장을 한다.
이제, Dialer를 상속하자. 솔루션 탐색기에서 Phoneword.iOS 프로젝트에 우클릭을 한뒤에 "추가"에서 "새 항목"을 선택한다.
새 항목 화면에서 Apple의 하위의 code를 선택하고, 클래스(Class)를 선택한다. 이때 클래스가 여러개 보이는데, 처음 보이는 것을 선택하면 된다.
클래스 명은 "PhoneDialer"로 하여 생성한다.
코드는 다음과 같이 수정한다.
using Foundation;
using Phoneword.iOS;
using UIKit;
using Xamarin.Forms;
[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.iOS
{
public class PhoneDialer : IDialer
{
public bool Dial(string number)
{
return UIApplication.SharedApplication.OpenUrl (
new NSUrl ("tel:" + number));
}
}
}
변경이 완료되었으면, 저장을 한다.
이번에는 안드로이드 쪽이다. Phoneword.Android 프로젝트에 우클릭 하고 추가의 새 항목을 선택한다.
추가 창에서 visual C#하위의 Android를 선택한뒤 클래스(Class)를 선택하여 이름을 아까와 동일한 PhoneDialer로 작성하고 생성한다.
아래와 같이 코드를 수정한다(이때 임포트 되는 모듈이 많으므로 빼먹지 않도록 주의한다).
using Android.Content;
using Android.Telephony;
using Phoneword.Droid;
using System.Linq;
using Xamarin.Forms;
using Uri = Android.Net.Uri;
[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.Droid
{
public class PhoneDialer : IDialer
{
public bool Dial(string number)
{
var context = MainActivity.Instance;
if (context == null)
return false;
var intent = new Intent (Intent.ActionCall);
intent.SetData (Uri.Parse ("tel:" + number));
if (IsIntentAvailable (context, intent)) {
context.StartActivity (intent);
return true;
}
return false;
}
public static bool IsIntentAvailable(Context context, Intent intent)
{
var packageManager = context.PackageManager;
var list = packageManager.QueryIntentServices (intent, 0)
.Union (packageManager.QueryIntentActivities (intent, 0));
if (list.Any ())
return true;
var manager = TelephonyManager.FromContext (context);
return manager.PhoneType != PhoneType.None;
}
}
}
변경이 완료되었으면 저장을 한다.
솔루션 탐색기에서 Phoneword.Android 프로젝트를 더블 클릭하여 MainActivity.cs 파일을 열어서 다음과 같이 수정한다(원문은 더블클릭하라고 했지만, 해당되는 이름의 파일을 클릭해도 된다).
using Android.App;
using Android.Content.PM;
using Android.OS;
namespace Phoneword.Droid
{
[Activity(Label = "Phoneword", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
Instance = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
}
작성이 완료되면 저장을 한다.
Phoneword.Android 프로젝트를 우클릭하여 속성을 선택한다. 속성에서 "Android 매니페스트"(Android Manifest)항목을 선택하고 하단의 Required permissions: 항목중 "CALL_PHONE"항목을 체크한다.
역시 변경되었으면 저장한다.
이제 UWP를 수정하자.
솔루션 탐색기에서 Phoneword.UWP 프로젝트에 우클릭을 한뒤 추가(Add)에 새 항목(New Item)을 선택한다. 선택화면에서는 Visual C# 에서 Code를 선택하여 클래스(Class)를 선택한다. 이름은 PhoneDialer로 작성하여 생성한다.
생성된 클래스는 다음과 같이 작성한다(아직 어셈블리를 추가하지 않았기 때문에 에러가 보이는 경우도 있으나 조급할 필요가 없다).
using Phoneword.UWP;
using System;
using System.Threading.Tasks;
using Windows.ApplicationModel.Calls;
using Windows.UI.Popups;
using Xamarin.Forms;
[assembly: Dependency(typeof(PhoneDialer))]
namespace Phoneword.UWP
{
public class PhoneDialer : IDialer
{
bool dialled = false;
public bool Dial(string number)
{
DialNumber(number);
return dialled;
}
async Task DialNumber(string number)
{
var phoneLine = await GetDefaultPhoneLineAsync();
if (phoneLine != null)
{
phoneLine.Dial(number, number);
dialled = true;
}
else
{
var dialog = new MessageDialog("No line found to place the call");
await dialog.ShowAsync();
dialled = false;
}
}
async Task<PhoneLine> GetDefaultPhoneLineAsync()
{
var phoneCallStore = await PhoneCallManager.RequestStoreAsync();
var lineId = await phoneCallStore.GetDefaultLineAsync();
return await PhoneLine.FromIdAsync(lineId);
}
}
}
변경이 완료되었으면 저장한다.
프로젝트 탐색기에서 참조(Reference)를 우클릭한뒤 "참조 추가"(Add Reference)를 클릭한다.
좌특의 Universal Windows의 하위 확장(Extensions)을 클릭한다.
항목들중 Windows Mobile Extensions for the UWP 모듈 최신(작성시점 10.0.162..)을 체크하고, 확인 버튼을 클릭한다.
(종종 체크가 이미 되어 있는 경우가 있는데 이 때는 변경할 것이 없다.)
Phoneword.UWP 프로젝트 하위의 "Package.appxmanifest" 항목을 연다.
상위 탭버튼 중에 "기능"(Capabilities)를 선택한뒤에 좌측 기능중 "전화 통화"(Phone Call)를 체크 하고 저장한다.
이제 빌드할 시간이다. 먼저 지금까지 작성한 코드중에 에러가 있는지 확인을 해본다. 종종 IDailer를 찾지 못한다는 에러가 보일 수 있는데 이 경우 Phoneword 프로젝트를 빌드를 해주면 된다. Phoneword 프로젝트에 우클릭하여 빌드를 클릭하여 빌드하자.
Phoneword 빌드가 끝나면, 솔루션 탐색기에서 Phoneword.UWP 프로젝트를 우클릭을 한뒤 "시작프로젝트로 설정"(Set as StartUp Project)를 클릭한다. 이제 상단의 녹색화살표가 있는 "로컬 컴퓨터"(Local Machine)를 클릭한다(혹은 단축키 F5).
기다리면, UWP로 빌드된 프로그램이 실행된것을 확인할 수 있다.
참조자료
자바린 공식 페이지(영문)