Hello World

Để bắt đầu tạo một ứng dụng Android bằng Xamarin Studio (nếu cần trình IDE mạnh mẽ hơn thì có thể dùng Visual Studio) ta vào File -> New -> Solution -> Chọn Android Application -> Đặt tên cho Solution

Một solution dùng để chứa nhiều project, tập tin solution có phần mở rộng là .sln, có thể được mở bới Xamarin Studio lẫn Visual Studio

Giao diện làm việc và cấu trúc project của một ứng dụng Xamarin Android

Nếu đã từng phát triển ứng dụng Android thuần với ngôn ngữ Java, ta dễ dàng thấy được cấu trúc thư mục và tập tin ở project Xamarin Android với Android thuần là hoàn toàn tương tự – có thể nói là giống nhau ngoại trừ code logic thuần được viết bằng Java (vd: MainActivity.java) còn với Xamarin thì được viết bằng C# (vd: MainActivity.cs)

Thực thi ứng dụng

Để thực thi ứng dụng ta chọn thiết bị hoặc máy ảo phù hợp, chọn chế độ debug, và click Start

Các thành phần của project

Trong project của ứng dụng Xamarin Android có các thành phần cơ bản sau:

  • References: Chứa các thư viện được Import vào project, bao gồm Mono Android, .Net BCL, và các thư viện bên thứ ba mà nhà phát triển thêm vào.
  • Components: Là các thư viện thực thi (linh kiện), mà nhà phát triển có thể thêm vào từ Xamarin Components Store (có miễn phí và trả phí)

  • Assets: Thư mục chưa các tập tin raw, được đính kèm theo ứng dụng khi thực thi, mà nhà phát triển có thể truy cập đến bất kỳ lúc nào (vd: mở một file Assets.Open(“my_asset.txt”);)
  • Resources: Chứa các tập tin tài nguyên của ứng dụng như layout, hình ảnh,… (giống với thư mục res trong Android thuần)
  • AndoridManifest.xml: Lưu trữ các thông số và cấu hình của ứng dụng, như tên, phiên bản, target version, permission,…

Định dạng xml hoàn toàn giống với Android thuần

  • MainActivity.cs: Tập tin thực thi của trang Main, được viết bằng C#

Ứng dụng HelloWorld

Mặc định Xamarin sẽ tạo một ứng dụng mẫu gồm 1 Activity (Main), trên giao diện sẽ có 1 Button “Click Me!”, khi click vào Button đó thì chướng trình sẽ đếm số lần click và hiển thị lên Button.

Tập tin Layout “Main.xml”

Định nghĩa giao diện người dùng (User Interface) của trang chính, nội dung source:

[xml]<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/myButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</LinearLayout>
[/xml]

Layout được xây dựng không khác gì so với Android thuần.

  • LinearLayout là một dạng layout container – dùng để chứa các control khác – các control trong nó được sắp xếp theo dạng Stack (lần lượt từ trên xuống – vertical hoặc từ trái sang phải – horizontally
  • Một Button được tạo bên trong có id là “myButton” và hiển thị nội dung ứng với “string/hello” trong Resources/Values/String.xml:
[xml]<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, Click Me!</string>
<string name="app_name">HelloWorld</string>
</resources>
[/xml]

Ngoài việc định nghĩa giao diện bằng cách nhập các đoạn code xml như trên ta cũng có thể sử dụng giao diện trực quan để “vẽ” UI

Tập tin MainActivity.cs
[csharp]using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace HelloWorld
{
[Activity (Label = "HelloWorld", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity
{
int count = 1;

protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);

// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);

// Get our button from the layout resource,
// and attach an event to it
Button button = FindViewById<Button> (Resource.Id.myButton);

button.Click += delegate {
button.Text = string.Format ("{0} clicks!", count++);
};
}
}
}
[/csharp] Là tập tin chứa code logic miêu tả và thực thi chương trình chính. Nội dung:

  • Class MainActivity: Kế thừa thừa Android.App.Activity được định nghĩa các tham số cơ bản như: Label (nhãn), Icon (biểu tượng) và MainLauncher (trình thực thi chính – khi app chạy thi thì tập tin này sẽ thực thi đầu tiên)
  • Theo Chu trình sống của App (Application Life Cycle) – sẽ tìm hiểu ở phần sau thì khi Activity khởi tạo thì sẽ gọi phương thức OnCreate, do vậy trong OnCreate sẽ là những đoạn lệnh dùng để khởi tạo các giá trị chính của Activity như:
    • SetContentView: Chọn Layout cho Activity, ở đây đang chọn layout Main (định nghĩa trong tập tin Resources/Layout/Main.xml)
    • Get Button có Id là “myButton” bằng phương thức FindViewById
    • Xử lý sự kiện Click cho “myButton”

Ứng dụng: Phoneword

Ta sẽ tìm hiểu một ứng dụng mẫu của Xamarin có tên là Phoneword, ứng dụng gồm có 2 màn hình, màn hình chính sẽ “dịch” một đoạn text sang số điện thoại và cho phép người dùng call tới số đó, màn hình thứ 2 sẽ chứa lịch sử các cuộc gọi.

Source code download tại đây

Các thành phần của project Phoneword
  • Resources\layout\Main.xml định nghĩa UI cho trang chính
  • Resources\values\Strings.xml khai báo các chuỗi ký tự sử dụng trong App
  • MainActivity.cs code xử lý trang chính
  • CallHistoryActivity.cs code xử lý trang thứ hai
  • PhoneTranslator.cs chứa thuật toán dịch “phoneword”
  • Ngoài ra để ứng dụng có thể thực hiện cuộc gọi, thì trong Manifest phải bật CallPhone ở mục Required permisssions

Trang chính

Nội dung tập tin Main.xml, Control được bố trí trong LinearLayout, sắp xếp theo thứ tự từ trên xuống gồm:

  • 1 TextView để hiển thị đoạn caption “Enter…”
  • 1 EditText dùng để nhập liệu
  • 3 Button đảm nhiệm 3 nhiệm vụ khác nhau.
[xml]<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="Enter a Phoneword:"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView1" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/PhoneNumberText"
android:text="1-855-XAMARIN" />
<Button
android:text="Translate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TranslateButton" />
<Button
android:text="Call"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/CallButton" />
<Button
android:text="@string/callHistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/CallHistoryButton"
android:enabled="false" />
</LinearLayout>[/xml]

Ta cũng có thể dùng giao diện trực quan để thiết kế UI bằng cách kéo thả các control trong Form Widgets và chọn các thuộc tính cho chúng trong Properties

Khởi tạo MainActivity.cs
[csharp]using System;
using System.Collections.Generic;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace Phoneword_Droid
{
[Activity(Label = "Phoneword", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity
{
static readonly List<string> phoneNumbers = new List<string>();

protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);

// Get our UI controls from the loaded layout
Button translateButton = FindViewById<Button>(Resource.Id.TranslateButton);
EditText phoneNumberText = FindViewById<EditText>(Resource.Id.PhoneNumberText);
Button callButton = FindViewById<Button>(Resource.Id.CallButton);
Button callHistoryButton = FindViewById<Button> (Resource.Id.CallHistoryButton);
}
}
}
[/csharp]

  • Tạo class MainActivity kế thừa Android.App.Activity, set các thuộc tính cơ bản như Label, Icon và cho MainLauncher = true để khi người dùng mở app thì Activity này sẽ được khởi động.
  • Tạo một static List<string> phoneNumbers để chứa danh sách các số mà người dùng đã gọi
  • Trong phương thức OnCreate của Activity ta set layout cho MainActivity là Layout.Main với phương thức SetContentView
  • Tiếp theo là get các đối tượng/control từ layout bằng FindViewById
Xử lý sự kiện Click cho button Translate

Trong OnCreate ta thêm đoạn code xử lý sự kiện Click cho button Transtlate như sau Xem PhoneTranslator.cs để biết thuật toán chuyển đối Phoneword
[csharp]string translatedNumber = string.Empty;

translateButton.Click += delegate
{
translatedNumber = Core.PhonewordTranslator.ToNumber(phoneNumberText.Text);
if (String.IsNullOrWhiteSpace(translatedNumber)) {
callButton.Text = "Call";
callButton.Enabled = false;
}
else {
callButton.Text = "Call " + translatedNumber;
callButton.Enabled = true;
}
};
[/csharp]

Khi click vào button Translate chương trình sẽ dùng thuật toán ToNumber được viết trong PhoneTranslator.cs để chuyển các ký tứ được nhập vào ô phoneNumberText thành dạng số điện thoại. Nếu chuyển thành công thí số điện thoại sẽ được lưu vào một biến string – translatedNumber và đính vào Text của button Call.

Xử lý sự kiện Click cho button Call

Trong OnCreate ta tiếp tục xử lý sự kiện khi click vào button Call, ứng dụng sẽ hỏi xem người dùng có muốn gọi tới số máy đã dịch không, nếu chọn call thì sẽ gọi. Để làm việc đó ta sử dụng AlertDialogIntent.ActionCall, lưu ý là phải bật CallPhone trong Manifest-Permisssions, code chi tiết và mô tả bên dưới.

[csharp]// Add callButton event handler here
callButton.Click += (sender, e) =>
{
// On "Call" button click, try to dial phone number.
var callDialog = new AlertDialog.Builder(this);
callDialog.SetMessage("Call " + translatedNumber + "?");
callDialog.SetNeutralButton("Call", delegate
{
// add dialed number to list of called numbers.
phoneNumbers.Add(translatedNumber);
// enable the Call History button
callHistoryButton.Enabled = true;

// Create intent to dial phone
var callIntent = new Intent(Intent.ActionCall);
callIntent.SetData(Android.Net.Uri.Parse("tel:" + translatedNumber));
StartActivity(callIntent);
});
callDialog.SetNegativeButton("Cancel", delegate { });

// Show the alert dialog to the user and wait for response.
callDialog.Show();
};
[/csharp]

Tập tin CallHistoryActivity.cs
[csharp] using System;
using System.Collections.Generic;
using Android.App;
using Android.OS;
using Android.Widget;

namespace Phoneword_Droid
{
[Activity(Label = "@string/callHistory")] public class CallHistoryActivity : ListActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

// Create your application here
}
}
}
[/csharp] Ta thấy CallHistoryActivity kế thừa một Activity được xây dựng sẵn là ListActivity, hiển thị một danh sách các items từ data, như array hay Cursor, kèm theo event khi người dùng chọn một item nào đó. Do đó không nhất thiết ta phải xây dựng layout cho Activity này.

Chuyển đổi màn hình

Để chuyển đổi màn hình/activity ta có thể dùng cách tạo một Intent mới và cho khởi động nó như sau:
[csharp]var intent = new Intent(this, typeof(CallHistoryActivity));
StartActivity(intent);
csharp] Ngoài ra để truyền dữ liệu sang Intent mới ta dùng các phương thức <strong>PutExtra</strong>, ví dụ để put vào một ArrayList ta làm như sau:
[csharp]intent.PutStringArrayListExtra("phone_numbers", phoneNumbers); [/csharp]

Lấy ArrayList có tên là “phone_numbers”, nếu không tìm được thì khởi tạo một Array rỗng

Sang màn hình mới để lấy dữ liệu Extra ta dùng
[csharp]var phoneNumbers = Intent.Extras.GetStringArrayList("phone_numbers") ?? new string[0]; [/csharp]

Như vậy nội dung code hoàn chỉnh về việc xử lý sự kiện Click của button Call History và phương thức khởi tạo của CallHistoryActivity là:

Trong phương thức OnCreate của MainActivity

[csharp]callHistoryButton.Click += (sender, e) =>
{
var intent = new Intent(this, typeof(CallHistoryActivity));
intent.PutStringArrayListExtra("phone_numbers", phoneNumbers);
StartActivity(intent);
};
[/csharp]

Phương thức OnCreate của CallHistoryActivity

[csharp]protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

// Create your application here
var phoneNumbers = Intent.Extras.GetStringArrayList("phone_numbers") ?? new string[0];
this.ListAdapter = new ArrayAdapter<string>(this, Android.Resource.Layout.SimpleListItem1, phoneNumbers);
}
[/csharp]

Để gán data source cho ListActivity ta gán thuộc tính ListAdapter của nó bằng một ArrayAdapter dạng string, có dữ liệu lấy từ list phoneNumbers ở MainActivity và có kiểu hiển thị là SimpleListItem1 được xây dựng sẵn. (dòng code cuối)

Nhấn Start và khởi động ứng dụng với Emulator hoặc thiết bị của bạn.