You are on page 1of 414

ThS. NGUYỄN VĂN HIỆP - KS.

ĐINH QUANG HIỆP

GIÁO TRÌNH

LẬP TRÌNH ANDROID CƠ BẢN


BỘ GIÁO DỤC VÀ ĐÀO TẠO

TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT


THÀNH PHỐ HỒ CHÍ MINH
*******************

ThS. NGUYỄN VĂN HIỆP


KS. ĐINH QUANG HIỆP

LẬP TRÌNH
ANDROID CƠ BẢN
(Dành cho sinh viên chuyên ngành Điện - Điện tử, Điện tử
viễn thông, Tự động hóa)

NHÀ XUẤT BẢN ĐẠI HỌC QUỐC GIA


THÀNH PHỐ HỒ CHÍ MINH - 2015
LỜI MỞ ĐẦU

Hiện tại, công nghệ thiết bị di động thông minh (Smart Phone,
PDA, Table PC,…) ngày càng phát triển mạnh, cấu hình phần cứng của
các thiết bị ngày càng được các nhà sản xuất nâng cấp và sẽ hứa hẹn tiếp
tục phát triển hơn nữa trong tương lai. Điển hình là đã có khá nhiều điện
thoại bắt đầu sử dụng chip 64 bit, Ram đã tích hợp đến 3, 4 Gb và màn
hình đạt độ phân giải khủng 2K bên cạnh rất nhiều các điện thoại màn
hình Full HD, độ phân giải mà trước đây vài năm thôi vẫn là một sự khát
khao (Tính đến thời điểm tháng 10/2014). Bên cạnh đó, hệ điều hành cho
các thiết bị di động cũng phát triển nhanh chóng không kém. Các hệ điều
hành của nhiều hãng lần lượt ra đời và liên tục phát triển, cập nhật nhiều
phiên bản mới, đem lại sự thuận tiện cho người sử dụng như: Windows
phone, Android, IOS, Black Berry OS, Titan,...
Khi mà lập trình ứng dụng trên windows truyền thống dần bão hòa,
thì lập trình di động hiện đang là một mảnh đất màu mỡ. Ở Việt Nam, lập
trình di động đang là một ngành khá “hot” và thiếu nguồn nhân lực, nhưng
các trung tâm, trường học đào tạo ngành này cũng chưa nhiều, đa phần là
tự nghiên cứu. Do đó, việc đẩy mạnh công tác tìm hiểu, nghiên cứu, bổ
sung nội dung dạy và học ở các trường đại học về hệ điều hành, cách thức
tạo và phát triển ứng dụng cho hệ điều hành của các thiết bị di động là cần
thiết cho lĩnh vực công nghệ phần mềm đang phát triển của nước ta.
Thời gian gần đây, nổi bật và phát triển ấn tượng nhất trong số các
hệ điều hành dành cho thiết bị di động thông minh chính là hệ điều hành
Android với những tính năng ưu việt như: là hệ điều hành di động mã
nguồn mở cho phép các nhà phát triển tạo ra những ứng dụng di động
hấp dẫn; là một nền tảng mạnh, hỗ trợ nhiều công nghệ tiên tiến, có nhiều
API cho việc phát triển phần mềm. Android cung cấp truy cập đến một
loạt các thư viện, công cụ hữu ích và có thể sử dụng để xây dựng các ứng
dụng phong phú; và Android có thể nói đang là “con cưng” của Google
với sự hỗ trợ nâng cấp mạnh mẽ hơn bao giờ hết. Hoạt động được trên
nhiều dòng điện thoại với cấu hình đa dạng của các hãng khác nhau như
HTC, Samsung, LG, Motorola, Sony, Nokia,… từ đó tạo ra một cuộc
cách mạng mới về thế hệ thiết bị di động thông minh giá rẻ.
Trong những tháng vừa qua, một trò chơi được người Việt viết gây
“bão” trên toàn cầu, đó là trò Flappy Bird được đưa lên App Store và
Play Store vào tháng 5/2013 của anh Nguyễn Hà Đông (29 tuổi, Hà Nội).
Mặc dù, có rất nhiều tranh luận và ý kiến trái chiều về trò chơi này, tuy
nhiên điều đó cũng khiến cho người Việt chúng ta có quyền ước mơ, một
3
ước mơ không quá xa vời, đó là chúng ta có thể chen chân vào thế giới
phần mềm với những tên tuổi có tiếng. Hy vọng tài liệu Lập trình
Android cơ bản này sẽ giúp ích nhiều cho các bạn mới tiếp cận, đặc biệt
là tạo được niềm đam mê sáng tạo, xây dựng những viên gạch đầu tiên để
tạo đà cho sự nghiên cứu và phát triển tương lai.
Tài liệu viết hướng đến các sinh viên chuyên ngành Điện, Điện tử,
Viễn thông và Tự động, nên các kiến thức không quá chuyên sâu. Tất cả
đều có ví dụ đơn giản để minh họa cho các lý thuyết khô khan. Qua các
ví dụ, người đọc tự đúc kết được những kiến thức cần thiết và kinh
nghiệm cho mình. Hướng viết của tài liệu là trang bị cho sinh viên những
kỹ năng lập trình cơ bản, kết hợp các kiến thức điều khiển, điện tử đã học
để xây dựng các hệ thống giám sát điều khiển thông minh qua các thiết bị
cầm tay. Tuy nhiên, vì nội dung quá dài, nên tác giả tách ra làm hai
quyển. Quyển 1 chỉ trình bày các vấn đề lập trình Android cơ bản. Quyển
2 trình bày các ứng dụng điều khiển cụ thể và chuyên sâu dành cho sinh
viên chuyên ngành điện tử, tự động hơn.
Đây chỉ là một tài liệu sơ khai, còn rất nhiều thiếu sót và sẽ được
bổ sung hoàn thiện dần, rất mong sự đóng góp, xây dựng của tất cả bạn
đọc. Mọi ý kiến đóng góp xin liên hệ: Nguyễn Văn Hiệp, Khoa Điện-
Điện tử, Trường Đại học Sư phạm Kỹ thuật TPHCM, Email:
thewind030282@gmail.com

Ngày 18 tháng 11 năm 2014


Nhóm tác giả

Nguyễn Văn Hiệp


Đinh Quang Hiệp

4
MỤC LỤC

LỜI MỞ ĐẦU .......................................................................................... 3


MỤC LỤC ................................................................................................ 5

Chương 1: LỊCH SỬ PHÁT TRIỂN CỦA THIẾT BỊ DI


ĐỘNG VÀ HỆ ĐIỀU HÀNH DI ĐỘNG .......................... 9
1.1. Giới thiệu về lịch sử phát triển của thiết bị di động ........................... 9
1.2. Một số hệ điều hành trên thiết bị di động và điện thoại
thông minh ........................................................................................ 11

Chương 2: TỔNG QUAN VỀ HỆ ĐIỀU HÀNH ANDROID ............ 15


2.1. Android là gì? ................................................................................... 15
2.2. Lịch sử phát triển của Android ......................................................... 15
2.3. Các phiên bản của Android............................................................... 16
2.4. Ưu – Nhược điểm của Android ........................................................ 27
2.5. Giới thiệu về nề tảng của Android .................................................... 28

Chương 3: PHẦN MỀM PHÁT TRIỂN ỨNG DỤNG


ANDROID ......................................................................... 35
3.1. Download các gói phần mềm công cụ .............................................. 35
3.2. Cài đặt bộ JDK Platform (JDK) K7U21 ........................................... 38
3.3 Chạy phầm mềm Eclipse ................................................................... 40
3.4. Cài đặt Android Plugin ADT vào Eclipse ........................................ 42
3.5. Cài đặt các công cụ, thư viện hỗ trợ cho việc lập trình hệ
điều hành Android ............................................................................ 46
3.6. Tích hợp Android vào Eclipse .......................................................... 49
3.7. Cài đặt thiết lập chạy giả lập (Android Virtual Devices) ................. 49

5
Chương 4: CÁC THÀNH PHẦN CƠ BẢN TRONG
ANDROID ......................................................................... 55
4.1. Các thành phần trong một Android Project ...................................... 55
4.2. Ngôn ngữ Java trong lập trình Android trên phần mềm Eclipse ........... 74
4.3. Sử dụng phần mềm Eclipse lập trình cho Android OS ..................... 84

Chương 5: CÁC THIẾT KẾ GIAO DIỆN NGƯỜI DÙNG


(UI LAYOUTS)............................................................... 101
5.1. Linear Layout.................................................................................. 104
5.2. Absolute Layout.............................................................................. 113
5.3. Table Layout ................................................................................... 117
5.4. Relative Layout ............................................................................... 119
5.5. Frame Layout .................................................................................. 124

Chương 6: CÁC ĐIỀU KHIỂN GIAO DIỆN NGƯỜI


DÙNG CƠ BẢN (UI CONTROLS) .............................. 127
6.1. Các điều khiển giao diện người dùng cơ bản của Android ............. 128
6.2. Tạo các UI Control ......................................................................... 129

Chương 7: LISTVIEW ........................................................................ 191

Chương 8: HIỂN THỊ HÌNH ẢNH VÀ MENU ................................ 223


8.1. Gallery và Imageview ..................................................................... 223
8.2. Menu ............................................................................................... 233

Chương 9: XỬ LÝ SỰ KIỆN .............................................................. 251


9.1. Đăng ký Envent Listener bằng việc sử dụng một lớp ẩn bên
trong ................................................................................................ 253
9.2. Đăng ký sự kiện sử dụng Activity thực thi giao diện Listener ............ 259
9.3. Đăng ký sự kiện bằng cách sử dụng file Layout
Activity_main.xml để chỉ định trực tiếp một trình xử sự kiện ......... 266
6
Chương 10: ACTIVITY VÀ INTENT ............................................... 271
10.1. Activity ......................................................................................... 271
10.2. Liên kết các Activity sử dụng các Intent ...................................... 274

Chương 11: DỊCH VỤ XÁC ĐỊNH VỊ TRÍ ...................................... 303


11.1. Hiển thị bản đồ lên ứng dụng ....................................................... 303
11.2. Điều khiển bản đồ ........................................................................ 319
11.3. Tìm hiểu Google Map từ ví dụ của Google .................................. 332

Chương 12: LƯU DỮ LIỆU ............................................................... 335


12.1. Đọc dữ liệu tĩnh từ Resource ........................................................ 335
12.2. Đọc và ghi tập tin .......................................................................... 344
12.3. Tạo và sử dụng cơ sở dữ liệu ........................................................ 365

Chương 13: TIN NHẮN VÀ EMAIL ................................................. 381


13.1. Tin nhắn SMS ............................................................................... 381
13.2. Gửi Email...................................................................................... 407

TÀI LIỆU THAM KHẢO ................................................................... 412

7
8
Chương 1
LỊCH SỬ PHÁT TRIỂN CỦA THIẾT BỊ DI ĐỘNG
VÀ HỆ ĐIỀU HÀNH DI ĐỘNG

1.1.GIỚI THIỆU VỀ LỊCH SỬ PHÁT TRIỂN CỦA THIẾT BỊ DI


ĐỘNG
Ngày 10 tháng 3 năm 1867 được coi là mốc son đánh dấu sự ra đời
của điện thoại. Cha đẻ của chiếc điện thoại đầu tiên là Alexander Graham
Bell. Chiếc máy thô sơ có thể truyền được giọng nói này đã mở ra một kỷ
nguyên phát triển mới trong lịch sử thông tin liên lạc, thay thế cho điện
tín. Năm 1967, chiếc điện thoại được coi là "di động" đầu tiên trình làng
với tên gọi Carry Phone, rất cồng kềnh cho việc di chuyển vì nó nặng
đến 4.5kg.

Hình 1.1. Điện thoại đầu tiên Carry Phone

9
Điện thoại di động chính thức ra đời vào ngày 3 tháng 4 năm 1973,
mang tên Motorola Dyna Tac, phát minh bởi nhà sáng chế Martin
Cooper. Motorola Dyna Tac mang hình dáng gần giống điện thoại di
động ngày nay mặc dù vẫn còn khá cồng kềnh (nặng khoảng 0.79kg) và
không phổ biến.

Hình 1.2. Motorola Dyna Tac


Nokia 1011 là điện thoại GSM (Global System for Mobile
Communications) sản xuất hàng loạt đầu tiên. Nó được ra mắt vào ngày
10 tháng 11 năm 1992. Điện thoại có phiên bản màu đen duy nhất với
các kích thước 195 x 60 x 45 (mm) và danh bạ có thể lưu 99 số. Nokia
1011 được sản xuất đến năm 1994, sau đó nó được phát triển thành
Nokia 2110.
Năm 1996, Nokia 9000 Communicator được Nokia sản xuất. Máy
khá to và nặng 397g. Điện thoại sử dụng CPU i386 Intel 24MHz, Bộ nhớ
8MB (ứng dụng 4MB, bộ nhớ chương trình 2MB và dữ liệu người dùng
2MB). Hệ điều hành là GEOS 3.0.
10
Năm 1996, Motorola thành công vang dội với khác niệm mới “điện
thoại thời trang”. Kiểu dáng điện thoại nắp gập, nhỏ gọn đã nhanh chóng
trở thành xu hướng của điện thoại bây giờ. Điện thoại Motorola
StarTAC nặng 88g, dài 9.4cm (khi gập lại)
Năm 1999: Nokia 7110 là điện thoại đầu tiên tích hợp Wap cho
phép truy cập internet. Từng xuất hiện trong bộ phim nổi tiếng "THE
MATRIX" của Hollywood. Điểm nổi bật nhất của Nokia 7110 là cơ chế
trượt tự động. Màn hình hiển thị đơn sắc với kích thước khá nhỏ là 96x65
pixels. Nhạc chuông của 7110 là nhạc chuông đơn sắc, điểm nổi bật khi
nói về âm thanh của 7110 là loa ngoài khá to và rõ. Máy chỉ có duy nhất
một kết nối là Hồng Ngoại (iRDA).
Năm 2001, Erisson cho ra giới thiệu chiếc điện thoại màn hình màn
đầu tiên T68. Màn hình 256 màu, độ phân giải 101 x 80 pixel. Hỗ trợ kết
nối Bluetooth.
Năm 2004, chiếc điện thoại Motorola siêu mỏng Razr V3 được
trình làng và nó trở thành biểu tượng thời trang lúc bấy giờ. V3 nhanh
chóng trở thành chiếc điện thoại được ưa chóng và bán chạy nhất. Máy
mỏng 13.9mm, có hai màn hình và thiết kế vỏ sò độc đáo.

1.2. MỘT SỐ HỆ ĐIỀU HÀNH TRÊN THIẾT BỊ DI ĐỘNG VÀ


ĐIỆN THOẠI THÔNG MINH
Năm 1998, hệ điều hành Symbian lừng danh một thời. Vào sáng
ngày 24 tháng 6 năm 1998, Psion Software đã được đổi tên thành
Symbian Ltd., một công ty liên doanh lớn giữa Psion và các nhà sản xuất
điện thoại như Ericsson, Motorola và Nokia. Theo đó, nhiệm vụ của khối
“liên minh” này nhằm tập trung phát triển các PDA và điện thoại di động
thông minh.

Hình 1.3. Các nhà sản xuất


11
Symbian 5.0 không phải là tên gọi chính thức mà đây là bản EPOC
Release 5 (ER5) trên nền EPOC32. Chiếc điện thoại đầu tiên sử dụng
Symbian không phải đến từ Nokia mà của Ericsson, đó là R380 ra mắt
“đình đám” vào năm 2000. R380 không thể cài thêm các phần mềm từ
bên ngoài và còn khá nhiều hạn chế.
Tháng 10/2003, Nokia giới thiệu điện thoại chuyên chơi game N-
Gage. "Game thủ lừng danh", nhưng rất tiếc bộ nhớ chỉ có chưa đầy 4MB
nên không thể chứa được nhiều game hay. Một kết nối khá nổi bật trong
thời bấy giờ đã có mặt trên N-Gage là Bluetooth. Dù không có tính năng
nào nổi bật nhưng Nokia cũng cho N-Gage tính năng là nghe nhạc MP3
dù chỉ ở mức trung bình (16 âm sắc).
BlackBerry Os hệ điều hành dành cho danh nghiệp của RIM.
BlackBerry là một dòng thiết bị gửi nhận email di động và điện thoại
thông minh do công ty Research In Motion (RIM) của Canada phát triển
và thiết kế từ năm 1999. Thiết bị BlackBerry đầu tiên được giới thiệu vào
năm 1999 ở dạng máy nhắn tin hai chiều. Đến năm 2002, hai chiếc điện
thoại thông minh phổ dụng hơn của BlackBerry được ra mắt, hỗ trợ push
e-mail, điện thoại di động, nhắn tin, Internet fax, duyệt web và các dịch
vụ thông tin không dây khác.
BlackBerry 5810 (03/2002): có chức năng thoại, tuy nhiên không
có micro trên máy, không có loa trên máy nên phải dùng tai nghe. Hỗ trợ
GPRS nên có thể email, chat, duyệt web.
7/2002, BB 6710 là chiếc điện thoại thực sự. Nổi bật về tin nhắn,
mail, web,... có cả loa và micro giúp cho việc đàm thoại đơn giản hơn
1MB SRAM và 8MB Flash
Cuối 2005, BB 8700 với hàng loại các tính năng nổi bật trở thành
chiếc BB thành công nhất trong 10 năm qua. BB 8700 hỗ trợ EDGE,
chuẩn kết nối không dây tốc độ cao tại điểm nó xuât hiện
Năm 2008, Bold 9000 GPS, Wifi, bộ nhớ trong 1GB, màn hình độ
phân giải cao hơn, đẹp hơn.
Năm 2010, Touch 9800 được giới thiệu với màn hình cảm ứng 3.2
inch (360x480), camera 5.0 (chất lượng kém).
Năm 2013, BB Z10, màn hình 4.2 inch, 1.280 x 768 pixel, Camera
8.0, CPU hai nhân 1.5GHz, Ram 2GB, bộ nhớ trong 16Ghz. Z10 đánh
dấu sự trở lại của RIM.

12
iOS là hệ điều hành trên các thiết bị di động của Apple.

Hình 1.4. Điện thoại chạy hệ điều hành IOS


Ban đầu, hệ điều hành này chỉ được phát triển để chạy trên iPhone,
nhưng sau đó nó đã được mở rộng để chạy trên các thiết bị của Apple
như iPod touch, iPad và Apple TV. Ngày 31 tháng 5 năm 2011, App
Store của Apple chứa khoảng 500 000 ứng dụng iOS, và được tải về tổng
cộng khoảng 15 tỷ lần.
Trong quý 4 năm 2010, có khoảng 26% điện thoại thông minh chạy
hệ điều hành iOS, sau hệ điều hành Android của Google và Symbian của
Nokia.
Năm 2005, Apple hợp tác vs Motorola đưa Itunes lên dòng điện
thoại Motorola Rokr, V3i và điện thoại dần thay thế vị trí máy MP3.
29/6/2007, Apple bán chiếc điện thoại cảm ứng điện dung đầu tiên,
thay đổi các nhìn nhận mới cũng như xu hướng về điện thoại di động
(Ram 128, bộ nhớ trong 4G/8G, GPRS, giao diện chạm nhập đơn giản,
sinh động). Chiếc điện thoại này được gọi là Iphone 2G (IP 2G).
11/7/2008, Apple giới thiệu IP 3G, có tích hợp 3G, Ram vẫn 128
MB, bộ nhớ trong 16GB, tích hợp GPS. 10/7/2008, App Store chính
thức mở cửa, với chỉ khoảng 500 ứng dụng.
19/6/2009, Apple phát hành IP 3GS, 3GS được nâng RAM lên
256MB, gấp đôi so với iPhone 2G và 3G, máy ảnh tăng lên 3 "chấm".
9/2012, Apple giới thiệu IP 5, màn hình 4.0-inch 1136 x 640 pixel (326
PPI), máy ảnh 8.0, chip lõi kép 1GHz, RAM 1GB. Máy mỏng 7.6mm, có
tích hợp 4G. 5 triệu chiêc ip5 đã được bán sau 3 ngày liên hệ.
13
Android là hệ điều hành mở đầy hấp dẫn của Google.

Hình 1.5. Biểu tượng chú robot xanh hệ điều hành Android

Android là hệ điều hành cho các thiết bị cầm tay dựa trên lõi Linux
do công ty Android Inc (California, Mỹ) thiết kế. Công ty này sau đó
được Google mua lại vào năm 2005 và bắt tay xây dựng Android
Platform.
Android chính thức gia nhập Liên minh thiết bị cầm tay mã nguồn
mở (OHA) gồm các đại gia trong ngành viễn thông và thiết bị cầm tay
như: HTC, LG, Samsung, T-Mobile,...
Windows phone 7
Windows Phone 7 là một hệ điều hành di động được phát triển bởi
Microsoft, và là sự kế thừa để bởi nền tảng Windows Mobile. Nó ra mắt
tại châu Âu, Singapore và Úc vào ngày 21 tháng 10 năm 2010 và tại Mỹ,
Canada vào ngày 08 tháng 11 năm 2010 , với châu Á là trong năm 2011.
Với Windows Phone 7, Microsoft cung cấp một giao diện người dùng
mới gọi là Metro, tích hợp hệ điều hành với các dịch vụ khác của
Microsoft, và kế hoạch kiểm soát chặt chẽ phần cứng mà nó chạy trên
nền tảng này.

14
Chương 2
TỔNG QUAN VỀ HỆ ĐIỀU HÀNH ANDROID

2.1. ANDROID LÀ GÌ?


Android là một hệ điều hành dành cho thiết bị di động như điện thoại,
máy tính bảng và các thiết bị đeo. Android được phát triển bởi Google, dựa
trên nền tảng Linux kernel và các phần mềm nguồn mở. Ban đầu, nó được
phát triển bởi Android Inc (sau đó được Google mua lại) và gần đây nó trở
thành một trong những phần mềm đứng đầu của liên minh OHA (Open
Handset Alliance – với khoảng 78 thành viên bao gồm cả nhà sản xuất, nhà
phát triển ứng dụng,... cho thiết bị di dộng mà dẫn đầu là Google). Andorid
được phát triển nhằm cạnh tranh với các hệ điều hành di động khác như iOS
(Apple), BlackBerry OS, Windows Mobile (Microsoft), Symbian (Nokia),
Bada (Samsung), WebOS (Palm),...
Android có một cộng đồng phát triển ứng dụng rất lớn, tính đến tháng
08/2014 hiện có khoảng hơn 1.4 triệu ứng dụng có sẵn trên Play Store và
đang liên tục được cập nhật. Ứng dụng được phát triển bằng ngôn ngữ Java
kết hợp với thư viện Java có sẵn của Google. Các nhà phát triển ứng dụng có
thể sử dụng máy tính chạy hệ điều hành Windows hoặc MacOS hoặc Linux
kết hợp với Android SDK để phát triển ứng dụng cho Android.

2.2. LỊCH SỬ PHÁT TRIỂN CỦA ANDROID


Tổng công ty Android (Android, Inc.) được thành lập tại Palo Alto,
California vào tháng 10 năm 2003 bởi Andy Rubin (đồng sáng lập công ty
Danger). Dù những người thành lập và nhân viên đều là những người có
tiếng tăm, nhưng tổng công ty Android hoạt động một cách âm thầm, chỉ tiết
lộ rằng họ đang làm phần mềm dành cho điện thoại di động. Google mua lại
Tổng công ty Android vào ngày 17 tháng 8 năm 2005, biến nó thành một bộ
phận trực thuộc Google. Vào thời điểm đó không có nhiều thông tin về
Tổng công ty, nhưng nhiều người đồn đoán rằng Google dự tính tham gia thị
trường điện thoại di động sau bước đi này. Tại Google, nhóm do Rubin
đứng đầu đã phát triển một nền tảng thiết bị di động phát triển trên nền nhân
Linux. Google quảng bá nền tảng này cho các nhà sản xuất điện thoại và
các nhà mạng với lời hứa sẽ cung cấp một hệ thống uyển chuyển và có khả
năng nâng cấp. Google đã liên hệ với hàng loạt hãng phần cứng cũng như
đối tác phần mềm, bắn tin cho các nhà mạng rằng họ sẵn sàng hợp tác với
các cấp độ khác nhau.

15
Ngày 5 tháng 11 năm 2007, Liên minh thiết bị cầm tay mở (Open
Handset Alliance), một hiệp hội bao gồm nhiều công ty trong đó có Texas
Instruments, Tập đoàn Broadcom, Google, HTC, Intel, LG, Tập đoàn
Marvell Technology, Motorola, Nvidia, Qualcomm, Samsung
Electronics, Sprint Nextel và T-Mobile được thành lập với mục đích phát
triển các tiêu chuẩn mở cho thiết bị di động.Cùng ngày, Android cũng được
ra mắt với vai trò là sản phẩm đầu tiên của Liên minh, một nền tảng thiết bị
di động được xây dựng trên nhân Linux phiên bản 2.6. Chiếc điện thoại
chạy Android đầu tiên được bán ra là HTC Dream, phát hành ngày 22 tháng
10 năm 2008. Biểu trưng của hệ điều hành Android mới là một con robot
màu xanh lá cây.
Từ năm 2008, Android đã trải qua nhiều lần cập nhật để dần dần cải
tiến hệ điều hành, bổ sung các tính năng mới và sửa các lỗi trong những lần
phát hành trước. Mỗi bản nâng cấp được đặt tên lần lượt theo thứ tự bảng
chữ cái, theo tên của một món ăn tráng miệng; ví dụ như phiên bản
1.5 Cupcake, sau đó tiếp nối bằng phiên bản 1.6 Donut (bánh vòng). Phiên
bản mới nhất hiện nay là 5.0 Lollipop (kẹo que). Vào năm 2010, Google ra
mắt loạt thiết bị Nexus - một dòng sản phẩm bao gồm điện thoại thông minh
và máy tính bảng chạy hệ điều hành Android, do các đối tác phần cứng sản
xuất. HTC đã hợp tác với Google trong chiếc điện thoại thông minh Nexus
đầu tiên, Nexus One. Kể từ đó, nhiều thiết bị mới hơn đã gia nhập vào dòng
sản phẩm này, như điện thoại Nexus 4 và máy tính bảng Nexus 10, lần lượt
do LG và Samsung sản xuất. Google xem điện thoại và máy tính bảng
Nexus là những thiết bị Android chủ lực của mình, với những tính năng
phần cứng và phần mềm mới nhất của Android.

2.3. CÁC PHIÊN BẢN CỦA ANDROID


Hệ điều hành Android đã trải qua chặng đường 5 năm phát triển,
hàng loạt phiên bản mang nhiều cải tiến ra mắt. Sau đây là các tính năng
chủ chốt trong các phiên bản Android từ khi ra mắt đến nay.

Hình 2.1. Các phiên bản của Android


16
Điểm khởi đầu của Android là Android 1.0.
Kỷ nguyên Android chính thức khởi động vào ngày 22-10-2008,
khi mà chiếc điện thoại HTC Dream G1 chính thức được bán ra ở Mỹ. Ở
giai đoạn này, rất nhiều những tính năng cơ bản cho một smartphone bị
thiếu sót, chẳng hạn như bàn phím ảo, cảm ứng đa điểm hay khả năng
mua ứng dụng. Tuy nhiên, vai trò của phiên bản đầu tiên này vô cùng
quan trọng. Nó đã đặt nền móng cho các tính năng có thể xem là đặc
điểm nhận dạng của Android ngày nay.
Android 1.1 (dành riêng cho T_mobile G1)
Tháng 2/2009, bản nâng cấp đầu tiên của Android được trình làng,
khoảng ba tháng sau khi G1 được bán ra. Phiên bản 1.1 không phải là
một cuộc cách mạng gì to lớn bởi tính năng chính của nó là sửa một danh
sách lỗi khá dài. Tuy nhiên, nó đã cho thấy khả năng nâng cấp thiết bị di
động qua phương pháp Over-The-Air (tức tải về và cài đặt bản cập nhật
ngay trên thiết bị, không cần kết nối với máy tính). Ở thời điểm đó, rất ít
hệ điều hành di động có thể làm được việc này, hầu hết đều phải nhờ đến
một phần mềm chuyên dùng nào đó trên PC. Trước đó, ở Mỹ có dòng
máy Danger Hiptop (được biết nhiều hơn với tên Sidekick) đã có cập
nhật dạng gần giống over the air theo từng giai đoạn, và chính Andy
Rubin, người sáng lập công ty Android Inc. (sau đó Google mua lại)
cũng chính là nhà đồng sáng lập hãng Danger.
Android 1.5: Cupcake (Bánh nướng)
Android 1.5 ra mắt ngày 30-4-2009. Android 1.5 có lẽ có vai trò
cực kỳ quan trọng trong quá trình trưởng thành của Android khi nó bổ
sung cho hệ điều hành này những tính năng nổi bật giúp nó cạnh tranh
với các nền tảng đối thủ khác. Đây cũng là bản Android đầu tiên được
Google gọi tên theo các món đồ ăn với chữ cái bắt đầu được xếp theo thứ
tự alphabet. Android 1.5 không có nhiều điểm thay đổi so với người tiền
nhiệm của mình. Google chỉ điểm thêm vài điểm để làm giao diện trông
bóng bẩy, mượt mà hơn một tí, chẳng hạn như widget tìm kiếm có độ
trong suốt nhẹ, biểu tượng app drawer có một số hoa văn nhỏ mới,… Nói
chung, giao diện không phải là một điểm nhấn của Android 1.5 mà người
ta quan tâm nhiều hơn đến các tính năng mới mà nó mang lại, chẳng hạn
như: bàn phím ảo, cải tiến clipboard, khả năng quay phim, mở rộng khả
năng cho widget,…
Android 1.6: Donut (bánh rán)
Andoid 1.6 ra mắt ngày 30-9-2009. Mặc dù chỉ thêm có 0.1 vào mã
số của Android 1.5 nhưng nó cũng mang lại nhiều cải tiến đáng giá. Một
vài điểm trong giao diện được cải thiện, vài tính năng nhỏ được thêm
17
vào, cuối cùng là hỗ trợ cho mạng CDMA. Động thái này cho phép nhiều
nhà mạng hơn có thể sử dụng với Android, giúp cho Android có thêm
một số lượng lớn người dùng ở Mỹ và ở cả Châu Á nữa. Nhưng có lẽ
điểm thú vị nhất của Donut đó là hỗ trợ các thành phần đồ họa độc lập
với độ phân giải. Lần đầu tiên, Android có thể chạy trên nhiều độ phân
giải và tỷ lệ màn hình khá nhau, cho phép những thiết bị có nhiều độ
phân giải hơn là 320 x 480.
Android 2.0: Eclair
Android 2.0 ra mắt ngày 26-10-2009. Chỉ sau gần một tháng ra mắt
Donut (Android 1.6), Google tung ra Eclair, phiên bản được nhận định là
‘‘bước đi lớn’’ của hệ điều hành này. Eclair cải tiến rất nhiều, từ giao
diện đến ứng dụng bên trong hệ thống. Ứng dụng chụp ảnh tăng cường
thêm chức năng zoom số (phóng to), cân bằng trắng, hỗ trợ đèn flash và
các hiệu ứng màu sắc.
Hệ thống hoạt động ổn định hơn, cải thiện khả năng xử lý, hỗ trợ
kết nối Bluetooth tốt hơn, đặc biệt tùy chọn đồng bộ nhiều tài khoản. Một
điểm thuận tiện được đánh giá cao lúc bấy giờ là giao diện danh bạ cho
phép nhấn chọn vào một ảnh danh bạ để gọi, nhắn tin hay email đến họ.
Giao diện ứng dụng lịch biểu (Calendar) cũng thay đổi. Eclair là phiên
bản Android đầu tiên hỗ trợ ảnh nền động (live wallpaper) dù tùy chọn
này tiêu tốn khá nhiều pin.
Android 2.2: Froyo
Android 2.2 ra mắt ngày 20-5-2010. Từ phiên bản 2.0 trở đi,
Android dần hoàn thiện hơn. Phiên bản 2.2 (Froyo) mang Adobe Flash
đến Android, kéo theo hàng loạt ứng dụng và game trên nền Flash. Người
dùng cũng có thể xem video clip nền Flash như YouTube và ra lệnh thực
hiện cuộc gọi qua Bluetooth. Một chức năng mới trong Froyo được nhóm
người dùng lưu động yêu thích là USB Tethering và Wi-Fi Hotspot, biến
chiếc smartphone Android thành thiết bị phát sóng Wi-Fi từ kết nối 3G.
Tính năng này được sử dụng rất phổ biến đến ngày nay. Lần đầu tiên
Android cho phép cài đặt ứng dụng (app) lên thẻ nhớ SD thay vì mặc
định cài ngay vào bộ nhớ trong của thiết bị. Điểm đầu tiên nữa trong
Froyo bao gồm mật khẩu đã hỗ trợ số và chữ số. Thiết bị đầu tiên mang
nhãn Froyo ra mắt thị trường là HTC Nexus One.
Android 2.3: Gingerbread (bánh gừng)
Android 2.3 ra mắt ngày 6-12-2010. Đến cuối năm 2012,
Gingerbread vẫn đang phủ sóng trên rất nhiều thiết bị dùng Android,
chiếm đến hơn phân nửa (54%). Google hợp tác Samsung trình làng dòng
smartphone đầu tiên sử dụng Gingerbread mang tên Nexus S, hỗ trợ công
18
nghệ giao tiếp tầm gần NFC. Gingerbread đưa vào hệ thống một công cụ
quản lý tải tập tin, cho phép theo dõi và truy xuất đến các tập tin đã tải về
máy. Hệ thống này hỗ trợ nhiều camera cho các thiết bị có camera mặt
sau và trước, quản lý nguồn pin hiệu quả hơn, tiết kiệm thời lượng pin.
Phiên bản này khắc phục khá nhiều lỗi từ Froyo, kèm theo một số điều
chỉnh trong giao diện người dùng (UI).
Android 3.0: Honeycomb
Android 3.0 ra mắt ngày 22-2-2011. Đây không chỉ là một phiên
bản, mà có thể xem là một thế hệ Android đầu tiên dành riêng cho máy
tính bảng (tablet), ra mắt cùng tablet Motorola XOOM. Mang những tính
năng từ thế hệ Android 2.x, Android 3.0 cải tiến giao diện phù hợp với
cách sử dụng máy tính bảng, bàn phím ảo thân thiện hơn, hỗ trợ xử lý đa
tác vụ (multi-tasking), cho phép chuyển đổi qua lại các ứng dụng đang
cùng chạy. Không chỉ có bề mặt được trau chuốt, phần lõi hệ thống có
các cải tiến tương thích với phần cứng như hỗ trợ chip xử lý (CPU) đa
lõi, tăng tốc phần cứng,…
Android 3.0 đặt nền móng quan trọng cho thế hệ Android 4.x hợp
nhất, khắc phục sự phân mảng của Android (có các phiên bản riêng dành
cho smartphone và tablet).
Sau Android 3.0, Android 3.1 và Android 3.2 là hai bản nâng cấp
nhỏ của Honeycomb và vẫn dùng lại cái tên này. Mục đích chủ yếu của
chúng là để sửa lỗi và thêm vài tính năng mới như resize widget ngay
trên homescreen, hỗ trợ thẻ SD,…
Android 4.0: Ice Cream Sandwich (Bánh kẹp kem)
Android 4.0 ra mắt ngày 19-10-2011. Ice Cream Sandwich(ICS) là
thế hệ Android được mong đợi nhất đến thời điểm đó, ra đời cùng dòng
smartphone bom tấn Samsung Galaxy Nexus, thế hệ smartphone đầu tiên
trang bị ICS. Android 4.0 đưa chức năng truy xuất nhanh các ứng dụng
thường dùng vào phần bên dưới giao diện chủ, tùy biến widget, dễ sắp
xếp và duyệt danh sách ứng dụng hơn. Các ứng dụng đã có thể truy xuất
nhanh từ màn hình khóa thiết bị (Lock screen). Ice Cream Sandwich hoạt
động mượt mà, nhanh và đẹp hơn.
Android 4.1: Jelly Bean
Android 4.1 ra mắt ngày 9-7-2012. Máy tính bảng Nexus 7, sản
phẩm hợp tác giữa Google và Asus, là thiết bị dùng Jelly Bean đầu tiên ra
mắt. Android 4.1 nâng tầm hoạt động cho hệ điều hành của Google, trở
thành hệ điều hành cho thiết bị di động hàng đầu hiện nay, đe dọa cả
“ông lớn” Windows. Khả năng sắp xếp giao diện chủ và widget trong
19
Jelly Bean rất tùy biến và linh hoạt. Jelly Bean giới thiệu Google Now,
dịch vụ trực tuyến mới hiện chỉ dành cho Android, một phụ tá ảo đắc lực
cho công việc sắp xếp lịch trình, tìm kiếm thông tin, xác định vị trí… Rất
đa năng và được xem như lời đáp trả của Google với phụ tá ảo Apple Siri
trong iOS
Android 4.2: vẫn là Jelly Bean
Android 4.2 ra mắt tháng 11-2012. Chỉ sau gần năm tháng ra mắt
Android 4.1, Google tiếp tục “bồi” thêm sức nặng cho Android với phiên
bản 4.2 và vẫn mang tên mã Jelly Bean.
Android 4.2 tiếp tục mang đến những cải tiến hấp dẫn cho ứng
dụng chụp ảnh (Camera) như: HDR, Photo Sphere, hiệu ứng ảnh, Google
Now, đưa tính năng lướt chọn từ rất hay trong bàn phím ảo. Chức năng
“bom tấn” hỗ trợ nhiều tài khoản người dùng (multi-user profile) lần đầu
tiên được áp dụng trong Android 4.2 nhưng chỉ có người dùng máy tính
bảng thừa hưởng chức năng này.
Android 4.3: vẫn là Jelly Bean
Công bố (24/7/2013), phiên bản hệ điều hành di động Android 4.3
chính thức được cung cấp đến các thiết bị Nexus 4, Nexus 7, Nexus 10
thông qua giao thức OTA. Không có thay đổi lớn về cả giao diện lẫn cấu
trúc so với Android 4.1 và 4.2 trước đó, phiên bản 4.3 có thể coi là một
bản nâng cấp nhỏ mà Google dành cho hệ điều hành của hãng. Một số
nâng cấp ở phiên bản này là: tạo tài khoản giới hạn quyền sử dụng, nâng
cấp trình chụp ảnh, bluetooth thông minh tiết kiệm năng lượng, cải tiến
tính năng thông báo notifications, tích hợp Google Keep (Ghi chú đồng
bộ đám mây),…
Trong đó, tính năng bluetooth thông minh như sau:
Phiên bản mới nhất của chuẩn kết nối Bluetooth là Bluetooth 4.0.
Hiệp hội Bluetooth đã phân biệt có hai loại thiết bị sử dụng Bluetooth 4.0
là Bluetooth Smart Ready và Bluetooth Smart. Bluetooth Smart là những
thiết bị ngoại vi như đồng hồ đo nhịp tim, máy chăm sóc sức khỏe,…
Bluetooth Smart Ready là những thiết bị có khả năng kết nối đến các
thiết bị Bluetooth Smart, đồng thời vẫn có các tính năng quen thuộc như
truyền tập tin, kết nối tai nghe,…

20
Hình 2.2. Kết nối bluetooth thông minh
iPhone, iPad, Samsung Galaxy SIII, Motorola Droid RAZR,… là
những thiết bị đã hỗ trợ Bluetooth Smart Ready. Ở Android 4.3, hai dòng
thiết bị Nexus 4 và Nexus 7 mới (phiên bản 2013) cũng có khả năng hỗ
trợ công nghệ Bluetooth Smart Ready (còn gọi là Bluetooth Low-
Energy). Nhờ vậy, hai thiết bị này có thể giao tiếp với các thiết bị ngoại
vi sử dụng Bluetooth Smart như máy đo nhịp tim, hay đồng hộ đếm bước
chạy bộ thông qua kết nối Bluetooth 4.0, với điểm mạnh là không gây
hao tốn nhiều dung lượng pin khi sử dụng. Thêm một cải tiến về kết nối
Bluetooth, khi Android 4.3 hỗ trợ profile AVRCP 1.3, cho phép truyền
thông tin về tên bài hát lên các thiết bị phát nhạc từ xa thông qua kết nối
Bluetooth, như dàn âm thanh trong xe hơi, hay loa Bluetooth.
Android 4.4: KitKat
Trong sự kiện diễn ra vào sáng 1/11 (theo giờ VN), Google đã công
bố nền tảng di động mới nhất của mình, bản Android 4.4 với tên mã là
KitKat. Theo GSMArena, Android 4.4 đã được Google cải tiến giao diện
làm việc với thanh trạng thái và thông báo ở cạnh trên màn hình sẽ hiển thị
trong suốt, nhờ thế khi chạy một ứng dụng thông báo hiện ra sẽ "hòa màu"
vào nền của ứng dụng chứ không còn là một màu đen nữa. Ngoài ra, phiên
bản mới còn cải thiện khả năng chạy đa nhiệm bằng cách nâng cấp việc
quản lý bộ nhớ và tăng cường độ phản hồi của màn hình cảm ứng.
Android 4.4 còn bổ sung chế độ chụp ảnh HDR+ mới, cho phép
người dùng chụp nhiều tấm ảnh với độ phơi sáng khác nhau rồi ghép lại
thành ảnh duy nhất để hiển thị rõ chi tiết cả trong vùng sáng lẫn trong tối.
Nền tảng mới này của Google còn hỗ trợ khả năng in ảnh, tài liệu
và trang web ngay từ điện thoại và máy tính bảng một cách nhanh chóng
và dễ dàng với giao diện trực quan hơn.
Đặc biệt, Android 4.4 có khả năng hỗ trợ các dòng máy Android
cũ, chỉ cần yêu cầu cấu hình phần cứng RAM là 512MB.
21
Ngoài ra, ứng dụng Hangouts cũng được tích hợp sâu vào Android
4.4 với việc cải tiến Hangouts và Messaging làm thành tính năng duy
nhất trên máy. Nhờ vậy, người dùng chỉ cần sử dụng ứng dụng Hangouts
để nhắn tin với bạn bè theo giao thức SMS/MMS hoặc OTT và hỗ trợ
khả năng trò chuyện bằng video.
Theo công bố của Google, Android 4.4 được cài sẵn trong máy
Nexus 5 vừa được giới thiệu. Sau đó, bản cập nhật Android 4.4 đầu tiên
sẽ được trình làng trong vài tuần tới và các dòng máy smartphone đầu
tiên có thể lên nền tảng mới nhất này là: Nexus 4, Nexus 7, Nexus 10,
Galaxy S4 Google Edition và HTC One Google Edition.
Android 5.0 Lollipop
Ngày 25/06/2014, tại sự kiện Google I/O, Google công bố Material
Design - phong cách thiết kế mới dành cho các phần mềm và dịch vụ của
hãng như Android, Chrome, Gmail,...
Trong đó, hệ điều hành Android L Preview (bản dành cho chuyên gia
phát triển) có giao diện phẳng, nhất quán và đẹp hơn với nhiều màu sắc tươi
sáng và phần nào gợi nhớ đến iOS và Windows Phone. Các biểu tượng (icon)
cũng được chuyển từ vuông sang tròn, trong khi hệ thống nhắc báo đã được
cải tiến. Giới phân tích đánh giá đây là bước tiến đáng khen ngợi của Google
bởi thiết kế vốn không phải điểm mạnh của hãng này.

Hình 2.3. Giao diện của Android L


Matias Duarte, trưởng nhóm thiết kế Android, cho hay một điểm
nhấn của bản L (chính thức là Android Lollipop) là hiệu năng. Android
L là thế hệ Android đầu tiên chính thức dùng trình phiên dịch ART
(Android Runtime) thay vì Dalvik như trong các phiên bản Android trước
đó (bản thử nghiệm của ART đã được nhúng trong Android 4.4).
Đối với giới phát triển, đây là một sự chuyển đổi lớn, còn với người
dùng Android phổ thông, họ chỉ cần biết rằng các ứng dụng Android thời gian
22
tới sẽ chạy mượt mà và cải thiện đáng kể thời lượng pin. Chuyên gia Dave
Burke của Google khẳng định ART sẽ hoạt động nhanh gấp hai lần Dalvik và
hỗ trợ quản lý bộ nhớ, tài nguyên máy hiệu quả hơn.
Liên quan tới thời lượng pin, Android L cũng bổ sung nhiều chế độ
linh hoạt để người sử dụng có thể kiểm soát thời gian sử dụng pin dễ
dàng hơn.
[1] Trong suốt 5 năm ra đời và phát triển, hầu hết mọi thành phần
của Android đều đã ít nhiều được thay thế, cập nhật mới hay thậm chí là
thay đổi hoàn toàn. Tuy nhiên, có một thứ cực kỳ quan trọng, đó là bộ
máy ảo Dalvik dùng để chạy ứng dụng Android thì vẫn được giữ nguyên
gần như so với ban đầu. Giờ đây, trong Android 5.0, Google đã ra mắt
một giải pháp thay thế cho anh chàng Dalvik già cỗi, đó chính là Android
Runtime (ART).
[1] Nội dung một số phần được tham khảo ở bài tham luận trên
trang http://tinhte.vn
Dalvik và ART là gì?
Hiện nay, các ứng dụng Java sau khi viết xong chỉ được biên dịch
(compile) một phần bởi lập trình viên. Phần mã biên dịch này sau đó sẽ
phải đi qua một trình phiên dịch khác để trở thành mã máy (native code)
phù hợp với CPU và thiết bị của người dùng. Quá trình này tốn thời gian
hơn và không thực sự hiệu quả, nhưng bù lại lập trình viên có thể viết
app chỉ một lần và đem nó lên nhiều máy để chạy.

Hình 2.4. Các công đoạn phát triển ứng dụng


Trên Windows, Mac hay Linux, chúng ta có một thứ gọi là Java
Virtual Machine để thực hiện công việc trên. Còn với Android, Dalvik
chính là trình biên dịch và nó đã có mặt từ những buổi đầu mà hệ điều
23
hành này ra đời. Dalvik do kỹ sư Dan Bornstein làm ra và nó được đặt
tên theo một làng chài ở Iceland. Đây thực chất là một phần mềm mã
nguồn mở và nó nằm ở trong khu vực bao gồm các thư viện cốt lõi cần
để Android chạy. Ngoài ứng dụng thì vài thành phần khác của Android
cũng phải dùng đến Dalvik.
Chính nhờ Dalvik mà chúng ta chỉ có một app duy nhất nhưng vẫn có
thể chạy trên smartphone, tablet, Smart TV, thiết bị nhúng,... với nhiều loại
CPU khác nhau và từ các hãng khác nhau (tất nhiên việc tối ưu hóa giao
diện, tính năng là câu chuyện khác, nhưng cơ bản là chạy được).
Vấn đề với việc dịch bằng Dalvik đó là nó chậm! Chính sự xuất
hiện của nó thường làm cho hiệu năng ứng dụng thấp hơn so với các
phần mềm dùng trực tiếp mã đã được biên dịch sẵn. Trong nhiều trường
hợp, nhất là với những CPU hiện đại có sức mạnh xử lý tốt thì sự khác
biệt này không đáng kể. Tuy nhiên, với những phần mềm nặng thì chúng
ta sẽ thấy nó một cách rõ rệt. Android hiện nay chậm chạp cũng một
phần là do Dalvik. Và việc bắt vi xử lý chạy lâu hơn cũng là nguyên nhân
khiến hệ thống tiêu thụ nhiều điện hơn.
Android Runtime – ART
Google biết về Dalvik, tất nhiên, đó là lý do mà hãng đã phát triển
nên một giải pháp thay thế mang tên Android Runtime (ART) trong suốt
hai năm vừa qua. Một phiên bản thử nghiệm của ART đã được nhúng
trong Android 4.4 và bạn có thể tìm thấy nó ở trong phần Settings >
Developer Options > Select Runtime (một số máy dùng ROM cook có
thể không có, và việc kích hoạt nó lên có thể khiến hệ thống và ứng dụng
gặp lỗi đấy nhé).

Hình 2.5. Kích hoạt ART trong Android 4.4


24
ART sử dụng một cách xử lí rất khác so với Dalvik. Hiện nay, các
ứng dụng Android được phiên dịch ra mã máy ở thời điểm mà chúng
ta chạy chúng lên. Dalvik thực hiện điều này bằng một bộ máy gọi là
"Just-In-Time" (JIT) conpiler. Chữ Just-In-Time dịch sang tiếng Việt thì
có nghĩa là "vừa kịp lúc", tức là chỉ đoạn mã nào cần thiết mới được dịch
và chỉ dịch khi cần. Còn với ART, nó dùng bộ máy "Ahead-Of-Time"
(AOT) để phiên dịch mã bytecode của ứng dụng thành mã máy ở thời
điểm bạn cài nó vào thiết bị. Điều này đồng nghĩa với việc khi bạn chạy
app lên, nó đã tồn tại sẵn ở dạng mã máy và thiết bị của chúng ta cứ thế
mà thực thi app.
Lợi ích của ART
Lợi ích lớn nhất của ART đó là nó cho phép lập trình viên Android
tiếp tục viết phần mềm theo như cách mà họ đã làm từ trước đến nay,
phần mềm của họ cũng vẫn có thể chạy trên nhiều loại CPU và thiết bị
khác nhau, nhưng với tốc độ nhanh hơn đáng kể. Cảm giác máy sẽ mượt
mà hơn. Với các phần mềm cần nhiều sức mạnh tính toán, ví dụ app
chỉnh sửa ảnh hay video thì lợi ích sẽ càng rõ ràng hơn nữa. Chưa hết,
hiện nay hầu hết thiết bị Android đều sử dụng vi xử lý đa nhân (hai, bốn,
tám,...). Sự có mặt của ART có thể giúp ứng dụng cần phải kích hoạt ít
nhân hơn Dalvik lúc phiên dịch, từ đó dẫn đến việc tiết kiệm pin hơn.
Chúng ta cũng có thể thấy được ART là một trong những lý do mà
Google tuyên bố là Android 4.4 có thể dùng tốt cho những thiết bị với
RAM chỉ 512MB. Vì đã được phiên dịch trước nên không còn hiện
tượng Dalvik chiếm lấy RAM và bộ nhớ này sẽ được sử dụng hoàn toàn
cho các ứng dụng.
Hạn chế của ART
Tất nhiên, ART sẽ cần thời gian lâu hơn để dịch lúc mà chúng ta
mới vừa cài ứng dụng xong, tuy nhiên lợi ích lâu dài đó là ứng dụng sẽ
tải và chạy nhanh hơn. Bạn hãy thử nghĩ xem, chúng ta chỉ cần app có
một lần nên chờ lâu hơn hồi trước một chút thì cũng không vấn đề gì,
trong khi tốc độ dùng app về sau nhanh hơn thì cũng đáng để bù đắp.
Thực chất, với những ứng dụng nhỏ thì khoảng thời gian để ART dịch
cũng rất ngắn, chỉ với những app lớn thì chúng ta mới thấy rõ sự khác
biệt. Hiện nay, với những chiếc Nexus, lúc bạn chuyển từ Dalvik sang
ART thì toàn hệ thống cũng chỉ mấy có vài phút để dịch sẵn hết toàn bộ
app đã cài trong máy.
Ngoài ra, một hạn chế khác đó là mã máy sau khi được dịch sẵn
sàng thì sẽ chiếm dung lượng lưu trữ lớn hơn là mã bytecode. Lý do đó là
vì một ký tự trong bytecode thực chất được biểu diễn bằng nhiều lệnh
25
của mã máy nên to hơn là chuyện dễ hiểu. Mức độ lớn hơn thì cao nhất là
khoảng 10-20% tùy app. Con số này nghe có vẻ lớn nhưng tập tin thực
thi lại không chiếm dung lượng lớn trong app nên việc tăng lên như thế
sẽ không gây nhiều ảnh hưởng. Ví dụ, cả file APK của ứng dụng
Google+ mới có dung lượng đến 28,3MB, nhưng phần mã chạy chỉ là
6,9MB mà thôi.

Hình 2.5. Sự chênh lệch về dung lượng ứng dụng giữa Dalvik và ART

ART hiện chỉ mới là bản thử nghiệm sơ khai


Xin nói lại rằng bản ART đang có mặt trong Android
4.4 KitKat chỉ mới là bản thử nghiệm, do đó tốc độ chưa thật sự tốt như
bản chất của nó.
Chưa rõ bao giờ thì Google sẽ ra mắt chính thức ART, nhưng
trong bối cảnh hãng đã phát triển nó hàng năm trời và đã thử nghiệm
trên Android 4.4, có thể chúng ta sẽ sớm thấy nó được áp dụng rộng rãi.
Nhiều khả năng là trong Android 4.5 hay 5.0 thì Google sẽ tích hợp
ART. Android vốn bị chỉ trích là hệ điều hành dễ dính mã độc và
Google khẳng định nền tảng mới đã được phát triển an toàn hơn. Ngoài
26
ra, họ cũng sẽ cung cấp các bản vá bảo mật định kỳ 6 tuần một lần trực
tiếp tới thiết bị của người sử dụng thay vì phải thông qua bản cập nhật
của nhà mạng hay nhà sản xuất. Android L Preview bắt đầu được cho
phép tải còn bản chính thức sẽ xuất hiện trong mùa thu. Vào thời điểm
quyển sách này được chỉnh sửa thì Android L đã chính thức ra mắt với
tên Lollipop.

2.4. ƯU - NHƯỢC ĐIỂM CỦA ANDROID


 Ưu điểm của Android
Tính linh hoạt: Android kế thừa tính mở từ Linux, hay nói cụ thể
hơn là Google và Android mang đến một thế giới hoàn toàn mở. Với
Apple, người dùng dường như bị cột chặt với những gì hãng này cho
phép, từ tính năng chuẩn cho đến ứng dụng – chỉ những ứng dụng được
Apple công nhận mới có thể hoạt động trên iPhone một cách hợp pháp.
Apple tin rằng đó là cách để họ kiểm soát cả guồng máy kinh doanh, từ
thiết bị đầu cuối, dịch vụ cho đến ứng dụng bổ sung. Trong khi đó, với
thế giới Android, người dùng được tự do với những gì họ muốn, các nhà
phát triển có được sự tự do hơn để tạo và thương mại các ứng dụng, và
các nhà sản xuất có thể tùy biến lại những trải nghiệm Android cho
khách hàng của riêng mình.
Gia tăng về số lượng thiết bị: Một thực tế là, chỉ có iPhone và các
sản phẩm mang nhãn Apple mới sử dụng iOS. Đó có thể là sản phẩm tốt
nhất thế giới, nhưng nếu chỉ có duy nhất một thiết bị như thế thì chắc
chắn rằng khách hàng sẽ không sớm thì muộn cảm thấy họ có quá ít lựa
chọn. Với Android, sự lựa chọn có phần thoải mái hơn.
Nhiều công cụ dành cho người dùng hơn: Với App Inventor,
Google đã thậm chí đặt vào tay người dùng nhiều sức mạnh hơn. Với rất
nhiều bộ công cụ phát triển dạng tự tay thực hiện (Do-It-Yourself) được
Google và các đối tác cung cấp, chưa bao giờ việc tạo ứng dụng lại dễ
dàng đến thế với người dùng và kể các lập trình viên. Tuy nhiên, nhiều
người lại cho rằng điều này sẽ khiến cho Android tràn ngập ứng dụng
“rác”, nhưng chắc chắn một điều rằng, trong số đó sẽ có vài ứng dụng
đáng đồng tiền bát gạo.
Hiệu ứng từ thương hiệu Google: Rõ ràng là Android không hề
đơn độc bởi hiện có vài HĐH trên nền Linux khác cũng đang làm mưa
làm gió trên vùng đất di động, điển hình là Bada của Samsung hay
MeeGo của liên doanh Nokia – Intel. Điểm khác biệt nằm ở chỗ Android
được Google “chống lưng” và tiền tố Google đi cùng với Android là một
thương hiệu đáng giá.
27
 Nhược điểm của Android
Thời lượng sử dụng pin thấp: Hầu hết các thiết bị Android đều có
thời lượng pin thấp và đây là một trong những vấn đề quan trọng mà
Google và các nhà sản xuất thiết bị Android đang chú trọng giải quyết.
Android đa năng nhưng khó kích hoạt: Khi trải nghiệm Android
lần đầu, bạn sẽ thấy nhiều thứ đang hoạt động. Có hàng loạt widget, ứng
dụng và lựa chọn mà bạn chưa hề biết đến. Chẳng hạn như Galaxy S III sở
hữu những tính năng độc đáo mà iPhone không thể làm được. Đáng tiếc,
bạn cũng phải tùy chỉnh không ít cài đặt nếu muốn kích hoạt tính năng.
Chúng là điều khó khăn đối với người mới dùng Android
Nội dung giải trí: Google đang không ngừng cải tiến nội dung của
Play Store, cung cấp chương trình giải trí truyền hình và thêm nhiều
phim cho thuê trên thiết bị Android… Nhưng so sánh với iTunes, sự lựa
chọn vẫn còn thua xa những gì bạn nhận được trên iOS. Khi nhắc đến
lĩnh vực âm nhạc, Google Play còn thiếu vắng nội dung của Warner, một
trong bốn hãng ghi âm lớn nhất hành tinh.
Phụ kiện cho thiết bị Android chưa thực sự tốt: Dễ nhận thấy,
giới sản xuất phụ kiện dường như kém quan tâm đến Android. Trên thị
trường có rất nhiều phụ kiện độc dành cho iPhone, không những hỗ trợ
chụp ảnh tốt mà còn tăng cường dung lượng pin. Nghĩ một cách đơn
giản, dòng điện thoại Android không có được sự nhiệt tình tương tự
từ đối tác làm phụ kiện
Dễ nhiễm phần mềm độc hại và virus: Android hoạt động
như hệ điều hành mở và xuất hiện những kho ứng dụng không chính
thức. Bởi vậy, thiết bị Android dễ nhiễm malware hoặc ứng dụng giả
mạo. Gần đây nhất, một virus đánh cắp thông tin qua SMS đã xuất hiện
trên kho ứng dụng Android tại Trung Quốc. Ước tính có hơn 500.000
người bị lây nhiễm loại virus này.

2.5. GIỚI THIỆU VỀ NỀN TẢNG CỦA ANDROID


a) Giới hạn của thiết bị cầm tay
Trong khi các thiết bị cầm tay như điện thoại di động, máy tính
bảng đem lại nhiều tiện lợi cho người sử dụng, thì nó vẫn còn tồn tại
nhiều mặt hạn chế. Những giới hạn cơ bản là:
Bộ nhớ giới hạn: Hầu hết các thiết bị cầm tay đều có bộ nhớ lưu trữ
giới hạn. Tuy nhiên, với nhiều nỗ lực của các tập đoàn sản xuất công
nghệ bán dẫn, giới hạn này sẽ đươc giải quyết trong tương lai không xa.

28
Năng lượng xử lý giới hạn: rất nhiều dịch vụ trên internet như
game, nhạc, video đòi hỏi phải có bộ xử lý nhanh. Nhưng khả năng xử lý
của các thiết bị cầm tay vẫn còn rất hạn chế.
Nguồn năng lượng: Việc thường xuyên thay đổi vị trí của các thiết
bị làm tiêu hao năng lượng nhanh chóng. Rất nhiều nghiên cứu phát minh
ra những nguồn năng lượng mới có chu kỳ sống lâu hơn chẳng hạn như
fuel cell.
Công nghệ và nền tảng khác nhau: Mỗi thiết bị được xây dựng trên
những nền tảng và tiêu chuẩn riêng mà nhà sản xuất quy định. Đó là hạn
chế bởi nó giới hạn khả năng tương thích và giao tiếp giữa các thiết bị.
Bàn phím nhỏ, cách nhập dữ liệu khác biệt: Bàn phím của các thiết
bị cầm tay thường nhỏ hoặc ít phím. Điều này làm cho viêc sử dụng các
dịch vụ chậm chạp và khó khăn hơn.
Giao diện người dùng đơn giản, kích thước nhỏ gọn nên giao diện
ứng dụng của các thiết bị cầm tay cũng rất đơn giản.
Băng thông giới hạn: Khi số lượng người sử dụng thiết bị cầm tay
ngày càng tăng thì nhu cầu sử dụng băng thông mạng cũng tăng theo
b) Kiến trúc nền tảng

Hình 2.6. Cấu trúc Stack (ngăn xếp) của hệ thống Android

29
Tầng ứng dụng (Applycations)
Tích hợp sẵn một số ứng dụng cần thiết cơ bản như: contacts,
phone, browser, camera,… Tất cả ứng dụng chạy trên Android đều được
viết trên nền tảng Java.
Các ứng dụng được cài đặt như các phần mềm chứng khoán
(Stock), các trò chơi (Game), từ điển,…
Các chương trình có các đặc điểm là:
+ Viết bằng Java, phần mở rộng là apk.
+ Khi mỗi ứng dụng được chạy, nó có một phiên bản Virtual
Machine được dựng lên để phục vụ hệ thống. Nó có thể là một Active
Program: chương trình có giao diện với người sử dụng hoặc là một
background: chương trình chạy nền hay là dịch vụ.
+ Android là hệ điều hành đa nhiệm, điều đó có nghĩa là trong
cùng một thời điểm, có thể có nhiều chương trình cùng chạy một lúc, tuy
nhiên, với mỗi ứng dụng thì có duy nhất một thực thể (instance) được
phép chạy mà thôi. Điều đó có tác dụng hạn chế sự lạm dụng tài nguyên,
giúp hệ thống hoạt động tốt hơn.
+ Các ứng dụng được gán số ID của người sử dụng nhằm phân
định quyền hạn khi sử dụng tài nguyên, cấu hình phần cứng và hệ thống.
+ Android là một hệ điều hành có tính mở, khác với nhiều hệ điều
hành di động khác, android cho phép một ứng dụng của bên thứ ba được
phép chạy nền. Các ứng dụng đó chỉ có một hạn chế nhỏ đó là nó không
được phép sử dụng quá 5~10% công suất CPU, điều đó nhằm để tránh
độc quyền trong việc sử dụng CPU.
+ Ứng dụng không có điểm vào cố định, không có phương thức
main để bắt đầu.
Applycations Framework
Bằng việc phát triển trên nền tảng mã nguồn mở (Open source
code), Android cung cấp cho các nhà phát triển phần mềm khả năng xây
dựng các ứng dụng cực kỳ phong phú và sinh động và sáng tạo. Họ được
tự do tận dụng các tài nguyên về thiết bị phần cứng, thông tin địa điểm
truy cập, các dịch vụ chạy (services run), các thiết lập báo cáo, thông
báo, trạng thái,… Nhà phát triển có thể truy cập vào các hàm API cùng
một khuôn khổ được sử dụng bởi các ứng dụng lõi. Các kiến trúc được
thiết kế đơn giản hóa việc sử dụng lại các thành phần.
Tầng Applycation Framework bao gồm nhiều dịch vụ (services)
cho việc quản lý:
30
+ Activity Manager: quản lý vòng đời (lifecycle) của các ứng
dụng điều hướng cho các avtivity.
+ Window Manager: cung cấp khả năng quản lý giao diện người
sử dụng.
+ View System: tập hợp rất nhiều các View có khả năng kế thừa
lẫn nhau để thiết kế phần giao diện ứng dụng như: TextView, EditText,
GirdView, TableView,…
+ Content Providers: cho phép các ứng dụng truy xuất dữ liệu từ
các ứng dụng khác (tính kế thừa), ví dụ như ứng dụng Phone sẽ truy xuất
dữ liệu thông tin về số điện thoại của người được gọiđược chứa trong
ứng dụng Contact.
+ Resource Manager: cung cấp truy xuất tới các tài nguyên không
phải là mà nguồn (source code), chẳng hạn như graphics, layout …
+ Location Manager, Notifycation Manager: cho phép tất cả
ứng dụng có thể hiển thị các loại thông báo khác nhau (custom arlets)
trong status bar.
+ Telephony Manager: dịch vụ thoại (Phone’s services), cho phép
các ứng dụng thông qua dịch vụ này truy xuất các thao tác liên quan đến
điện thoại, ví dụ như thực hiện một cuộc gọi điện thoại,…
+ Package Manager: quản lý các gói ứng dụng, các chương trình
đã cài đặt, các thư viện.
Tầng thư viện (Libraries)
Bao gồm một tập hợp các thư viện C/C++ được sử dụng bởi các
thành phần khác nhau trong hệ thống Android. Một số các thư viện cơ
bản được liệt kê dưới đây:
+ System C library: sử dụng hệ thống C chuẩn, được điều hưởng
cho những thiết bị nền tảng Linux nhúng.
+ Media Framework: dự trên nền tảng PacketVideo’s OpenCore,
các thư viện hỗ trợ phát và ghi cho các định dạng âm thanh, hình ảnh
thông dụng bao gồm MPEG4, H.264, MP3, AAC, AMR, JPG, và PNG.
+ Surface Manager: quản lý việc truy xuất vào hệ thống hiển thị.
+ Webkit: cung cấp kỹ thuật duyệt web hiện đại thông qua việc
kết hợp sức mạnh giữa trình duyệt web của Android với một trang web
nhúng.
+ SGL: cung cấp các công cụ đồ họa 2D.

31
+ OpenGL/ES: thi hành các hàm API dựa trên thư viện đồ họa
OpenGL / ES 1.0, cung cấp công cụ đồ họa 3D đối với phần cứng được
hỗ trợ và phần mềm.
+ FreeType: bộ tạo phông chữ dạng bitmap và vector.
+ SQLite: hệ quản lý cơ sở dữ liệu cho các ứng dụng với đặc điểm
chạy nhẹ nhàng và việc quản lý mạnh.
Android Routine
Phần này chứa các thư viện mà một chương trình viết bằng ngôn
ngữ Java có thể hoạt động. Phần này có hai bộ phận tương tự như mô
hình chạy Java trên máy tính thường. Thứ nhất là các thư viện lõi (Core
Library), chứa các lớp như JAVA IO, Collections, File Access .Thứ hai
là một máy ảo java (Dalvik Virtual Machine).
Mặc dù cũng được viết từ ngôn ngữ Java nhưng một ứng dụng Java
của hệ điều hành android không được chạy bằng JRE của Sun (nay là
Oracle) (JVM) mà là chạy bằng máy ảo Dalvik do Google phát triển.
Các VM Dalvik thực thi các tập tin thực thi Dalvik (dex). Định dạng
được tối ưu hóa cho bộ nhớ tối thiểu. VM là dựa trên nền tảng thanh ghi,
và chạy các lớp đã được biên dịch bởi một trình biên dịch Java để chuyển
đổi thành các định dạng dex. Các VM Dalvik dựa vào nhân Linux cho
các chức năng cơ bản như luồng và quản lý bộ nhớ thấp.
Tầng hạt nhân Linux (Linux Kernel Linux)
Hệ điều hành android được phát trển dựa trên hạt nhân linux, cụ thể
là hạt nhân linux phiên bản 2.6, điều đó được thể hiện ở lớp dưới cùng
này. Tất cả mọi hoạt động của điện thoại muốn thi hành được thì đều
được thực hiện ở mức cấp thấp ở lớp này bao gồm quản lý bộ nhớ
(memory management), giao tiếp với phần cứng (driver model), thực
hiện bảo mật (security), quản lý tiến trình (process).
Tuy được phát triển dựa vào nhân linux nhưng thực ra nhân linux
đã được nâng cấp và sửa đổi rất nhiều để phù hợp với tính chất của
những thiết bị cầm tay như hạn chế về bộ vi xử lý, dung lượng bộ nhớ,
kích thước màn hình, nhu cần kết nối mạng không dây,…
Tầng này có các thành phần chủ yếu:
+ Display Driver: điều khiển việc hiển thị lên màn hình cũng như
thu nhận những điều khiển của người sử dụng lên màn hình (di chuyển,
cảm ứng,…)
+ Camera Driver: điều kiển hoạt động của camera, nhận dữ liệu
từ camera trả về.
32
+ Bluetooth Driver: điều khiển thiết bị phát và thu hơn Bluetooth.
+ USB driver: quản lý hoạt động của các cổng giao tiếp USB.
+ Keypad driver: điều khiển hoạt động của bàn phím cứng trên máy.
+ Wifi Driver: quản lý về việc thu phát sóng wifi.
+ Audio Driver: điều khiển các bộ thu phát âm thanh, giải mã các
tính hiệu dạng audio thành tín hiệu số và ngược lại.
+ Binder IPC Driver: chịu trách nhiệm về việc kết nối và liên lạc
với mạng vô tuyến như CDMA, GSM, 3G, 4G, E để đảm bảo những
chức năng truyền thông được thực hiện.
+ M-System Driver: quản lý việc đọc ghi… lên các thiết bị nhớ
như thẻ SD, flash.
+ Power Management: quản lý, giám sát việc tiêu thụ điện năng.
c) Các ứng dụng sẵn có trên Android
Một điện thoại android sẽ di kèm với một vài ứng dụng được cài
đặt sẵn:
+ Một chương trình kiểm tra email tương thích với Gmail
+ Chương trình quản lý tin nhắn SMS
+ Phiên bản thu gọn của google map
+ Trình duyệt web
+ Chương trình tán gẫu (chat)
+ Đa phương tiện
+ Chương trình quản lý thông tin cá nhân
Tất cả các ứng dụng có sẵn này đều được viết bằng ngôn ngữ java
sử dụng android SDK.
d) Các phần mềm ứng dụng trên hệ điều hành Android
Việc phát triển phần mềm cho Android OS có thể được thực hiện
trên hệ điều hành Windows của Microsoft hay trên hệ điều hành nguồn
mở Linux. Trong quyển sách này, tác giả chọn phát triển các ứng dụng
cho Android OS trên Windows vì nó mang tính phổ biến, việc cài đặt các
phần mềm hỗ trợ cho việc lập trình không quá phức tạp.
Các bộ công cụ phục vụ việc lập trình ứng dụng trên Android OS gồm:
+ Java for Developers 6 (JDK): do các phần mềm chạy trên
Android OS đều được phát triển trên nền tảng ngôn ngữ Java nên ta cần

33
bộ thư viện của Java, được cung cấp miễn phí cho người phát triển phần
mềm bởi Oracle.
+ Eclipse 3.5 (GALILEO): bộ công cụ dùng để lập trình, phát
triển phần mềm cho Java, được cung cấp miễn phí từ trang web của
Eclipse.
+ Android software development Kit (Android SDK) plugin: là
tập hợp các công cụ và thư viện để phát triển các ứng dụng cho Android
OS. Sau khi cài đặt thì Android SDK sẽ được tích hợp vào chương trình
Eclipse. Nhà phát triển phần mềm sẽ dùng Eclipse đã tích hợp Android
SDK plugin để viết ứng dụng cho Android OS.
+ Android Asset Packaging Tool (aapt): Công cụ để tạo tập tin
.apk chứa các mã nhị phân và tài nguyên của một ứng dụng Android.
+ sqlite3: Đây là công cụ để truy cập các tập tin dữ liệu SQLite
được tạo ra và sử dụng bởi một ứng dụng Android.
+ dx: Đây là công cụ biên dịch các tập tin .class thành mã bytecode
chạy trên máy ảo Dalvik (lưu trữ dưới dạng tập tin .dex).
+ mksdcard: giúp tạo một tập tin ảnh lưu trữ dữ liệu mà ta có thể
sử dụng trên bộ giả lập, để mô phỏng sự có mặt của một thẻ nhớ ngoài
(như thẻ SD).
+ Android Plug-in for Eclipse (ADT): Android plugin dành cho
Eclipse cho phép tích hợp nhiều công cụ để phát triển ứng dụng Android.
Nếu bạn đang dùng Eclipse, bạn nên cài đặt công cụ này. Plugin này cho
phép bạn tạo và gỡ lỗi cho các dự án Android và các tài nguyên
(resources). Việc cài đặt ADT plugin có rất nhiều lợi ích đó là thay vì
phải cài đặt và quản lý các phiên bản Android trên máy tính để lập trình,
bạn có thể chọn phiên bản Android SDK phù hợp nhất để cài đặt. Công
cụ này cũng cho phép các lập trình viên dễ dàng nâng cấp môi trường
phát triển ngay khi có phiên bản mới của Android. Hơn nữa, ngoài các
Android platform để bạn chọn lập trình, bạn có thể tải về các công cụ và
các gói hỗ trợ khác, chẳng hạn như Google Play services.

34
Chương 3
PHẦN MỀM PHÁT TRIỂN ỨNG DỤNG
ANDROID

3.1. DOWNLOAD CÁC GÓI PHẦN MỀM CÔNG CỤ


+ SDK ADT bundle for windows: eclipse, sdk, sdk manager.
+ ADT plugin
+ Phần mềm Java k7u21
Lưu ý: các file giải nén và cài đặt nên lưu cùng thư mục.
Android SDK thực chất là tập hợp các công cụ và thư viện để phát
triển các ứng dụng trên nền tảng hệ điều hành Android.
Bước 1: Vào trang http://developer.android.com/sdk/index.html để
tải SDK ADT bundle for windows. Tùy thuộc vào hệ điều hành mà bạn
chọn bản Mac, Linux hay Window. Ở đây chọn bản tải Android SDK cho
Window và làm theo các bước sau.

Hình 3.1. Download SDK


35
Hình 3.2. Download SDK phiên bản 64 bit
Bước 2: Tải ADT về theo link
http://developer.android.com/tools/sdk/eclipse-adt.html.
Chọn phiên bản mới nhất. Lưu ý nếu bạn cài đặt bản mới nhất, có
thể có một số sự khác biệt về giao diện khi bạn lập trình về sau.

Hình 3.3. Tải ADT mới nhất


36
Hình 3.4. Tải ADT

Hình 3.5. Tải ADT


Bước 3: Tải miễn phí bộ JDK từ trang web của Oracle tại địa chỉ:
http://www.oracle.com/technetwork/java/javase/downloads/index.html

37
Hình 3.6. Phần mềm jdk-u21 (java) sau khi tải về

3.2. CÀI ĐẶT BỘ JDK PLATFORM (JDK) K7U21


Sau khi tải JDK k7u21 về, ta tiến hành cài đặt.

Hình 3.7. Cài đặt bộ JDK Platform

38
Chọn next

Hình 3.8. Cài đặt bộ JDK Platform


Để mặc định các thông số, tiếp tục Click Next

Hình 3.9. Cài đặt bộ JDK Platform


39
Quá trình cài đặt bộ JDK bắt đầu, đợi cho đến khi tiến trình kết thúc.

Hình 3.10. Cài đặt bộ JDK Platform


Click close để hoàn tất quá trình cài đặt bộ JDK.

3.3. CHẠY PHẦN MỀM ECLIPSE


Bộ Eclipse không cần cài đặt, các bạn vào file adt-bundle-windows-
x86_64-20130219 vừa tải về, tìm file elclipse, chạy file eclipse.exe

Hình 3.11. Chạy eclipse.exe


40
Hình 3.12. Màn hình khởi động của Eclipse

Hình 3.13. Màn hình chọn thư mục làm việc của Eclipse

Hình 3.14. Màn hình làm việc của chương trình Eclipse
41
3.4. CÀI ĐẶT ANDROID PLUGIN ADT VÀO ECLIPSE
Chạy chương trình Eclipse, tại menu Help, chọn Install New
Software

Hình 3.15. Quá trình cấu hình phần mềm Eclips

Chọn Add, gõ tên tùy ý vào ô Name và gõ địa chỉ để tải ADT vào
ô Location hoặc tìm đến thư mục ADT đã tải ở trên.

Hoặc

42
Hình 3.16. Quá trình cấu hình phần mềm Eclipse

Hình 3.17.Quá trình cấu hình phần mềm Eclipse

43
Tìm đến thư mục lưu file ADT đã tải về.

Hình 3.18. Quá trình cấu hình phần mềm Eclipse

Chọn select all

Hình 3.19. Quá trình cấu hình phần mềm Eclipse


44
Bấm Next để tiếp tục quá trình cài đặt.

Hình 3.20. Quá trình cấu hình phần mềm Eclipse

Chương trình yêu cầu xác nhận việc chấp nhận/không chấp nhận
các điều khoảng sử dụng. Để tiếp tục, chọn I accept the terms of the
license agreements, bấm Finish để hoàn tất bước cuối của quá trình
cài đặt.

Hình 3.21. Quá trình cấu hình phần mềm Eclipse


Eclipse sẽ tự đồng tiến hành download và cài đặt bộ plugin
Android
45
Hình 3.22. Quá trình cấu hình phần mềm Eclipse

Sau khi cài đặt xong bộ plugin Android, Eclipse yêu cầu khởi động
lại chương trình, bấm Yes để hoàn tất quá trình cài đặt.
Khi chương trình Eclipse đã khởi động lại xong, ta cần thiết lập cấu
hình để Eclipse có thể nhận ra các bộ Android SDK mà ta đã cài đặt ở
phần trước đó.

3.5. CẬP NHẬT CÁC CÔNG CỤ, THƯ VIỆN HỖ TRỢ CHO VIỆC
LẬP TRÌNH HỆ ĐIỀU HÀNH ANDROID
Chọn window => Android SDK manager

Hình 3.23. Cài đặt các công cụ, thư viện lập trình
46
Hình 3.24. Cài đặt các công cụ, thư viện lập trình

Lưu ý: Trước khi chạy SDK Manager phải cho máy tính kết nối
mạng Internet, vì quá trình cài đặt của SDK Manager đòi hỏi kết nối tới
máy chủ của Google để cập nhật phiên bản mới nhất cũng như tải một số
thành phần thiết yếu. Quá trình tải các thành phần thiết yếu diễn ra một
cách tự động cho SDK Manager đảm nhiệm.
Đánh dấu các Packages bạn muốn tải: Document chính là phần
Javadoc mô tả hoạt động của các phương thức và các lớp, Sample là các
đoạn Code mẫu, SDK Platform ứng với các phiên bản hệ điều hành (2.2
– API level 8, 2.1 – API level 7,…) và Google API để phát triển các phần
mềm liên quan đến dịch vụ của google. Click Install packages ở góc
phải để tiếp tục quá trình cài đặt.
Nếu chọn hết tất cả thì tiện lợi cho lập trình nhưng quá trình
download cài đặt diễn ra khá lâu. Để tối ưu có thể chọn mục tool và
phiên bản 4.4.4. Sau đó, chọn Install packages.

47
Hình 3.25. Cài đặt các công cụ, thư viện lập trình
Chọn accept license => install

Hình 3.26. Cài đặt các công cụ, thư viện lập trình
Quá trình download và cài đặt diễn ra khá lâu.
Lưu ý: Ở cột status xuất hiện installed nghĩa là đã được cài đặt, not
installed là chưa được cài đặt.
48
3.6. TÍCH HỢP ANDROID VÀO ECLIPSE
Eclipse → Windows → Preferences → Android. Nhấn nút Browse
và chỉnh đường dẫn tới thư mục chứa các packages của Android SDK
mà bạn đã tải khi chạy file SDK Manager.
→ Apply
→ Ok

Hình 3.27. Quá trình cấu hình phần mềm Eclipse

Hình 3.28. Quá trình cấu hình phần mềm Eclipse

3.7.CÀI ĐẶT THIẾT BỊ CHẠY GIẢ LẬP(ANDROID VIRTUAL


DEVICES)
Trong quá trình lập trình code cho các ứng dụng của Android OS, lập
trình viên cần phải có một công cụ để debugging và testing (gỡ rối và kiểm
49
tra) chạy trực tiếp trên máy tính, mô phỏng xem thử các code đã viết có
chạy đúng theo yêu cầu đã đặt ra hay không, trước khi ứng dụng đó được
cho chạy trên thiết bị thực tế. Bộ công cụ Android SDK cung cấp plugin có
tên là Android Emulator có giao diện như một thiết bị cầm tay thực sự.
Android Emulator được trang bị đầy đủ hầu hết các tính năng của một
thiết bị thật. Tuy nhiên, một số đã bị giới hạn như là kết nối qua cổng USB,
camera và video, nghe phone, nguồn điện giả lập và bluetooth.
Các bước thiết lập một Android Emulator
+ Từ menu Windows của Eclipse, chọn mục Android SDK and
AVD Manager.

Hình 3.29. Quá trình cài đặt máy ảo Android


+ Trong cửa sổ Android SDK and AVD Manager, trong danh sách
chọn Virtual devices, bấm New để thiết lập một máy Android Emulator.

Hình 3.30. Hộp thoại quản lý các máy ảo Android
50
Trong hộp thoại Create new Android Vitual Device (AVD) lần
lượt cấu hình như sau:

Hình 3.31. Tạo máy ảo với cấu hình mong muốn
+ Name: tùy ý (nhưng ký tự được sử dụng các lý tự “A-Z”, “a-
z”,”.-_”, nghĩa là khoảng trắng cũng không được).
+ Target: chọn phiên bản hệ điều hành bạn muốn.
+ SD Card: gõ vào Size SD Card ảo cho AVD, hoặc chỉnh tới tập
tin đã có sẵn. Nhiều AVD có thể dùng chung Sdcard.
+ Skin: có thể để Default (HVGA) hoặc chọn kích cỡ màn hình
bạn muốn.
+ Snapshot: cho phép chức năng tương tự như standby của
windows.
51
Khi đã thiết lập xong cấu hình phần cứng cho máy ảo, bấm nút OK để
tạo máy ảo. Sau đó, trong cửa sổ Android SDK and ADV Manager, mục
Android Virtual Devices sẽ liệt kê các máy ảo đã được tạo thành công.

Hình 3.32. Trình quản lý các máy ảo đã thêm máy ảo ta mới tạo
+ New: tạo một máy ảo ADV khác
+ Edit: chỉnh sửa máy ảo ADV đã tạo trước đó
+ Delete: xóa máy ảo ADV đã tạo
+ Repair: trong quá trình debug, máy ảo có thể bị lỗi, để khác
phục ta dùng chức năng Repair
+ Details: liệt kê thông tin chi tiết của máy ảo (Name, đường dẫn
lưu file ADV, Target, SD card, …)
+ Start: khởi động máy ảo.

Hình 3.33. Mình hình thiết lập khi khởi động máy ảo
52
Khi nhấn Start, một cửa sổ thiết lập nhanh về phần người dùng
được thiết lập.
+ Scale display to real size: chỉnh tỷ lệ màn hình. Có thể để mặc
định (không chọn) hoặc chọn vào mục này, sau đó thiết lập các thông số
màn hình ở khung bên dưới.
+ Wipe user data: xóa toàn bộ dữ liệu của phiên làm việc trước đó
của máy ảo. Nếu không chọn mục này, máy ảo sẽ vẫn lưu các giá trị dữ
liệu của những phiên làm việc trước.
+ Launch from snapshot: cho phép máy ảo chạy lại sau khi thực
hiện chức năng Snapshot.
+ Save to snapshot: lưu các thông tin vào Snapshot cho lần khởi
động sau
Sau khi thiết lập xong, nhấn Launch để chạy máy ảo ADV. Quá
trình khởi động máy ảo ADV tương đối lâu.

Hình 3.34.Quá trình khởi động Hình 3.35.Màn hình máy ảo
máy ảo Android

Nhìn tổng quan, ta thấy máy ảo ADV có đầy đủ chức năng như một
máy thực bên ngoài, bao gồm màn hình, bàn phím, các nút chức năng,
nút điều hướng. Màn hình cảm ứng thực tế sẽ chạy trong máy ảo bằng
cách click chuột vào đối tượng tác động.
53
54
Chương4
CÁC THÀNH PHẦN CƠ BẢN TRONG
ANDROID

4.1. CÁC THÀNH PHẦN TRONG MỘT ANDROID PROJECT


4.1.1. AndroidManifest.xml
Trong bất kỳ một project Android nào khi tạo ra đều có một file
AndroidManifest.xml, file này được dùng để định nghĩa các screen (màn
hình, sau này ta dùng tên Activity) sử dụng, các permission (các cấp
quyền) cũng như các theme (chủ đề) cho ứng dụng. Đồng thời nó cũng
chứa thông tin về phiên bản SDK cũng như main activity sẽ chạy đầu
tiên. File này được tự động sinh ra khi tạo một Android project. Trong
file manifest bao giờ cũng có 3 thành phần chính đó là: application,
permission và version.
Dưới đây là nội dung của một file AndroidManifest.xml của một
chương trình (tất nhiên sẽ khác nhau với các ứng dụng khác nhau, ở đây
ta lấy một trường hợp cụ thể phân tích các thành phần của nó).
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="com.example.guitextview"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
55
android:theme="@style/AppTheme">
<activity
android:name="com.example.guitextview.MainActivity"
android:label="@string/app_name">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>
- Application
Thẻ <application>, bên trong thẻ này chứa các thuộc tính được
định nghĩa cho ứng dụng Android như:
android:icon = “drawable resource”: Ở đây đặt đường dẫn đến file
icon của ứng dụng khi cài đặt. VD: android:icon = “@drawable/icon”.
android:name = “string”: thuộc tính này để đặt tên cho ứng dụng
Android. Tên này sẽ được hiển thị lên màn hình sau khi cài đặt ứng dụng.
android:theme = “drawable theme”: thuộc tính này để đặt theme
cho ứng dụng. Các theme là các cách để hiển thị giao diện ứng dụng.
Ngoài ra còn nhiều thuộc tính khác…
- Permission
Bao gồm các thuộc tính chỉ định quyền truy xuất và sử dụng tài nguyên
của ứng dụng. Khi cần sử dụng một loại tài nguyên nào đó thì trong file
manifest của ứng dụng cần phải khai báo các quyền truy xuất như sau:
<uses-permission
android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission
android:name="android.permission.ACCOUNT_MANAGER"/>
<uses-permission android:name="android.permission.VIBRATE" />
56
<uses-permission
android:name="android.permission.CALL_PHONE"/>
- SDK version
Thẻ xác định phiên bản SDK được khai báo như sau:
<uses-sdk android:minSdkVersion="8" />.
Ở đây chỉ ra phiên bản SDK thấp nhất mà ứng dụng hiện đang sử
dụng.
4.1.2.File R.java
File R.java là một file tự động sinh ra ngay khi tạo ứng dụng, file này
được sử dụng để quản lý các thuộc tính được khai báo trong file XML của
ứng dụng và các tài nguyên hình ảnh. Mã nguồn của file R.java được tự động
sinh khi có bất kỳ một sự kiện nào xảy xa làm thay đổi các thuộc tính trong
ứng dụng. Chẳng hạn như, bạn kéo và thả một file hình ảnh từ bên ngoài vào
project thì ngay lập tức thuộc tính đường dẫn đến file đó cũng sẽ được hình
thành trong file R.java hoặc xóa một file hình ảnh thì đường dẫn tương ứng
đến hình ảnh đó cũng tự động bị xóa. Có thể nói, file R.java hoàn toàn không
cần phải đụng chạm gì đến trong cả quá trình xây dựng ứng dụng.
Dưới đây là nội dung của một filegen\ R.java
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.example.guitextview;
publicfinalclass R {
publicstaticfinalclass attr {
}
Publicstaticfinalclass dimen {
/** Default screen margins, per the Android Design guidelines.
Customize dimensions originally defined in
res/values/dimens.xml (such as

57
screen margins) for sw720dp devices (e.g. 10" tablets) in
landscape here.
*/
Publicstaticfinalintactivity_horizontal_margin=0x7f040000;
Publicstaticfinalintactivity_vertical_margin=0x7f040001;
}
Publicstaticfinalclass drawable {
Publicstaticfinalintic_launcher=0x7f020000;
}
Publicstaticfinalclass id {
Publicstaticfinalintaction_settings=0x7f080002;
PublicstaticfinalinttextView1=0x7f080001;
Publicstaticfinalinttext_id=0x7f080000;
}
Publicstaticfinalclass layout {
Publicstaticfinalintactivity_main=0x7f030000;
}
Publicstaticfinalclass menu {
Publicstaticfinalintmain=0x7f070000;
}
Publicstaticfinalclass string {
Publicstaticfinalintaction_settings=0x7f050001;
Publicstaticfinalintapp_name=0x7f050000;
Publicstaticfinalinthello_world=0x7f050002;
Publicstaticfinalinttxt_tv2=0x7f050003;
}
Publicstaticfinalclass style {
customizations can go here.
*/

58
PublicstaticfinalintAppBaseTheme=0x7f060000;
/** Application theme.
All customizations that are NOT specific to a particular API-level
can go here.
*/
PublicstaticfinalintAppTheme=0x7f060001;
}
}

4.1.3. Chu kỳ tồn tại của ứng dụng Android


Một tiến trình Linux gói gọn một ứng dụng Android đã được tạo ra
cho ứng dụng khi codes cần được chạy (run) và sẽ còn chạy cho đến khi:
- Nó không còn phụ thuộc.
- Hệ thống cần lấy lại bộ nhớ mà nó chiếm giữ cho các ứng dụng khác.
Một sự khác thường và đặc tính cơ bản của Android là thời gian
sống của tiến trình ứng dụng không được điều khiển trực tiếp bới chính
nó. Thay vào đó, nó được xác định bởi hệ thống qua một kết hợp của:
- Những phần của ứng dụng mà hệ thống biết đáng chạy
- Những phần quan trọng như thế nào đối với người dùng
- Bao nhiêu vùng nhớ chiếm lĩnh trong hệ thống.
Android có cơ chế quản lý các process theo chế độ ưu tiên. Các
process có ưu tiên (priority) thấp sẽ bị Android giải phóng mà không hề
cảnh báo nhằm đảm bảo tài nguyên.
+ Foreground process: là process của ứng dụng hiện thời đang
được người dùng tương tác.
+ Visible process: là process của ứng dụng mà activity đang hiển
thị đối với người dùng (onPaused() của activity được gọi).
+ Service process: là Service đang chạy (running).
+ Background process(xử lý nền): là process của ứng dụng mà
các activity của nó ko hiển thị với người dùng (onStoped() của activity
được gọi).

59
+ Empty process: process không có bất cứ một thành phần nào
active.
Theo chế độ ưu tiên thì khi cần tài nguyên, Android sẽ tự động kill
process, trước tiên là các empty process.
Chu kỳ sống của các thành phần trong ứng dụng Android OS
Trong một ứng dụng Android có chứa nhiều thành phần và mỗi
thành phần đều có một chu trình sống riêng. Và ứng dụng chỉ được gọi là
kết thúc khi tất cả các thành phần trong ứng dụng kết thúc. Activity là
một thành phần cho phép người dùng giao tiếp với ứng dụng. Tuy nhiên,
khi tất cả các Activity kết thúc và người dùng không còn giao tiếp được
với ứng dụng nữa nhưng không có nghĩa là ứng dụng đã kết thúc. Bởi vì,
ngoài Activity là thành phần có khả năng tương tác người dùng thì còn có
các thành phần không có khả năng tương tác với người dùng như là
Service, Broadcast, Receiver. Có nghĩa là những thành phần không tương
tác người dùng có thể chạy nền (background) dưới sự giám sát của hệ
điều hành cho đến khi người dùng tự tắt chúng.
Từ lúc bắt đầu cho đến khi kết thúc một chu trình sống của một
thành phần, đôi lúc chúng có thể là active hoặc inactive, hoặc là trong
trường hợp activies nó có thể visible hoặc invisible.
4.1.4. Activity
Tổng quan về Activity
Activity là một thành phần chính của một ứng dụng Android, được
dùng để hiển thị một màn hình và nắm bắt các hoạt động xảy ra trên màn
hình đó, chẳng hạn như quay số điện thoại, chụp ảnh, gửi email, hoặc
xem một bản đồ. Mỗi activity là một cửa sổ, trong đó cho vẽ giao diện
người dùng của nó. Cửa sổ thường lấp đầy màn hình, nhưng có thể nhỏ
hơn so với màn hình và nổi lên trên các cửa sổ khác.
Một ứng dụng thường bao gồm nhiều activity được ràng buộc lỏng
lẻo với nhau. Thông thường, một trong những activity trong một ứng dụng
được quy định như các activity "chính" (main activity), được trình bày cho
người dùng xem khi ứng dụng được chạy lần đầu. Mỗi activity sau đó có
thể bắt đầu activity khác để thực hiện hành động khác nhau.
Trong eclipse để tạo một Activity mới, bạn thực hiện các bước sau:
Bước 1: Chọn thư mục scr
Bấm chuột phải vào mục scr chọn new/ class

60
Hình 4.1. Tạo một lớp dự án
Trong thư mục new/class chứa các thông tin quan trọng sau:
+ source Folder: là thư mục chứa tập tin *.java mà bạn sắp tạo
+ package: Định nghĩa java package mà Activity của bạn thuộc về
+ Name: Tên của Activity mới
+ Superclass: là lớp cha. Trong phần này, ta đang tìm hiểu cách
tạo lớp con từ cha, nên thông tin phần này sẽ là android.app.Activity.

Hình 4.2. New java Class


61
Sau khi điền đầy đủ thông tin. Ta chọn finish, rồi kiểm tra trong
thư mục scr có chứa Activity mới hay chưa.
Vòng đời của Activity
Các Activity trong hệ thống được quản lý bởi một cấu trúc dữ liệu
ngăn xếp. Khi có một hoạt động được khởi tạo, nó được đẩy vào trong
ngăn xếp, chuyển sang trạng thái thực thi và hoạt trộng trước đó sẽ
chuyển sang trạng thái chờ. Hoạt động này chỉ trở lại trạng thái kích hoạt
khi mà hoạt động vừa khởi tạo kết thúc việc thực thi.
Một activity có ba trạng thái chính:
- Active hoặc running: khi nó ở trên nhất của màn hình và nhận
tương tác người dùng
- Paused: khi Activity không còn là trọng tâm trên màn hình nhưng
vẫn hiện thị trước người dùng.
- Stopped: khi một activity hoàn toàn bị che khuất, nó sẽ rơi vào
trạng thái Stopped. Tuy nhiên, nó vẫn còn lưu trữ toàn bộ thông tin trạng
thái. Và nó thường bị hệ thống đóng lại khi có tình trạng thiếu bộ nhớ.
Khi chuyển giữa các trạng thái, ứng dụng sẽ gọi các hàm callback
ứng với các bước chuyển:
void onCreate(Bundle savedInstanceState)
void onStart()
void onRestart()
void onResume()
void onPause()
void onStop()
void onDestroy()
Biểu đồ sau mô tả trạng thái trong vòng đời của một Activity. Hình
chữ nhật vuông còn thể hiện các phương thức Callback mà chúng ta có
thể khai báo để gọi thực thi một số thao tác khi hoạt động chuyển sang
trạng thái khác (phương thức Callback là phương thức được gọi lại bởi
một phương thức khác khi có một sự kiện xảy ra). Các trạng thái chính
của một hoạt động được thể hiện bởi các hình viên thuốc.

62
Hình 4.3. Vòng đời của một Activity

63
Vòng đời của một Activity có thể được thể hiện trong những quá
trình sau:
Toàn bộ thời gian sống của một Activity bắt đầu từ lời gọi đầu tiên
tới phương thức onCreate (Bundle) tới lời gọi phương thức onDestroy().
Trong quá trình này, một Activity sẽ khởi tạo lại tất cả các tài nguyên cần
sử dụng trong phương thức onCreate() và giải phóng chúng khi phương
thức onDestroy() được thực thi.
Thời gian sống có thể nhìn thấy của một hoạt động bắt đầu từ lời
gọi tới phương thức onStart(), cho tới khi phương thức onStop() của nó
được thực thi. Toàn bộ các tài nguyên đang được sử dụng bởi hoạt động
vẫn tiếp tục được lưu giữ, người dùng có thể thấy giao diện nhưng không
tương tác được với hoạt động do trong quá trình này hoạt động không ở
trạng thái chạy tiền cảnh nên người dùng không thể nhìn thấy.
Activity Stack (Ngăn xếp Activity): Bên trong hệ thống, các activity
được quản lý theo cơ chế activity stack:
- Khi một Activity mới được start, nó được đặt ở đỉnh của stack và
trở thành activity đang chạy, activity trước sẽ ở bên dưới activity mới và
sẽ không thấy trong suốt quá trình activity mới tồn tại.
- Nếu người dùng nhấn nút Back thì activity kế tiếp của stack sẽ di
chuyển lên và trở thành active.

Activity mới Activity đang chạy

Activity mới được khởi tạo

Activity đang chạy cuối

Activity n - 1

Nút nhấn Back được nhấn hoặc


Activity đang chạy được đóng
Activity 3

Activity 2

Activity 1

Hình 4.4. Activity Stack


64
Active (running): Activity đang hiển thị trên màn hình
(foreground). Activity này tập trung vào những thao tác của người dùng
trên ứng dụng.
4.1.5. Tìm hiểu về Intent
Ta có thể khái niệm về Intent như sau:
- Là một cấu trúc dữ liệu mô tả cách thức, đối tượng thực hiện của
một Activity.
- Là cầu nối giữa các Activity: ứng dụng Android thường bao gồm
nhiều Activity, mỗi Activity hoạt động độc lập với nhau và thực hiện
những công việc khác nhau. Intent chính là người đưa thư, giúp các
Activity có thể triệu gọi cũng như truyền các dữ liệu cần thiết tới một
Activity khác. Điều này cũng giống như việc di chuyển qua lại giữa các
Forms trong lập trình Windows. Ta sẽ có một chương nói thật chi tiết về
vấn đề này.

Hình 4.5.Mô hình truyền dữ liệu giữa hai activity


Lưu ý: Trong hình trên Activity B chỉ trả về kết quả khi cần thiết.
Ví dụ: giả sử Activity A nhắc người dùng chọn ảnh profile; Activity B
liệt kê các ảnh trong thẻ nhớ và cho phép người dùng chọn ảnh. Khi đó
cặp “code+result” là cần thiết và có thể là “0:null” tức cancel hoặc “1:ảnh
20” tức chọn ảnh 20.
Kiểu dữ liệu của Intent: Intent về cơ bản là một cấu trúc dữ liệu,
được mô tả trong lớp android.content.Inten
Các thuộc tính của một đối tượng Intent
Bảng 4.1. Các thuộc tính của một đối tượng Intent
Thuộc tính chính Thuộc tính phụ
Action: Tên (string) của action mà Catelogy: Thông tin về nhóm của
intent sẽ yêu cầu thực hiện action
Có thể là action được android định Type: Định dạng kiểu dữ liệu
nghĩa sẵn (bulit-in standard option) (chuẩn MIME)
hoặc do lập trình viên định nghĩa
Thường được tự động xác định
65
Data: Dữ liệu mà Activity gọi sẽ Component: Chỉ định cụ thể lớp
được xử lý sẽ thực thi Activity
Định dạng Uri (thông qua hàm Khi được xác định, các thuộc tính
Uri.parse(data)) khác trở thành không bắt buộc
Extras: Chứa tất cả các đóng gói
(key,value) do ứng dụng thêm vào
để truyền qua Intent (cấu trúc
Bundle)
Hàm Uri.parse(data) có nghĩa là gì? Đó là định dạng dữ liệu tương
ứng với mỗi action (chuẩn RFC 3986). Một khi bạn đã sử dụng built-in
action thì bạn phải cung cấp data cho nó theo định dạng này.

Bảng 4.2. Liệt kê một số định dạng và action tương ứng đã được định
nghĩa sẵn
Định dạng Action (Hoạt động) Mô tả
tel:phone_number ACTION_VIEW Mở Dial
form (chưa
gọi)
tel:phone_number ACTION_CALL Thực hiện
gọi tới số
phone
http://web_address ACTION_VIEW Mở trình
htpp://web_address duyệt web
với địa chỉ
được cấp
“some_words”(string) ACTION_WEB_SEARCH Thực hiện
http://wen_address search
https://web_address
sms:// ACTION_SENDTO Gửi tin
nhắn
geo:latitude,longitude ACTION_VIEW Mở ứng
geo:latitude,longitude?z=zoom dụng Maps
và chỉ tới vị
geo:0,0?q=my+street+address
trí được xác
geo:0,0?q=business+near+city định

66
Ý nghĩa một số Intent thông dụng
ACTION_ANSWER - mở Activity để xử lý cuộc gọi tới, thường là
Phone Dialer của Android.
ACTION_CALL - mở một Phone Dialer (mặc định là PD của Android)
và ngay lập tức thực hiện cuộc gọi dựa vào thông tin trong data URI.
ACTION_DELETE - mở Activity cho phép xóa dữ liệu mà địa chỉ
của nó chứa trong data URI
ACTION_DIAL - mở một Phone Dialer (mặc định là PD của
Android) và điền thông tin lấy từ địa chỉ chứa trong data URI.
ACTION_EDIT - mở một Activity cho phép chỉnh sửa dữ liệu mà
địa chỉ lấy từ data URI.
ACTION_SEND - mở một Activity cho phép gửi dữ liệu lấy từ
data URI, kiểu của dữ liệu xác định trong thuộc tính type.
ACTION_SENDTO - mở một Activity cho phép gửi thông điệp tới
địa chỉ lấy từ data URI
ACTION_VIEW - action thông dụng nhất, khởi chạy activity thích
hợp để hiển thị dữ liệu trong data URI.
ACTION_MAIN - sử dụng để khởi chạy một Activity.
Cách sử dụng Intent
Hàm thực thi Activity
Bảng 4.3. Hàm thực thi Activity
Tên hàm Mô tả
Thực thi activity
như mô tả trong
startActivity(intent) android.app.Activity
intent (không lấy
kết quả trả về)
Thực thi activity
như mô tả trong
startActivityForresult(intent)android.app.Activity
intent (có lấy kết
quả trả về)
sendBroadcast(intent)android.content.ContextWrapper Chạy một service
bindService(intent,ServiceConnection,int)android.c
Bind service
ontent.ContextWrapper

67
Khai báo Intent
 Kiểu khai báo Intent tường minh (Explicit Intents):
Intent có thể dùng thuộc tính phụ component để chỉ định đích danh
tên lớp sẽ thực thi Activity. Để thực hiện điều này, lớp Intent cung cấp
các hàm đó là setComponent (ComponentName) và setClass(Context,
Class) và setClassName (Context, String), setClassName (String, String).
Chỉ được dùng để gọi các Activity trong cùng một ứng dụng.
Ví dụ:
Intent intent = new Intent();
intent.setClassName("ten_package", "ten_lop_ben_trong_package");
startActivity(intent);
 Kiểu khai báo Intent không tường minh (Implicit Intents):
Trong trường hợp này, intent không chỉ định một lớp cụ thể mà thay
vào đó dùng các dữ liệu khác (action, data, type,...) và để hệ thống tự quyết
định xem lớp nào (ứng dụng nào) sẽ thích hợp để đáp ứng intent đó.
Thông tin action và category của activity trong một ứng dụng đáp
ứng intent đó phải được khai báo trong Manifest của ứng dụng
(AndroidManifest.xml) dưới dạng Intent-filter (tất nhiên nếu chúng ta
muốn gọi một built-in action thì ta không cần quan tâm đến vấn đề này)
Ví dụ:
ACTION_DIAL tel:123 thông thường sẽ được hệ thống giao cho
activity Phone Dialer mặc định của Android xử lý.
 Intent Filter
Activity, Service và BroadCast Receiver sử dụng Intent Filter để
thông báo cho hệ thống biết các dạng Implicit Intent mà nó có thể xử lý.
Nói cách khác, Intent Filter là bộ lọc Intent, chỉ cho những Intent được
phép đi qua nó.
Intent Filter mô tả khả năng của component định nghĩa nó. Khi hệ
thống bắt được một Implicit Intent (chỉ chứa một số thông tin chung
chung về action, data và category...), nó sẽ sử dụng những thông tin trong
Intent này, kiểm tra đối chiếu với Intent Filter của các component các
ứng dụng, sau đó quyết định khởi chạy ứng dụng nào thích hợp nhất để
xử lý Intent bắt được. Nếu có hai hay nhiều hơn ứng dụng thích hợp,
người dùng sẽ được lựa chọn ứng dụng mình muốn.

68
Ví dụ:
<activity
android:name=".ExampleActivity"
android:label="@string/activity_name">
<intent-filter>
<action android:name="android.intent.action.SENDTO" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="sms" />
</intent-filter>
</activity>
Trên là một ví dụ về Activity với bộ lọc Intent cho phép bắt và xử
lý các Intent gửi tin nhắn SMS.
4.1.6. Tìm hiểu về Content Provider và URI
Trong hệ thống Android tất cả các tài nguyên nguyên Contact,
SMS,… đều được lưu trữ vào CSDL SQLite của hệ thống. Cũng như các
cơ sở dữ liệu khác, cơ sở dữ liệu mà hệ thống Android sử dụng để lưu
trữ thông tin cũng cho phép chúng ta truy vấn dữ liệu như một cơ sở dữ
liệu MSSQL thông thường. Tuy nhiên, trong hệ thống đó, chúng ta
không cần phải thao tác bằng lệnh SQL nhiều để truy xuất dữ liệu mà
thay vào đó Android đã được trang bị một API cho phép người lập trình
có thể dễ dàng truy xuất dữ liệu. Đó gọi là ContentProvider.
ContentProvider cung cấp cho chúng ta một đối tượng con trỏ giúp chúng
ta có thể dễ dàng lấy được bất cứ dữ liệu lưu trữ nào chỉ cần cung cấp
một đường dẫn đúng đến dữ liệu đó. Đường dẫn này còn được gọi là Uri.
Phân tích cấu trúc của Uri gồm bốn phần:

Content://com.example.transportationprovider/trains/122
A B C D

Phần A: Đây là tiền tố chỉ ra dữ liệu được điều khiển bởi Content
Provider và nó không bao giờ thay đổi.
Phần B: Phần này chỉ đến nơi lưu trữ dữ liệu. Cũng giống như cấu
trúc của một số điện thoại thì cái này có thể hình dung nó như là mã quốc
gia hoặc cũng có thể coi nó như là tên của CSDL.
Phần C: Phần này chỉ ra loại dữ liệu. Chẳng hạn như, dữ liệu
contact, dữ liệu SMS,… Phần này có thể coi nó như là tên của một table.
69
Phần D: Phần này chỉ đến đúng vị trí của dữ liệu, có thể coi phần này
như là ID của row trong table hoặc một dữ liệu nào đó dùng để truy vấn.
Ví dụ: Uri chỉ đến contact thứ 0 trong CSDL là
content://contacts/people/0
Để có thể thực hiện truy vấn đến vùng dữ liệu được chỉ ra bởi một
Uri ta cần có hai đối tượng con trỏ được cung cấp bởi Activity đó là:
Cursor và ContentResolver.
Để lấy được hai đối tượng này thì trong Activity sử dụng hàm:
getContentResolver() trả về đối tượng ContentResolver.
getContentResolver().query(Uri uri); trả về đối tượng Cursor.
4.1.7. Tìm hiểu về Background Service
Service là một trong bốn thành phần chính trong một ứng dụng
Android (Activity, Service, BroadcastReceiver, ContentProvider) thành
phần này chạy ngầm và làm những công việc không cần tới giao diện
như chơi nhạc, download, xử lý tính toán,…
Một Service có thể được sử dụng theo hai cách:
- Nó có thể được bắt đầu và được cho phép hoạt động cho đến khi
một người nào đó dừng nó lại hoặc nó tự ngắt. Ở chế độ này, nó được bắt
đầu bằng cách gọi Context.startService() và dừng bằng lệnh
Context.stopService(). Nó có thể tự ngắt bằng lệnh Service.stopSelf()
hoặc Service.stopSelfResult(). Chỉ cần một lệnh stopService() để ngừng
Service lại cho dù lệnh startService() được gọi ra bao nhiêu lần.
- Service có thể được vận hành theo như đã được lập trình việc sử
dụng một Interface mà nó định nghĩa. Các người dùng thiết lập một
đường truyền tới đối tượng Service và sử dụng đường kết nối đó để thâm
nhập vào Service. Kết nối này được thiết lập bằng cách gọi lệnh
Context.bindService() và được đóng lại bằng cách gọi lệnh
Context.unbindService(). Nhiều người dùng có thể kết nối tới cùng một
thiết bị. Nếu Service vẫn chưa được khởi chạy, lệnh bindService() có thể
tùy ý khởi chạy nó.
Hai chế độ này thì không tách biệt toàn bộ. Bạn có thể kết nối với
một Service mà nó đã được bắt đầu với lệnh startService().
Ví dụ: Một Service nghe nhạc ở chế độ nền có thể được bắt đầu
bằng cách gọi lệnh startService() cùng với một đối tượng Intent mà định
dạng được âm nhạc để chơi. Chỉ sau đó, có thể là khi người sử dụng
muốn kiểm soát trình chơi nhạc hoặc biết thêm thông tin về bài hát hiện
70
tại đang chơi, thì sẽ có một Activity tạo lập một đường truyền tới Service
bằng cách gọi bindService(). Trong trường hợp như thế này,
stopService() sẽ không thực sự ngừng Service cho đến khi liên kết cuối
cùng được đóng lại.
Giống như một Activity, một Service cũng có các phương thức chu
kỳ thời gian mà bạn có thể cài đặt để kiểm soát những sự thay đổi trong
trạng thái của nó. Service chỉ có ba phương thức được gọi đến trong chu
trình sống là:
void onCreate()
void onStart(Intent intent)
void onDestroy()
Bằng việc thực hiện những phương thức này, ta có thể giám sát hai
vòng lặp của chu kỳ thời gian của mỗi Service Entire lifetime của một
Service diễn ra giữa thời gian onCreate() được gọi ra và thời gian mà
onDestroy() trả lại. Giống như một Activity, một Service lại tiến hành cài
đặt ban đầu ở onCreate(), và giải phóng tất cả các tài nguyên còn lại ở
onDestroy().
Active lifetime của một Service bắt đầu bằng một lệnh tới
onStart(). Đây là phương thức được chuyển giao đối tượng Intent mà đã
được thông qua để tới startService().
Không có callback tương đương nào cho thời điểm Service ngừng lại –
không có phương thức onStop(). Các phương thức onCreate() và onDestroy()
được gọi cho tất cả các Service dù chúng có được bắt đầu bằng
Context.startService() hoặc Context.bindService() hay không. Tuy nhiên,
onStart() chỉ được gọi ra đối với các Service bắt đầu bằng startService(). Nếu
một Service cho phép những Service khác kết nối với nó thì sẽ có thêm các
phương thức callback dành cho Service đó để thực hiện.
IBinder onBind (Intent intent)
Boolean onUnbind (Intent intent)
Void onRebind (Intent intent)
Hàm callback onBind() thông qua đối tượng Intent đã được truyền
đến bindService và onUnbind() được chuyển giao đối tượng mà đã được
chuyển đến. Nếu Service đang được chỉ định (binding), onBind() quay
trở lại kênh thông tin mà người dùng sử dụng để tương tác với Service.
Phương thức onUnbind() có thể yêu cầu onRebind() được gọi nếu một
người dùng kết nối với Service.

71
Hình 4.6. Lưu đồ minh họa cho các phương thức callback giành cho một
Service
Mặc dù, nó phân tách các Service được tạo ra thông qua
startService với các Service mà được tạo ra bằng bindService(). Hãy nhớ
rằng bất kỳ Service nào, cho dù nó được khởi tạo như thế nào thì nó vẫn
có thể cho phép các người dùng kết nối tới nó một cách hiệu quả nhất,
cho nên bất kỳ Service nào cũng có thể được chỉ định thông qua các các
phương thức onBind() và onUnbind().
4.1.8. Tìm hiểu về Telephony
Telephony là một trong bốn thành phần chính của một hệ thống
Android. Nó cho phép người lập trình có thể lấy các thông tin của hệ
thống như thông tin SIM, thông tin thiết bị, thông tin mạng,… Ngoài ra,
chúng ta cũng có thể cài đặt các thông số cho thiết bị nếu các thông số đó
có thể thay đổi được. Tất cả những điều đó được quản lý bởi một class
TelephonyManager trong Android.
TelephonyManager telMan =
(TelephonyManager)getSystemService(Context.TELEPHONY_SE
VICE);
Ví dụ:
- Lấy thông tin ID thiết bị: telMan.getDeviceId();
- Lấy thông tin số serial SIM: telMan.getSimSerialNumber();

72
4.1.9. Tìm hiểu về Broadcast
a) Tổng quan về Broadcast Receiver
Broadcast Receiver (có thể gọi là Receiver) là một trong bốn loại
thành phần trong ứng dụng Android. Chức năng dùng để nhận các sự
kiện mà các ứng dụng hoặc hệ thống phát đi.
Có hai cách phát - nhận đó là:
- Không có thứ tự: receiver nào đủ điều kiện thì nhận hết, không
phân biệt và cũng tách rời nhau.
- Có thứ tự: receiver nào đăng ký ưu tiên hơn thì nhận trước, và có
thể truyền thêm thông tin xử lý cho các receiver sau.
Thực ra vòng đời (lifecycle) của BroadcastReceiver chỉ có duy nhất
một phương thức onReceive().
- Khi có sự kiện mà BroadcastReceiver đã đăng ký nhận được phát
đi, thì phương thức onReceive() của BroadcastReceiver đó sẽ được gọi.
- Sau khi thực thi xong phương thức này, vòng đời của Receiver
kết thúc.
Ngay khi onReceive() kết thúc, hệ thống coi như receiver đã không
còn hoạt động và có thể kill process chứa receiver này bất cứ lúc nào. Do
đó, ta cần tránh xử lý các code quá lâu trong onReceive(), không có xử lý
bất đồng bộ, chờ callback… trong Receiver (cụ thể như hiển thị Dialog,
kết nối service…).
Một số broadcast thông dụng:
- Báo hệ thống khởi động xong
- Báo pin có sự thay đổi
- Báo có package mới cài vào hoặc xóa đi
- Báo tắt máy
- Báo cắm sạc, rút sạc
- Thông báo tin nhắn tới
- Thông báo cắm, rút thẻ nhớ
- Thông báo có cuộc gọi đi.
Ta có thể định nghĩa broadcast cho riêng mình (mục tiêu chính của
việc này giúp ta có thể liên lạc giữa các ứng dụng mà mình viết hoặc thông
báo một sự kiện liên quan đến ứng dụng của mình với các ứng dụng khác).

73
b) Phương thức onReceive()
Phương thức này được gọi khi có sự kiện tương ứng được phát đi.
Ở trong phương thức này, ta thấy truyền vào context và intent. Vì
Receiver không kế thừa từ Context nên cần truyền context mà receiver
này đang chạy vào:
- Thứ nhất, để có thể xử lý các phương thức yêu cầu truyền thêm
Context.
- Thứ hai, để sử dụng các phương thức của lớp Context. Intent
được truyền vào sẽ có đầy đủ thông tin như sự kiện nào mà receiver này
đăng ký đã xảy ra dẫn đến onReceive() được gọi, có gửi kèm thông tin gì
hoặc dữ liệu gì hay không.

4.2. NGÔN NGỮ JAVA TRONG LẬP TRÌNH ANDROID TRÊN


PHẦN MỀM ECLIPSE
Ngôn ngữ lập trình Java là ngôn ngữ lập trình hướng đối tượng mạnh
mẽ và rất phổ biến, được phát triển bởi Sun Microsystems (nay thuộc sở
hữu của Oracle). Ứng dụng viết bằng Java trở nên phổ biến vì có thể hoạt
động trên nhiều nền tảng công nghệ, hệ điều hành khác nhau. Mã lệnh Java
viết ra một lần, có thể thực thi trên nhiều dòng máy và thiết bị khác nhau.
Ngôn ngữ lập trình Java được sử dụng để xây dựng từ các ứng
dụng nhỏ gọn như thiết bị cầm tay, các ứng dụng giải trí, đến các ứng
dụng lớn cho doanh nghiệp. Hiện nay, các nhà phát triển viết ứng dụng.
4.2.1. Biến
Biến là vùng nhớ dùng để lưu trữ các giá trị của chương trình. Mỗi biến
gắn liền vớimột kiểu dữ liệu và một định danh duy nhất gọi là tên biến.
- Tên biến thông thường là một chuỗi các ký tự (Unicode), ký số.
- Tên biến phải bắt đầu bằng một chữ cái, một dấu gạch dưới hay
dấu dollar.
- Tên biến không được trùng với các từ khóa (xem phụ lục các từ
khóa trong java).
- Tên biến không có khoảng trắng ở giữa tên.
Trong java, biến có thể được khai báo ở bất kỳ nơi đâu trong
chương trình.
Cách khai báo
<kiểu_dữ_liệu><tên_biến>;
74
<kiểu_dữ_liệu><tên_biến>=<giá_trị>
Gán giá trị cho biến
<tên_biến>=<giá_trị>;
Biến công cộng (toàn cục): là biến có thể truy xuất ở khắp nơi
trong chương trình, thường được khai báo dùng từ khóa public, hoặc đặt
chúng trong một class.
Biến cục bộ: là biến chỉ có thể truy xuất trong khối lệnh nó khai báo.
Lưu ý: Trong ngôn ngữ lập trình java có phân biệt chữ in hoa và in
thường. Vì vậy, chúng ta cần lưu ý khi đặt tên cho các đối tượng dữ liệu
cũng như các xử lý trong chương trình.
4.2.2. Các kiểu dữ liệu cơ sở
Ngôn ngữ lập trình java có tám kiểu dữ liệu cơ sở: byte, short, int,
long, float, double, boolean và char.
Bảng 4.3. Các kiểu dữ liệu cơ sở
Kiểu Kích thước Giá trị Giá trị Giá trị mặc
(bytes) min max định
byte 1 -256 255 0
Short 2 -32768 32767 0
63 63
Long 8 -2 2 -1 0L
Float 4 0.0f
Double 8 0.0d
int 4 -231 231- 1 0

 Kiểu số nguyên
- Java cung cấp bốn kiểu số nguyên khác nhau là: byte, short, int,
long. Kích thước, giá trị nhỏ nhất, lớn nhất, cũng như giá trị mặc định của
các kiểu dữ liệu số nguyên được mô tả chi tiết trong bảng trên.
- Kiểu mặc định của các số nguyên là kiểu int.
- Các số nguyên kiểu byte và short rất ít khi được dùng.
- Trong java không có kiểu số nguyên không dấu như trong ngôn
ngữ C/C++.
Khai báo và khởi tạo giá trị cho các biến kiểu nguyên:
75
intx=0;
longy=100;
Một số lưu ý đối với các phép toán trên số nguyên
- Nếu hai toán hạng kiểu long thì kết quả là kiểu long.
- Một trong hai toán hạng không phải kiểu long sẽ được chuyển
thành kiểu long trước khi thực hiện phép toán.
- Nếu hai toán hạng đầu không phải kiểu long thì phép tính sẽ thực
hiện với kiểu int.
- Các toán hạng kiểu byte hay short sẽ được chuyển sang kiểu int
trước khi thực hiện phép toán.
- Trong java không thể chuyển biến kiểu int và kiểu boolean như
trong ngôn ngữ C/C++.
Ví dụ: Có đoạn chương trình như sau:
booleanb=false;
if(b= =0)
{
System.out.println("Xinchao");
}
Lúc biên dịch đoạn chương trình trên trình dịch sẽ báo lỗi: không
được phép so sánh biến kiểu boolean với một giá trị kiểu int.
 Kiểu dấu chấm động
Đối với kiểu dấu chấm động hay kiểu thực, java hỗ trợ hai kiểu dữ
liệu là float và double.
Kiểu float có kích thước 4 byte và giá trị mặc định là 0.0f
Kiểu double có kích thước 8 byte và giá trị mặc định là 0.0
Số kiểu dấu chấm động không có giá trị nhỏ nhất cũng không có giá
trị lớn nhất. Chúng có thể nhận các giá trị:
- Số âm
- Số dương
- Vô cực âm
- Vô cực dương

76
Khai báo và khởi tạo giá trị cho các biến kiểu dấu chấm động:
floatx=100.0/7;
doubley=1.56E6;
Một số lưu ý đối với các phép toán trên số dấu chấm động
- Nếu mỗi toán hạng đều có kiểu dấu chấm động thì phép toán
chuyển thành phép toán dấu chấm động.
- Nếu có một toán hạng là double thì các toán hạng còn lại sẽ được
chuyển thành kiểu double trước khi thực hiện phép toán.
- Biến kiểu float và double có thể ép chuyển sang kiểu dữ liệu khác
trừ kiểu boolean.
 Kiểu ký tự (char)
Kiểu ký tự trong ngôn ngữ lập trình java có kích thước là 2 bytes và
chỉ dùng để biểu diễn các ký tự trong bộ mã Unicode. Như vậy, kiểu char
trong java có thể biểu diễn tất cả 216= 65536 ký tự khác nhau.
Giá trị mặc định cho một biến kiểu char là null.
 Kiểu luận lý (boolean)
Kiểu boolean chỉ nhận một trong hai giá trị: true hoặc false. Trong
java kiểu boolean không thể chuyển thành kiểu nguyên và ngược lại. Giá
trị mặc định của kiểu boolean là false.
 Hằng
- Hằng là một giá trị bất biến trong chương trình. Tên hằng được
đặt theo quy ước giống như tên biến.
- Hằng số nguyên: trường hợp giá trị hằng ở dạng long ta thêm vào
cuối chuỗi số chữ “l” hay “L”. (ví dụ: 1L)
- Hằng số thực: truờng hợp giá trị hằng có kiểu float ta thêm tiếp vĩ
ngữ “f” hay “F”, còn kiểu số double thì ta thêm tiếp vĩ ngữ “d” hay “D”.
- Hằng Boolean: java có hai hằng boolean là true, false.
- Hằng ký tự: là một ký tự đơn nằm giữa nằm giữa hai dấu ngoặc đơn.
Ví dụ: ‘a’: hằng ký tự a
Một số hằng ký tự đặc biệt

77
Bảng 4.4. Một số ký tự đặc biệt
Ký tự Ý nghĩa
\b Xóa lùi(BackSpace)
\t Tab
\n Xuống hàng
\r Dấu enter
\” Nháy kép
\’ Nháy đơn
\\ Số ngược
\f Đẩy trang
\uxxxx Ký tự unicode
- Hằng chuỗi: là tập hợp các ký tự được đặt giữa hai dấu nháy kép “
”. Một hằng chuỗi không có ký tự nào là một hằng chuỗi rỗng.
Ví dụ: “HelloWolrd”
Lưu ý: Hằng chuỗi không phải là một kiểu dữ liệu cơ sở nhưng vẫn
được khai báo và sử dụng trong các chương trình.
4.2.3. Lệnh, khối lệnh trong java
Giống như trong ngôn ngữ C, các câu lệnh trong java kết thúc bằng
một dấu chấm phẩy (;).
Một khối lệnh là đoạn chương trình gồm hai lệnh trở lên và được
bắt đầu bằng dấu mở ngoặc nhọn ({) và kết thúc bằng dấu đóng ngoặc
nhọn (}).
Bên trong một khối lệnh có thể chứa một hay nhiều lệnh hoặc chứa
các khối lệnh khác.
{//khối 1
{//khối 2
lệnh 2.1
lệnh 2.2

}//kết thúc khối lệnh 2 lệnh 1.1
lệnh 1.2
78

}//kết thúc khối lệnh 1

{//bắt đầu khối lệnh 3


//Các lệnh thuộc khối lệnh 3
//…
}//kết thúc thối lệnh 3

4.2.4. Toán tử và biểu thức
- Toán tử số học
Bảng 4.5. Một số phép toán số học
Toán tử Ý nghĩa
+ Cộng
- Trừ
* Nhân
/ Chia nguyên
% Chia dư
++ Tăng 1
-- Giảm 1
- Toán tử trên bit
Bảng 4.6. Các phép toán trên bit
Toán tử Ý nghĩa
& AND
| OR
^ XOR
<< Dịch trái
>> Dịch phải
>>> Dịch phải và điền 0 vào bit trống
~ Bù bit
79
- Toán tử quan hệ & logic
Bảng 4.7. Toán tử quan hệ và logic
Toán tử Ý nghĩa
== So sánh bằng
!= So sánh khác
> So sánh lớn hơn
< So sánh nhỏ hơn
>= So sánh lớn hơn hay bằng
<= So sánh nhỏ hơn hay bằng
|| OR (biểu thức logic)
&& AND (biểu thức logic)
! NOT (biểu thức logic)
- Toán tử ép kiểu
Ép kiểu rộng (widening conversion): từ kiểu nhỏ sang kiểu lớn
(không mất mát thông tin)
Ép kiểu hẹp (narrow conversion): từ kiểu lớn sang kiểu nhỏ (có khả
năng mất mát thông tin)
<tên biến>=(kiểu_dữ_liệu)<tên_biến>;
Ví dụ:
floatfNum=2.2;
intiCount=(int)fNum;//(iCount=2)
- Toán tử điều kiện
Cú pháp:<điều kiện>?<biểu thức 1>:<biểu thức 2>
Nếu điều kiện đúng thì có giá trị, hay thực hiện <biểu thức 1>, còn
ngược lại là <biểu thức 2>. <điều kiện>: là một biểu thức logic. <biểu thức
1>, <biểu thức2>: có thể là hai giá trị, hai biểu thức hoặc hai hành động.
Vídụ:
intx=10;
inty=20;
intZ=(x<y)?30:40;

80
//Kếtquả z =30dobiểuthức(x<y)là đúng.
 Cấu trúc điều khiển
+ Cấu trúc điều kiện if…else
Dạng 1:
if(<điều_kiện>)
{
<khối_lệnh>;}
Dạng 2:
if(<điều_kiện>)
{<khối lệnh>;}
else
{
<khối_lệnh 1>;
<khối_lệnh 2>;
}
+ Cấu trúc switch…case
switch(<biến>)
{
case<giátrị_1>:
<khối_lệnh_1>;
break;
….
case<giátrị_n>:
<khối_lệnh_n>;
break;

default:
<khối lệnh default>;
}

81
 Cấu trúc lặp
Dạng 1: while(…)
while(điều_kiện_lặp)
{
khối_lệnh;
}
Dạng 2: do{…}while;
do
{
khối_lệnh;
}while(điều_kiện);
Dạng 3: for(…)
for(khởi_tạo_biến_đếm;đk_lặp;tăng_biến)
{
<khối_lệnh>;
}
 Cấu trúc lệnh nhảy (jump)
Lệnh break: Trong cấu trúc switch, chúng ta dùng câu lệnh break
để thoát thỏi cấu trúc switch trong cùng đang chứa nó. Tương tự như vậy,
trong cấu trúc lặp, câu lệnh break dùng để thoát khỏi cấu trúc lặp trong
cùng đang chứa nó.
Lệnh continue: dùng để tiếp tục vòng lặp trong cùng chứa nó
(ngược với break).
Nhãn (label): Không giống như C/C++, Java không hỗ trợ lệnh goto
để nhảy đến một vị trí nào đó của chương trình. Java dùng kết hợp nhãn
(label) với từ khóa break và continue để thay thế cho lệnh goto.
Ví dụ:
label: for (…)
{ for (…)
{ if(<biểu thức điều kiện>)
Break label;

82
else
continue label
}
}
Lệnh “label:” xác định vị trí của nhãn và xem như tên của vòng lặp
ngoài. Nếu <biểu thức điều kiện> đúng thì lệnh break label sẽ thực hiện
việc nhảy ra khỏi vòng lặp có nhãn là “label”, ngược lại sẽ tiếp tục vòng
lặp có nhãn “label” (khác với break và continue thông thường chỉ thoát
khỏi hay tiếp tục vòng lặp trong cùng chứa nó.).
4.2.5. Kiểu dữ liệu mảng
- Khái niệm
Mảng là tập hợp nhiều phần tử có cùng tên, cùng kiểu dữ liệu và mỗi
phần tử trong mảng được truy xuất thông qua chỉ số của nó trong mảng.
- Khai báo mảng
<kiểu dữ liệu> <tên mảng>[];

hoặc <kiểu dữ liệu>[] <tên mảng>;


Ví dụ:
Int arrInt[];
hoặc int[] arrInt;
int[] arrInt1,arrInt2,arrInt3;
- Cấp phát bộ nhớ cho mảng
- Không giống như trong C,C++ kích thước của mảng được xác
định khi khai báo. Chẳng hạn như:
intarrInt[100];//Khai báo náy trong Java sẽ bị báo lỗi.
- Để cấp phát bộ nhớ cho mảng trong Java ta cần dùng từ khóa new.
(Tất cả trong Java đều thông qua các đối tượng). Chẳng hạn để cấp phát
vùng nhớ cho mảng trong Java ta làm như sau:
intarrInt=new int[100];
Khởi tạo mảng
Chúng ta có thể khởi tạo giá trị ban đầu cho các phần tử của mảng
khi nó được khai báo.
83
Ví dụ:
int arrInt[] ={1,2,3};
char arrChar[] ={‘a’,‘b’,‘c’};
String arrStrng[] ={“ABC”,“EFG”,‘GHI’};
- Truy cập mảng
Chỉ số mảng trong Java bắt đầu tư 0. Vì vậy, phần tử đầu tiên có chỉ
số là 0, và phần tử thứ n có chỉ số là n-1. Các phần tử của mảng được truy
xuất thông qua chỉ số của nó đặt giữa cặp dấu ngoặc vuông ([]).
Ví dụ:
intarrInt[]={1,2,3};
intx=arrInt[0];//x sẽ có giá trị là 1.
inty=arrInt[1];//y sẽ có giá trị là 2.
intz =arrInt[2];//z sẽ có giá trị là 3.
Lưu ý: Trong những ngôn ngữ lập trình khác (C chẳng hạn), một
chuỗi được xem như một mảng các ký tự. Trong java thì khác, java cung
cấp một lớp String để làm việc với đối tượng dữ liệu chuỗi cũng khác thao
tác trên đối tượng dữ liệu này.

4.3. SỬ DỤNG PHẦN MỀM ECLIPSE LẬP TRÌNH CHO ANDROID


OS
4.3.1. Các bước tạo một project
Bước 1: Tạo dự án mới bằng cách vào File\New\Android
Application Project…. hoặc dùng nút New trên thanh công cụ.

Hình 4.7. Tạo project mới


Bước 2: Cửa sổ New Android Appliction xuất hiện. Ta điền các
thông tin vào các ô text
84
Hình 4.8. Tạo project mới

- Application Name: tên ứng dụng (a – Z, 0 – 9), nên viết hoa ký tự


đầu tiên
- Project Name: tên dự án (thông thường ta nên đặt cùng tên với
Application Name nhằm mục đích dễ gợi nhớ).
- Package Name: thường có phần mở rộng mặc định là
“com.example.”, ta có thể thêm bớt hoặc đổi tên cho dễ quản lý và mang
tính gợi nhớ. Phải có ít nhất một dấu chấm. Tốt nhất ta nên đổi lại cho thống
nhất và dễ quản lý, ví dụ tại đây tôi có thể đổi: nguyenvanhiep.fivemen
- Target SDK: chọn phiên bản Android để chạy ứng dụng
- Minimum Required SDK: phiên bản thấp nhất có thể chạy được
ứng dụng này.
Bước 3: Nhấn next để tiếp tục

85
Hình 4.9. Tạo project mới
- Create Project in Workspace: Chọn nơi lưu project.
- Mark this project as a library: Chọn project này làm thư viện (No).
- Create custom launcher icon: Tạo Icon cho project (Yes).
- Create Activity: Mỗi ứng dụng phải có ít nhất một Activity (yes).
Bước 4: Để mặc định các icon, nhấn next để configure Launcher,

Hình 4.10. Tạo project mới


86
Bước 5: Cửa sổ Create Activity xuất hiện, chọn create Activity, để
mặc định chế độ balnk Activity, nhấn next.

Hình 4.11. Tạo project mới


Bước 6: Điền các thông tin vào cửa sổ blank Activity hoặc để mặc
định.

Hình 4.12. Tạo Activity

87
- Activity Name: Mặc định là MainActivity, ta có thể thay đổi, và
phải bắt đầu bằng chữ hoa và không có các ký tự đặc biệt.
- Layout Name: tên giao diện, mặc định là activity_main, tương tự
ta có thể thay đổi được và phải bắt đầu bằng chữ cái thường và không có
các kí tự đặc biệt.
- Title: tựa của ứng dụng, phần này sẽ hiển thị trong giao diện của
ứng dụng.
- Các mục còn lại không cần quan tâm đến.
Bước 7: Nhấn Finish để kết thúc quá trình tạo một Project, được
giao diện như sau:

Hình 4.13. Giao diện project mới

88
Hình 4.14. Các thành phần trong pakage explore
Trong đó có các tệp tin và thư mục chính sau đây:
AndroidManifest.xml: file XML khai báo các thành phần có trong
ứng dụng (activities, services,...)
bin/: Thư mục chứa ứng dụng sau khi được biên dịch.
bin/classes/: Thư mục chứa tệp tin code java đã thông dịch.
bin/classes.dex: Thư mục chứa các tệp tin có khả năng thực thi tạo
bởi các lớp Java.
bin/Vdmau.apk: Tệp tin cài đặt trên thiết bị để thực thi.
89
libs/: Thư mục chứa các tệp JAR sử dụng trong ứng dụng (thư viện
của hãng thứ ba).
src/: Thư mục chứa mã nguồn Java của ứng dụng.
res/: Thư mục chứa các tài nguyên của ứng dụng, như các icons,
tệp tin layout,...
res/drawable/: Thư mục chứa file hình ảnh (PNG, JPEG,...).
res/layout/: Thư mục chứa các tệp tin xml đánh dấu giao diện.
res/menu/: Thư mục chứa các tệp tin xml đánh dấu menu của
ứng dụng.
res/values/: chứa các strings, dimensions,...
res/xml/: chứa các file XML khác cần cho ứng dụng.
4.3.2. Thiết kế giao diện cơ bản cho ứng dụng
Phần này ta có những chương chi tiết về sau sẽ nói rõ hơn. Tại
chương này ta chỉ nói các thiết kế đơn giản và các thao tác đơn giản mục
đích để làm quen.
Để thiết kế giao diện người dùng cho ứng dụng, ta bấm xổ thư mục
con layout, lúc này chương trình sẽ mặc định tạo ra file main.xml, file
này chính là cửa sổ chính của ứng dụng khi chương trình chạy.
Double Click vào file main.xml, nội dung của file main.xml được
hiện ra ở tab làm việc cạnh bên. Ta có thể tiến hành thiết kế giao diện ở
chế độ Graphical Layout theo phương pháp trực quan Drag and Drog
(kéo thả) bằng cách kéo các đối tượng (Button, TextView,…) trong hộp
Form Widgets sang màn hình thiết kế giao diện Android rồi thả ra.
Ngoài ra cũng có thể hiệu chỉnh, thêm bớt các đối tượng ở chế độ hiệu
chỉnh code bằng cách click vào tab main.xlm ở cạnh tab Graphical
Layout.

90
Hình 4.15. Màn hình thiết kế layout ứng dụng Android

Hình 4.15. Màn hình nhập code thiết kế giao diện ứng dụng
91
Sau khi đã thiết kế giao diện, ta cần gán nội dung cho các thành
phần đối tượng. Có hai cách để gán nội dung cho các đối tượng:
- Cách 1: Gán nội dung trực tiếp cho đối tượng trong quá trình
thiết kế giao diện, ví dụ như đối tượng TextView trong ví dụ trên, sẽ
được gán nội dung thông qua dòng lệnh:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="KHOA ĐIỆN ĐIỆN TỬ " />
Kết quả trên màn hình thiết kế, đối tượng TextView sẽ có nội dung:

Hình 4.16. Giao diện màn hình Layout


- Cách 2: Đặt một biến cho đối tượng, sau đó khai báo nội dung
của biến trong file string.xml. Ví dụ như đối tượng TextView trong ví dụ
trên được khai báo biến tên là tam trong file main.xml như sau:
<TextView
android:layout_width="fill_parent"

92
android:layout_height="wrap_content"
android:text="@string/tam" />
Chú ý: Không có khoảng trống trong dấu nháy “”
Trong khung Package Explorer, chọn thư mục values, double click
vào file strings.xml

Hình 4.17. Màn hình thiết kế nội dung hiển thị cho các đối tượng
Ở chế độ thiết kế trực quan, chọn tab Resources như hình trên.
Danh sách các đối tượng có trong ứng dụng đang thiết kế được liệt kê
trong khung Resources Elements, click vào biến tam (kiểu dữ liệu
String), khung Attributes for tam (String) sẽ liệt kê thông tin giới thiệu
và hai textbox quan trọng:
+ Name: tam
+ Value: KHOA ĐIỆN ĐIỆN TỬ
Ta cũng có thể đặt một biến mới bằng cách click vào nút Add, xóa
biến đang có bằng cách chọn biến đó, click Remove.
Ở chế độ thiết kế bằng code, chọn tab Strings.xml như hình:

93
Hình 4.18. Màn hình nhập mã nguồn nội dung hiển thị cho các đối tượng

Sau khi toàn tất quá trình khai báo cho các biến, click nút save
(hoặc nhấn tổ hợp phím Ctrl + S) để lưu lại các thiết đặt.
Quay trở lại file main.xml, ở chế độ Graphical Layout, ta sẽ thấy
nội dung của TextView với nội dung đã được quy định trong file
strings.xml như hình dưới:
Nhận xét:
Đối với những đối tượng với nội dung cố định, những ứng dụng
đơn giản, ta nên dùng phương pháp gán nội dung trực tiếp (ví dụ như các
button, checkbox, option,…)
Đối với những đối tượng có nội dung có thể thay đổi hay những
chương trình phức tạp, ta nên dùng phương pháp gán nội dung thông qua
biến (ví dụ như các TextView, EditText,…) vì nó sẽ đơn giản hơn cho
quá trình quản lý thiết kế, chỉnh sửa cũng như phát triển cho ứng dụng.

94
Viết code cho ứng dụng

Hình 4.19. Màn hình nhập mã lệnh chương trình


Trong thư mục src, chọn package với tên mà ta đã đặt trong phần
tạo mới project, double click vào file có phần mở rộng .java, khung kế
bên sẽ hiện ra cửa sổ cho người lập trình nhập code như hình trên.
Nếu người lập trình nhập code không đúng cú pháp đã quy định,
chương trình sẽ báo lỗi ngay tại dòng lỗi đó để người lập trình tiện lợi
trong việc kiểm tra và sửa lỗi.
4.3.3.Biên dịch lỗi và chạy mô phỏng ứng dụng:
Từ menu Run của Eclipse, chọn Run (hoặc nhấn tổ hợp phím Ctrl
+ F11), chương trình sẽ tiến hành debug và chạy mô phỏng trên thiết bị
ADV đã cài đặt trước.

95
Hình 4.20. Biên dịch ứng dụng

Hình 4.21. Màn hình chạy mô phỏng chương trình


Lưu ý: Do quá trình khởi động máy ảo ADV rất lâu, nên ta chỉ cần
khởi động thiết bị ảo ADV một lần duy nhất ban đầu, sau khi xem chạy
mô phỏng xong, ta không nên tắt cửa sổ máy ảo ADV đi mà chỉ cần thu
nhỏ lại trên thành taskbar của Windows. Trong những lần chạy mô phỏng
96
tiếp theo, chỉ cần bấm tổ hợp phím Ctrl + F11 hay click trên menu Run \
Run, ứng dụng sẽ được tự động cài đặt và chạy lại trên máy ảo ADV.
4.3.4. Đóng gói ứng dụng
Ứng dụng sau khi đã được thiết kế hoàn chỉnh về mặt giao diện và
code, đã chạy đúng theo yêu cầu mà người lập trình để ra, thì cần được
đóng gói thành một file cài đặt để có thể cài đặt và chạy trên các thiết bị
Android OS thực thụ. Có ba cách:
Cách 1: Trong khung Pakage Explorer, click phải chuột vào tên
project -> Android tools -> Export UnSigned Application Package …
Trong cửa sổ Export Project, chọn đường dẫn cho file cần đóng
gói, nhập tên file đóng gói, bấm Save để hoàn tất.
Cách 2: Vào đường dẫn lưu project tìm file .apk, ví dụ D:\adt-
bundle-windows-x86_64-
20130219\sdk\worspake\Vdmau\bin\Vdmau.apk.
Cách 3: Lấy file trực tiếp từ giao diện eclipse trong thư mục bin/res.

Hình 4.22. File .apk trực tiếp từ giao diện eclipse trong thư mục bin/res
4.3.5. Hướng dẫn cách Debug (gỡ rối) chương trình trên thiết bị thật
Nếu bạn đang sử dụng một thiết bị Android (smartphone hay
tablet), bạn có thể viết chương trình và kiểm tra gỡ rối trên chính thiết bị
của mình. Như vậy sẽ nhanh và thực tế hơn so với chạy trên máy ảo.
Trình tự làm như sau:

97
- Trên thiết bị di động bạn kích hoạt chế độ USB debugging. Tùy
thiết bị bạn có thể nhờ “anh Google” hướng dẫn cách kích hoạt. Ở đây tôi
dùng Sony Xperia Z1 thì cách làm như sau: bạn vào setting\About phone.
Sau đó gõ vào dòng Build number 6-7 liên tục cho đến khi hiện ra dòng
chữ “You are a developer” thì thôi. Tiếp theo bạn quay trở lại mục
Setting tìm dòng Developer options và vào đó check vào mục USB
debugging. Sau đó dùng cáp mini USB kết nối điện thoại với máy tính.
- Trên máy tính bạn muốn chạy debug chương trình nào bạn làm
như sau: Click phải chuột vào thư mục scr\Debug As\Debug
Configurations… như hình bên dưới

Hình 4.23. Chọn ứng dụng cần debug


Chọn tùy chọn Always prompt to pick device

98
Hình 4.24. Chọn tùy chọn thiết bị debug

Hình 4.23. Chọn thiết bị thực tế để đổ chương trình debug


Sau khi chọn OK bạn sẽ thấy chương trình được đổ xuống thiết bị
của chúng ta và chúng ta dễ dàng kiểm tra trên thiết bị thật đang sử dụng.
99
100
Chương 5
CÁC THIẾT KẾ GIAO DIỆN NGƯỜI DÙNG -
UI LAYOUTS

Ở phần này, chúng ta sẽ tìm hiểu các thành phần cấu tạo của giao
diện người dùng (giao diện của ứng dụng cho phép người dùng tương tác
để thực hiện các chức năng mong muốn) và cách tạo giao diện từ đơn
giản đến phức tạp. Thiết kế giao diện cho ứng dụng là thiết kế các thành
phần hiển trị trên trang màn hình (screen), các thành phần này chính là
View và ViewGroup.
Có nhiều kiểu View và ViewGroup khác nhau, nhưng chúng đều kế
thừa từ class android.view và được gọi chung là các Widget. Mọi widget
đều có chung các thuộc tính cơ bản như cách trình bày vị trí, background,
kích thước, lề,… Trong thiết kế giao diện, các sreeen luôn được bố trí
theo một kiểu cấu trúc phân cấp như hình dưới:

Hình 5.1. Tổ chức View và ViewGroup


Từ hình trên ta thấy xuất phát của mỗi screen thường là ViewGroup
(bản thân ViewGroup cũng chính là một View-Widget nhưng nó có thế
chứa các Widget khác bên trong nó), bên trong ViewGroup gốc này sẽ
chứa các View và các ViewGroup khác. Một số View thường được sử
dụng trong thiết kế giao diện ứng dụng như: TextView, Button,
RadioButton, CheckBox,… Các ViewGroup thường dùng chính là các
Layout dùng để sắp xếp các widget khác bên trong nó, một số Layout
phổ biến như: GridLayout, LinearLayout, RelativeLayout,
TableLayout,…

101
Thông thường, giao diện của mỗi screen sẽ được thiết kế trong một
file XML (Ví dụ file activity_main.xml nằm ở thư mục res/layout). Nội
dung bên trong của các file XML như sau:

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>
</LinearLayout>
Khi chạy ứng dụng sẽ load file XML trong hàm “onCreate(Bundle
savedInstanceState)” sử dụng phương thức
“setContentView(R.layout.activity_main)” để xây dựng giao diện, dưới
đây là giao diện khi chạy ứng dụng với code file XML ở trên.

102
Hình 5.2. Giao diện màn hình thiết kế Layout
Cách xây dựng giao diện bằng cách thiết kế trong file XML khá dễ
dàng, tuy nhiên đôi lúc nó không thể đáp ứng các yêu cầu phức tạp. Còn
một cách khác là xây dựng giao diện khi chạy ứng dụng (thường dùng
cho các ứng dụng có yêu cầu tùy biến giao diện, đặc biệt là các game).
Tới đây bạn đã tìm hiểu sơ lược về các thành phần cấu tạo nên giao
diện cho ứng dụng, cách thiết kế cũng như cách ứng dụng load giao diện.
Tiếp theo, chúng ta sẽ tìm hiểu chi tiết về các View và ViewGroup để
thiết kế giao diện đáp ứng các yêu cầu của ứng dụng. Như đã đề cập ở
trên, các giao diện cho ứng dụng thường có gốc là ViewGroup do đó
chúng ta sẽ bắt đầu tìm hiểu từ ViewGroup.
ViewGroup cho phép chứa các View bên trong nó, một ViewGroup
bắt nguồn từ lớp nền của Android là android.view.ViewGroup. Android
hỗ trợ các ViewGroup sau:
- LinearLayout
- AbsoluteLayout
- TableLayout
- RelativeLayout
- FrameLayout
103
5.1. LINEARLAYOUT
Đây là Layout được sử dụng phổ biến nhất để thiết kế giao diện,
LinearLayout cho phép bố trí các widget (View) theo dạng danh sách đơn
ngang hoặc dọc. Các widget sắp xếp trong LinearLayout có vị trí không
cố định, do đó khi thay đổi kích thước màn hình hiển thị các widget vẫn
sẽ nằm cân đối. LinearLayout cho phép sắp xếp các widget theo dạng dọc
hoặc ngang, điều này phụ thuộc vào giá trị được gán cho thuộc tính
android:orientation
- Nếu giá trị là horizontal thì các widget sẽ được sắp xếp theo một
hàng ngang.
- Nếu giá trị là vertical thì các widget sẽ được sắp xếp theo một
hàng dọc.
Mỗi View và ViewGroup thường có một số thuộc tính chung, bảng
dưới mô tả một số thuộc tính cơ bản thường dùng.
Bảng 5.1. Một số thuộc tính cơ bản của Layout
Thuộc tính Mô tả
layout_width Quy định độ rộng của widget
layout_height Quy định độ cao của widget
layout_margintop Quy định khoảng cách bên trên của widget
layout_marginbottom Quy định khoảng cách bên dưới của widget
layout_marginleft Quy định khoảng cách bên trái của widget
layout_marginright Quy định khoảng cách bên phải của widget
layout_gravity Quy định vị trí của widget
layout_weight Quy định khoảng không gian layout phân chia
cho widget
Hình ảnh minh hoạ cho margin và padding

104
Hình 5.3. Hình minh họa hiển thị Widget
Để hiểu rõ hơn về LinearLayout chúng ta sẽ tìm hiểu các ví dụ dưới
đây. Lưu ý: Các ví dụ chủ yếu phân tích các Layout do đó sẽ dùng
phương pháp thiết kế giao diện từ file XML, không hướng dẫn lập trình
chức năng ở phần này.
Ví dụ 1: Thiết kế screen gồm bốn cột màu, có độ rộng bằng nhau
Code file activity_main.xml:
//Đoạn code này chương trình tự sinh khi chọn LinearLayout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightsum="100">

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
105
android:background="#aa0000"
android:gravity="center_horizontal"
android:layout_weight="25"/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#00aa00"
android:gravity="center_horizontal"
android:layout_weight="25"/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#0000aa"
android:gravity="center_horizontal"
android:layout_weight="25"/>

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#aaaa00"
android:gravity="center_horizontal"
android:layout_weight="25"/>
</LinearLayout>
Code trên ta thấy thuộc tính orientation được gán giá trị là
horizontal để các widget trong nó được sắp xếp theo chiều ngang.
Thuộc tính android:weightsum được sử dụng để tính tổng weight
của các widget trong LinearLayout. Thuộc tính android:layout_weight
(của các widget nằm bên trong LinearLayout) là độ rộng của widget này
106
so với cá widget khác, ta thấy ở code trên giá trị của thuộc tính
android:layout_weight ở cả bốn TextView là “25”, do đó độ rông của
mỗi cột màu là như nhau và chiếm hết toàn bộ màn hình vì tổng của các
android:layout_weight bằng android:weightsum. Nếu bạn muốn các cột
màu có độ rộng như nhau nhưng chỉ chiếm một nửa màn hình, hãy tăng
gấp đôi giá trị của android:weightsum = “200”. Hãy thử và kiểm tra kết
quá có đúng như bạn nghĩ không!
Lưu ý: Giá trị của thuộc tính android:layout_weight không làm cho
widget nhỏ hơn giá trị được gán ở android:layout_width. Do đó, khi thiết
kế độ rông của widget bằng thuộc tính android:layout_weight thì cần
phải gán cho thuộc tính android:layout_width một giá trị thật nhỏ.
Hai thuộc tính android:layout_width=“match_parent” và
android:layout_height=“match_parent” để LinerLayout chiếm toàn bộ
không gian của widget cha (ở đây là màn hình hiển thị). Ở các TextView
cũng có thuộc tính android:layout_height=“match_parent” để chiều cao
của TextView chiếm toàn bộ chiều cao của cha nó (ở đây là
LinearLayout).
Kết quả hiển thị trên máy ảo:

Hình 5.4. Giao diện màn hình thiết kế Linear Layout theo chiều dọc
107
Ví dụ 2: Thiết kế screen có bốn hàng màu, với hàng màu đỏ và
vàng có độ cao gấp đôi so với hai hàng màu còn lại
Code file activity_main.xml:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="120">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:background="#aa0000"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="20"
android:background="#00aa00"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="20"
android:background="#0000aa"
android:gravity="center_horizontal"/>
<TextView
108
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:background="#aaaa00"
android:gravity="center_horizontal"/>
</LinearLayout>
Thuộc tính android:orientation của LinearLayout được gán giá trị là
vertical do đó các widget trong layout sẽ được sắp xếp theo chiều dọc. Ở
ví dụ này, các hàng màu có độ cao không giống nhau, nên giá trị của
thuộc tính android:layout_weight ở mỗi TextView là khác nhau (“40” và
“20”). Các TextView có thuộc tính android:layout_weight= “40” sẽ cao
gấp đôi các TextView có thuộc tính android:layout_weight= “20”. Nếu
không muốn các hàng màu này chiếm hết màn hình, bạn hãy thay đổi giá
trị thuộc tính android:weightsum của LinearLayout và kiểm tra kết quả.
Kết quả hiển thị trên máy ảo:

Hình 5.5. Thiết kế giao diện Linear theo chiều ngang


109
Ví dụ 3: Thiết kế screen là tổ hợp bốn hàng màu và bốn cột màu đã
làm ở các ví dụ một và ví dụ hai.
Code file activity_main.xml:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="100">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="50"
android:weightSum="100">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#aa0000"
android:gravity="center_horizontal"
android:layout_weight="25"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#00aa00"
android:gravity="center_horizontal"
android:layout_weight="25"/>
<TextView
110
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#0000aa"
android:gravity="center_horizontal"
android:layout_weight="25"/>
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:background="#aaaa00"
android:gravity="center_horizontal"
android:layout_weight="25"/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="50"
android:weightSum="120"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:background="#aa0000"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="20"
android:background="#00aa00"

111
android:gravity="center_horizontal"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="20"
android:background="#0000aa"
android:gravity="center_horizontal"/>
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="40"
android:background="#aaaa00"
android:gravity="center_horizontal"/>
</LinearLayout>
</LinearLayout>
Như đã nói ở trên, các ViewGroup có thể chứa cả View và
ViewGroup bên trong nó, ở ví dụ ta sẽ lồng các LinearLayout vào nhau
để đáp ứng yêu cầu thiết kế, hình dưới phác thảo việc lồng các
LinearLayout lại với nhau:

Horizontal

Vertical

Vertical

Hình 5.6. Hình phát thảo lồng các Layout vào nhau

112
Kết quả hiển thị trên máy ảo:

Hình 5.7. Kết quả thiết kế lồng vào nhau giữa các Linear Layout
Trong thực tế, thiết kế giao diện cho các ứng dụng thường phức
tạp, chỉ sử dụng một Layout để chưa các widget không để đáp ứng yêu cầu
thiết kế. Do đó, giải pháp lồng các Layout vào nhau sẽ giúp bạn giải quyết
hầu hết các bài toán giao diện. Ngoài LinearLayout còn một số Layout
khác hỗ trợ thiết kế giao diện mà chúng ta sẽ thảo luận dưới đây.

5.2. ABSOLUTELAYOUT
Layout này cho phép sắp xếp các widget vào vị trí bất kì trong
layout dựa vào hai thuộc tính tọa độ x, y. Tuy nhiên, kiểu layout này rất ít
khi được dùng bởi vì tọa độ của các widget luôn cố định và sẽ không tự
điều chỉnh được tỷ lệ khoảng cách giữa các widget. Khi chuyển ứng dụng
sang một màn hình có kích thước với màn hình thiết kế ban đầu thì vị trí
của các widget sẽ không còn được chính xác như ban đầu. Do đó, theo ý
kiến người viết bạn nên hạn chế sử dụng AbsoluteLayout mà nên dùng
các Layout khác.

113
Ví dụ: Thiết kế screen với bốn Button nằm ở bốn góc của screen và
một Button nằm ở giữa screen
Code file activity_main.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<AbsoluteLayoutxmlns:android="http://schemas.android.com/apk/
res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="8dp"
android:layout_y="8dp"
android:text="1"/>

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="5dp"
android:layout_y="408dp"
android:text="3"/>

<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="238dp"
114
android:layout_y="406dp"
android:text="4"/>

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="242dp"
android:layout_y="9dp"
android:text="2"/>

<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_x="118dp"
android:layout_y="192dp"
android:text="5"/>

</AbsoluteLayout>
Ta thấy rằng vị trí các Button trong Layout được xác định bới các
thuộc tính android:layout_x và android:layout_y, hình dưới sẽ minh hoạt
việc xác định các giá trị x,y:

115
layout_y

layout_x
Button

Hình 5.8. Thiết kế widget trong AbsoluteLayout


Kết quả hiển thị trên máy ảo với màn hình thiết kế 3.2” (320 x 480)
và màn hình hiển thị máy ảo 3.2” (320 x 480)

Hình 5.9. Kết quả hiển thị các widget trên màn hình thiết kế và máy ảo
320x480
Từ hình trên ta thấy khi màn hình thiết kế và màn hình sử dụng
giống nhau sẽ có kết quả chính xác vị trí các Button như lúc thiết kế.

116
Kết quả hiển thị trên máy ảo với màn hình thiết kế 2.7” (240 x 320)
và màn hình hiển thị máy ảo 3.2” (320 x 480)

Hình 5.10. Kết quả hiển thị các widget trên màn hình thiết kế và máy ảo
240x320
Từ kết quả trên ta thấy khi màn hình hiển thị khác màn hình thiết
kế thì vị trí các Button không còn ở những vị trí mong muôn khi thiết kế.
Do đó, AbsoluteLayout không được khuyến khích sử dụng khi thiết kế
giao diện.

5.3. TABLELAYOUT
Layout này được sử dụng khi cần thiết kế một bảng chứa dữ liệu
hoặc cần bố trí các widget theo các row và column. Cách sử dụng Layout
này khá đơn giản kết hợp với các thẻ TableRow. Các ô trong bảng có thể
chứa các thành phần khác như các widget và cả các Layout khác.
Ví dụ: Thiết kế screen đăng nhập sử dụng TableLayout
Code file activity_main.xml
<?xmlversion="1.0"encoding="utf-8"?>
<TableLayoutxmlns:android="http://schemas.android.com/apk/res/
android"
android:id="@+id/TableLayout1"

117
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="*">

<TableRowandroid:layout_width="match_parent">
<TextView
android:layout_width="wrap_content"
android:text="User"/>
<EditTextandroid:layout_width="match_parent"/>
</TableRow>

<TableRowandroid:layout_width="match_parent">
<TextView
android:layout_width="wrap_content"
android:text="Pass"/>
<EditTextandroid:layout_width="match_parent"
android:password="true"/>
</TableRow>

<TableRowandroid:layout_width="match_parent">
<Button
android:layout_width="wrap_content"
android:text="OK"/>
<Button
android:layout_width="wrap_content"
android:text="Thoát"/>
</TableRow>
</TableLayout>

118
Từ code trên ta thấy để tạo thêm các hàng cho bảng ta sẽ dùng thẻ
<TableRow></TableRow>, các widget ở mỗi hàng sẽ nằm trong cặp thẻ
này. Số cột của bảng phụ thuộc vào hàng có số widget lớn nhất. Bảng sử
dụng trong ví dụ này có kích thước là ba hàng và hai cột.
Thuộc tính android:StretchColumns=“*” cho phép căn đều độ rộng
các cột trong bảng. Ngoài ra, EditText để nhập mật khẩu sử dụng thuộc
tính android:pasword=“true” để hiển thị các dữ liệu được nhập vào ở
dạng ký tự “*”.
Kết quả hiển thị trên máy ảo:

Hình 5.11. Giao diện đăng nhập sử dụng Table Layout

5.4. RELATIVELAYOUT
Layout này cho phép sắp xếp các widget theo vị trí tương đối giữa
các widget hoặc với Layout chứa nó. Dựa vào việc xác định theo vị trí
tương đối mà RetaliveLayout cũng không phụ thuộc vào kích thước của
screen thiết bị. Thường nó dựa vào id của các widget khác để sắp xếp vị
trí tương đối. Do đó, khi sử dụng RelativeLayout, bạn phải chú ý đặt id
cho chuẩn xác, nếu sau khi thiết kế xong giao diện mà bạn lại đổi id của

119
các widget thì giao diện sẽ bị xáo trộn (do đó, khi đổi id của một widget
phải đối các tham chiếu của widget khác đến nó). Ngoài ra, nó còn có ưu
điểm là giúp tiết kiệm layout sử dụng nhằm mục đích giảm lượng tài
nguyên sử dụng khi load đồng thời đẩy nhanh quá trình xử lý.
Ví dụ: Thiết kế screen nhập ghi chú
Code file activity_main.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="Ghi chú"
android:textAppearance="?android:attr/textAppearanceLarge"/>

<EditText
android:id="@+id/editText2"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/textView1"

120
android:hint="Nhập ghi chú vào đây"
android:inputType="textMultiLine">

<requestFocus/>
</EditText>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/editText2"
android:text="Lưu"/>

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button1"
android:layout_alignBottom="@+id/button1"
android:layout_alignParentRight="true"
android:text="Hủy"/>

</RelativeLayout>

121
Một số thuộc tính thường dùng khi sử dụng RelativeLayout:

Tên thuộc tính Mô tả


android:layout_above Đặt widget hiện tại nằm kế sau
widget có id được chỉ ra
android:layout_alignBaseline Đặt widget này lên cùng dòng
với widget có id được chỉ ra
android:layout_alignBottom Canh sao cho đáy của widget
hiện thời trùng với đáy của
widget có id được chỉ ra
android:layout_alignLeft Đặt cạnh trái của widget hiện
thời trùng với cạnh trái của
widget có id được chỉ ra
android:layout_alignParentBottom Nếu thiết lập là true thì widget
hiện thời sẽ được canh xuống
đáy của widget chứa nó
android:layout_alignParentLeft Nếu được thiết lập là true thì
widget hiện thời sẽ canh trái so
với widget chứa nó
android:layout_alignParentRight Nếu được thiết lập là true thì
widget hiện thời sẽ canh phải
so với widget chứa nó
android:layout_alignParentTop Nếu được thiết lập là true thì
widget hiện thời sẽ canh lên
đỉnh widget chứa nó
android:layout_alignRight Canh cạnh phải của widget
hiện thời trùng với cạnh phải
của widget có id được chỉ ra
android:layout_alignTop Canh đỉnh của widget hiện thời
trùng với đỉnh của widget có id
được chỉ ra
android:layout_alignWithParentIfMissing Nếu thiết lập là true, thì widget
sẽ được canh theo widget chứa
nó nếu các thuộc tính canh của
widget không có

122
android:layout_below Đặt widget hiện thời ngay sau
widget có id được chỉ ra
android:layout_centerHorizontal Nếu thiết lập là true thì widget
hiện thời sẽ được canh giữa
theo chiều ngang widget chứa

android:layout_centerInParent Nếu thiết lập là true thì widget
hiện thời sẽ được canh chính
giữa theo chiều phải trái và
trên dưới so với widget chứa

android:layout_centerVertical Nếu thiết lập là true thì widget
hiện thời sẽ được canh chính
giữa theo chiều dọc widget
chứa nó
android:layout_toLeftOf Đặt cạnh phải của widget hiện
thời trùng với cạnh trái của
widget có id được chỉ ra
android:layout_toRightOf Đặt cạnh trái của widget hiện
thời trùng với cạnh phải của
widget có id được chỉ ra

Giải thích code:


<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/editText2"
android:text="Lưu"/>
Ta thấy, Button1 (có Text là “Lưu”) sẽ canh trái với
RelativeLayout và nằm bên dưới widget có id là editText2 (EditText để
nhập ghi chú). Các widget khác cũng được xác định vị trí cách tương tự
như button1.

123
Kết quả hiển thị trên máy ảo:

Hình 5.12. Màn hình nhập ghi chú sử dụng Relative Layout

5.5. FRAMELAYOUT
Frame Layout là loại Layout cơ bản nhất. Đặc điểm của
FrameLayout là khi đặt các widget lên screen thì các widget này luôn
nằm ở góc trái trên cùng, nó không cho phép chúng ta thay đổi vị trí của
các widget. Các widget đưa vào sau sẽ nằm chồng lên các widget đưa
vào trước theo thứ tự trong file XML.
Hình dưới sẽ minh họa về FrameLayout:

124
Hình 5.13. Sắp xếp các button trong Frame Layout
Code file activity_main.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="288dp"
android:background="#000aa0"
android:text="Button1"/>
<Button
android:id="@+id/button2"
android:layout_width="198dp"
125
android:layout_height="127dp"
android:background="#00aaa0"
android:text="Button2"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ffffff"
android:text="Button3"/>
</FrameLayout>
Theo thứ tự code trong file xml, Button1 ở đầu tiên nên trên giao diện
nó sẽ nằm dưới cùng, Button2 nằm ở giữa và Button1 nằm trên cùng.
Bài tập tổng hợp, dựa vào các Layout đã tìm hiểu ở trên, hãy thiết
kế Screen như hình dưới.

Hình 5.14. Giao diện ứng dụng tổng hợp các layout

126
Chương 6
CÁC ĐIỀU KHIỂN GIAO DIỆN NGƯỜI DÙNG
CƠ BẢN (UI CONTROLS)

Giao diện người dùng của một ứng dụng Android là một thứ mà
người dùng có thể thấy và tương tác với nó. Bạn đã tìm hiểu về các thiết
kế layout khác nhau và bạn có thể bố trí các view của bạn trên một
activity. Chương này sẽ hướng dẫn chi tiết về các view khác nhau.
View là một đối tượng vẽ cái gì đó lên màn hình để người dùng
có thể tương tác. Viewgroup là một đối tượng tổ chức các đối tượng
View (và Viewgroup) khác để định nghĩa thiết kế (layout) giao diện của
người dùng.
Bạn định nghĩa thiết kế (layout) của bạn trong file XML được thể
hiện với cấu trúc con người có thể đọc được, tương tự HTML. Ví dụ, một
thiết kế dọc đơn (simple vertical layout) với một text view và một button
(nút nhấn) giống như sau:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a TextView" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a Button" />
</LinearLayout>

127
6.1. CÁC ĐIỀU KHIỂN GIAO DIỆN NGƯỜI DÙNG (UI CONTROLS)
CƠ BẢN CỦA ANDROID
Một số điều khiển sau được cung cấp bởi Android cho phép ta xây
dựng giao diện đồ họa người dùng cho ứng dụng của chúng ta.

STT UI Control và mô tả
1 TextView
Điều khiển này được sử dụng để hiển thị văn bản (text) cho người
dùng
2 EditText
Điều khiển này định nghĩa trước lớp con của TextView bao gồm
các khả năng chỉnh sửa
3 AutoCompletTextView
Là một view tương tự như EditText ngoại trừ nó hiển thị một danh
sách được đề nghị để hoàn thành trong lúc bạn đang nhập.
4 Button
Là một nút nhấn có thể được nhấn, click bởi người dùng khi thực
thi một hoạt động.
5 ImageButton
Tương tự nút nhấn nhưng có hình ảnh.
AbsoluteLayout cho phép bạn chỉ định vị trí chính xác của các con
của nó.
6 CheckBox
Một công tắc on/off có thể được thay đổi bởi người dùng. Bạn nên
sử dụng các checkboxes khi người dùng có thể tùy chọn một nhóm
mà không bắt buộc sự loại trừ lẫn nhau.
7 ToggleButton
Một nút nhấn on/off có đèn hiển thị
8 RadioButton
RadioButton có hai trạng thái: hoặc là được đánh dấu (checked)
hoặc không được đánh dấu (unchecked)
9 RadioGroup
RadioGroup được dùng để nhóm một hoặc nhiều RadioButton với
nhau
128
10 ProgressBar
ProgressBar cung cấp một phản hồi trực quan về các công việc
đang xử lý, ví dụ bạn đang thực hiện một công việc ở trạng thái
nền.
11 Spinner
Một danh sách thả xuống cho phép người dùng chọn một giá trị từ
một tập hợp danh sách đó.
12 TimerPicker
Là một view cho phép người dùng chọn một thời gian của ngày,
trong chế độ 24 giờ hay chế độ AM/PM
13 DatePicker
Là một view cho phép người dùng chọn ngày.

6.2. TẠO CÁC UI CONTROL


Như đã giải thích ở chương trước, một đối tượng View có thể có
một ID duy nhất để nhận dạng. Cú pháp để tạo một ID trong file XML
như sau:
android:id= “@+id/text_id”
Để tạo một UI Control/View/Widget bạn phải định nghĩa một
view/widget trong file layout: res/layout/activity_main.xml và tạo cho nó
một ID duy nhất. Ví dụ ở đây ta tạo một TextView như sau:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/text_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

129
android:text="I am a TextView" />
</LinearLayout>
Khi lập trình, trong file .java (ví dụ: scr/MainActivity.java) tạo một
đại diện cho đối tượng Control này và “buột” nó từ layout, Ví dụ ta đặt
tên cho đại diện của TextView là myText và ta muốn buột nó vào
TextView của ta, ta sử dụng như sau:
TextView myText = (TextView) findViewById(R.id.text_id);
Trên đây ta ví dụ cho trường hợp Control của ta là một TextView,
các đối tượng khác làm tương tự.
 TextView
Một TextView hiển thị văn bản cho người dùng xem và nó tùy
chọn cho phép hiệu chỉnh nó. Một TextView là một trình chỉnh sửa văn
bản hoàn chỉnh, tuy nhiên lớp cơ bản này không cho phép ta chỉnh sửa.
Các thuộc tính của TextView
Sau đây là các thuộc tính quan trọng liên quan đến điều khiển
TextView. Bạn có thể kiểm tra tài liệu chính thức của Android về đầy đủ
các thuộc tính này và các phương pháp liên quan để bạn có thể thay đổi
các thuộc tính này trong thời gian chạy chương trình.

Thuộc tính Mô tả
android:id Đây là ID duy nhất nhận dạng điều khiển
này
android:capitalize Nếu được chọn, có nghĩa là TextView sẽ có
một phương thức nhập văn bản và nó sẽ tự
động viết hoa những gì người dùng nhập
vào.
- Nếu giá trị là 0: Không tự động viết hoa
bất cứ điều gì
- Nếu giá trị là 1: Viết hoa ký tự đầu tiên
trong câu.
- Nếu giá trị là 2: Viết hoa ký tự đầu tiên
của từ.
- Nếu giá trị là 3: Viết hoa tất cả các ký tự
android:cursorVisible Làm con trỏ hiện (mặc định) hoặc ẩn. Mặc
định là giá trị false
130
android:editable Nếu được thiết lập là true, có nghĩa là
TextView có phương thức nhập dữ liệu
android:fontFamily Font cho văn bản (Font được đặt tên bằng
chuỗi (string))
android:gravity Quy định cách sắp xếp văn bản theo các
trục x, y của view trong trường hợp văn bản
nhỏ hơn view
android:hint Một văn bản gợi ý khi Text đang rỗng.
android:inputType Loại dữ liệu được đặt trong vùng văn bản:
phone, date, time, number, password…
android:maxHeight Chiều cao tối đa của Textview tính theo
pixel
android:maxWidth Chiều rộng tối đa của TextView tính theo
pixel
android:minHeight Chiều cao tối thiểu của TextView
android:minWidth Chiều rộng tối thiểu
android:password Có hay không các ký tự được hiển thị như
các dấu chấm password khi nhập. Nó có hai
thuộc tính true (hiển thị ký tự nhập giống
như chấm password) và false.
android:text Văn bản để hiển thị
android:phoneNumber Nếu được thiết lập, nó cho phép TextView
có phương thức nhập số điện thoại. Nó có
hai giá trị: true và false
android:textAllCaps Trình bày văn bản theo chữ in hoa. Có hai
giá trị True và false
android:textColor Màu văn bản. Có thể là một giá trị màu,
theo các dạng “#rgb”, “#argb”, “#rrggbb”
hoặc “#aarrggbb”.
android:textColorHighlight Màu tô của văn bản
android:textColorHint Màu của văn bản gợi ý. Có thể là một giá
trị màu theo các dạng “#rgb”, “#argb”,
“#rrggbb” hoặc “#aarrggbb”.

131
android:textlsSelectable Chỉ định nội dung văn bản không thể hiệu
chỉnh có thể chọn. Có hai giá trị true và
false
android:textSize Size của văn bản. Kích thước này thường
được khuyên dùng đơn vì là “sp” (scaled
pixels)
android:textStyle Kiểu văn bản. Bạn có thể chọn một trong
các giá trị sau, nếu bạn chọn hơn một giá trị
cùng lúc thì dùng dấu “|” giữa các giá trị
chọn:
Normal: 0 (bình thường)
Bold: 1 (in đậm)
Italic: 2 (in nghiêng)
android:typeface Kiểu chữ của văn bản. Bạn có thể chọn một
hoặc nhiều giá trị sau: (Chọn nhiều hơn
một giá trị bạn dùng dấu “|” giữa các giá trị)
Nomal: 0
Sans: 1
Serif: 2
Monospace:3

Ví dụ:
Ta có một Textview có nội dung là “hello world” khi người dùng
click vào TextView này thì nó hiện ra câu thông báo: “You have clicked
the Label: + nội dung của TextView”. Thông báo này sẽ tự động biến
mất sau một khoảng thời gian. Ta tiến hành các bước đơn giản sau:
Bước 1: Bạn sử dụng Eclipse IDE để tạo một ứng dụng Android
tên là GUITextView1 dưới một đóng gói fivemen.com (Tên ứng dụng và
tùy chọn đóng gói bạn có thể đánh tùy ý, tuy nhiên bạn nên để ý nó) như
được giải thích trong chương tạo ví dụ “Hello World”
Ta tạo ứng dụng như sau và lưu ý các file, tên đặt trong hình chữ
nhật

132
Hình 6.1. Giao diện ứng dụng GUITextView1
Bước 2: Hiệu chỉnh nội dung của fileres/layout/activity_main.xml.
Trong ví dụ này, phần code phát sinh tự động ta koong phải thêm bớt gì
thêm nữa.

<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/tv1"
133
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>

</LinearLayout>

Bước 3: Thiết lập nội dung của file res/values/strings.xml để định


nghĩa các hằng số
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUITextView1</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>

</resources>

Bước 4: Hiệu chỉnh file scr/MainActivity.java để nhập sự kiện


click vào TextView
Sau đây là nội dung được hiệu chỉnh trong main activity:
src/fivemen.com.GUITextView1/MainActivity.java. File này bao gồm
các dòng lệnh cơ bản tự chương trình phát sinh:
package fivemen.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

134
public class MainActivity extends Activity {

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Tim TextView hello world
TextView hello_tv=(TextView)findViewById(R.id.tv1);
//Khai bao bien Label la chuoi de lay thong tin noi dung cua
hello_tv
final String Label=hello_tv.getText().toString();
//Thiet lap xu ly su kien khi Click vao TextView hello_tv
hello_tv.setOnClickListener(new View.OnClickListener() {
@Override
Publicvoid onClick(View v) {
// Hien thi thong tin ra man hinh dung doi tuong Toast
Toast.makeText(getApplicationContext(), "You have
clicked the label: " + Label, Toast.LENGTH_LONG).show();
}
});

}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}
Giải thích
135
- Dòng lệnh
TextView hello_tv=(TextView)findViewById(R.id.tv1);
có nghĩa là ta khai báo một TextView hello_tv và buột nó vào tv1
(TextView hiển thị chữ Hello World bên file xml).
- Dòngfinal String Label=hello_tv.getText().toString(); có nghĩa
là khai báo chuổi Label và gán cho nó giá trị chính là nội dung của
TextView hello_tv thông qua .getText()
- Hiển thị thông tin ra màn hình một khoảng thời gian rồi tự động
biến mất ta dùng đối tượng Toast.
Toast.makeText(getApplicationContext(), "You have clicked the
label: " + Label, Toast.LENGTH_LONG).show();
Ở đây nội dung hiện thị sẽ là dòng: “You have clicked the label:”
kết hợp với nội dung của chuỗi Label.
Bước 5:
Chạy mô phỏng chương trình:
Tại Thư mục Project GUITextView1 ta Drag chuột vào dòng scr
sau đó Click phải chuột chọn Run As\1 Android Application

Hình 6.2. Thao tác chạy mô phỏng chương trình


Khi đó, máy ảo sẽ được load ra và chương trình như sau:
136
Hình 6.3. Màn hình chạy chương trình GUITextView1
Bây giờ chúng ta hãy nhấn vào nhãn “Hello World”, giao diện sẽ
hiện ra như sau:

Hình 6.4. Giao diện chương trình GUITextView1 khi nhấn Hello World
Bài tập
137
Tiếp tục thiết kế một yêu cầu từ điều khiển TextView: Ta tạo thêm
một TextView mới trên giao diện hiện có như sau:

Hình 6.5. Giao diện yêu cầu của bà tập TextView2


Khi nhấn chuột vào dòng “CLICK ON ME” thì chuỗi chữ CLICK
ON ME được thay thế bằng chuỗi chữ “THANK YOU SO MUCH” với
size là 22.
- Tại file activity_main.xml ta thêm code cho TextView mới
như sau:
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_id"
android:layout_centerHorizontal="true"
android:layout_marginTop="144dp"
android:text="@string/txt_tv2"
android:textSize="20sp"
android:textColor="#FF00C2"/>
- Tại file String.xml ta thêm biến txt_tv2 như sau:

138
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUITextView</string>
<string name="action_settings">Settings</string>
<string name="hello_world">XIN CHÀO MỌI NGƯỜI</string>
<string name="txt_tv2">CLICK ON ME</string>

</resources>
- Tại file MainActivity.java ta thêm code cho TextView2 như sau:
//--text view Click on me
final TextView tv2= (TextView)findViewById(R.id.textView1);

tv2.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// Thay đổi nội dung hiện thị của tv2 và thay đổi size chữ
tv2.setText("THANK YOU SO MUCH");
tv2.setTextSize(22);

});

 EditText
EditText là nơi cho phép ta có thể hiệu chỉnh nội dung văn bản.
Thông thường dùng EditText để nhập văn bản vào.
Các thuộc tính của EditText

139
Các thuộc tính sau là các thuộc tính quan trọng liên quan đến
EditText. Bạn có thể tham khảo tài liệu chính thức của Android phiên
bản mới nhất để có bản liệt kê đầy đủ và các phương thức liên quan.
Được thừa kế từ lớp android.widget.TextView:

Thuộc tính Mô tả
android:autoText Nếu được thiết lập thì TextView có phương
thức nhập văn bản tự động hiệu chỉnh đúng
cho một số lỗi chính tả thường gặp.
android:drawableBottom Đây là vùng có thể vẽ được vẽ bên dưới văn
bản
android:drawableRight Đây là vùng có thể vẽ được vẽ bên phải văn
bản
android:editable Nếu được thiết lập thì TextView có một
phương pháp nhập
android:Text Đây là Text (văn bản) để hiển thị
android:background Đây là vùng có thể vẽ được sử dụng như
background (nền)
android:contentDescription Xác định Text mô tả chính nội dung của
View
android:id Cung cấp một tên nhận dạng cho view này
android:onClick Đây là tên của phương thức khi View này
được click
android:visibility Các điều khiển này được khởi tạo có thể
được nhìn thấy
Ví dụ: Ta thiết lập một giao diện như hình sau, mỗi lần nhấn nút
Show the text nó sẽ hiện thị câu thông báo nội dung ta nhập ở mục
EditText.
Bước 1: Tạo một ứng dụng Android với tên là GUIEditText. Trong
phần giao diện ta sẽ cần một TextView, một EditText và một Button.

140
Hình 6.6. Giao diện bài tập về EditText
Hiệu chỉnh nội dung mặc định trong file
res/layout/activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<EditText
android:id="@+id/editText1"

141
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="62dp"
android:ems="10"
android:text="@string/enter_text"
android:inputType="text">

<requestFocus/>
</EditText>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText1"
android:layout_centerVertical="true"
android:layout_marginLeft="26dp"
android:text="@string/txt_bt"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="45dp"
android:layout_marginTop="34dp"

142
android:text="@string/txt_tv"
android:textSize="20sp"
android:textColor="#9f2010"/>

Bước 2: Định nghĩa các hằng số yêu cầu trong file


res/values/strings.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUIEditText</string>
<string name="action_settings">Settings</string>
<string name="txt_tv">Example about EditText</string>
<string name="txt_bt">Show the text</string>
<string name="enter_text">change text</string>

</resources>

Bước 3: Viết chương trình nhập các đối tượng widget, phương
thức click để khi ta nhấn vào nút Show the Text nó sẽ hiện thông báo nội
dung của EditText. Chỉnh sửa file scr/MainActivity.java
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
importandroid.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends Activity {

143
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//Tìm nút nhấn Button1


Button btn =(Button)findViewById(R.id.button1);
//Thiết lập phương thức xử lý khi nút nhấn btn được click
btn.setOnClickListener(newOnClickListener(){
publicvoid onClick(View v) {

//Tìm editText1
EditText eText = (EditText) findViewById(R.id.editText1);
//Khai báo một biến chuỗi nd_edit chứa nội dung của eText
String nd_edit=eText.getText().toString();
//Dùng đối tượng Toast để hiện thị thông báo ra màn hình, nội
dung hiển thị chính
//là nội dung của EditText

Toast msg=Toast.makeText(getBaseContext(),
nd_edit, Toast.LENGTH_LONG);
msg.show();
}
});

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {

144
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}

Bước 4: Chạy ứng dụng với máy ảo ta và ta nhận một nội dung bất
kỳ vào EditText, ta có giao diện chương trình như sau (Ở đây, ta nhập
chuỗi chữ Everything is beginning)

Hình 6.7. Giao diện chạy chương trình ví dụ về EditText


Khi ta nhấn vào nút Show the text kết quả như sau:

145
Hình 6.8. Giao diện chạy chương trình ví dụ về EditText khi nhấn Button

 AutoCompleteTextView
AutoCompleteTextView là một View tương tự như EditText, ngoại
trừ nó hiển thị một danh sách các đề nghị tự động trong lúc người dùng
đang nhập văn bản.

Thuộc tính Mô tả
android:completionHint Định nghĩa gợi ý được hiển thị
trong danh sách thả xuống
android:completionHintView Định nghĩa view gợi ý được hiển
thị trong danh sách thả xuống
android:completionThreshold Định nghĩa số lượng ký tự người
dùng phải gõ để bắt đầu lọc và hiển
thị trong danh sách thả xuống
android:dropDownAnchor Đây được xem là View giữ danh
sách tự động hoàn chỉnh thả xuống
android:dropDownHeight Xác định chiều cao cơ bản của
danh sách thả xuống
146
android:dropDownHorizontalOffset Số pixel danh sách thả xuống nên
lệch ngang
android:dropDownSelector Đây là sự chọn lựa trong danh sách
thả xuống
android:dropDownVerticalOffset Số pixel danh sách thả xuống nên
lệch dọc
android:dropDownWidth Bề rộng cơ bản của danh sách thả
xuống
android:popupBackground Thiết lập background
Ví dụ: Ta thiết lập giao diện gồm hai TextView và một
AutoCompleteTextView như sau.

Hình 6.9. Giao diện bài tập AutoCompleteTextView


Yêu cầu, khi ta nhập ký tự đầu tiên tên thành phố vào, nó sẽ xổ ra
danh sách thả xuống tên các thành phố có ký tự đầu tiên giống ký tự ta đã
nhập vào.
Ta lần lượt thực hiện các bước sau:
Bước 1: Tạo ứng dụng Android tên là AutoCPTextView.
Bước 2: Hiệu chỉnh nội dung file res/layout/activity_main.xml để
có giao diện như yêu cầu.
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"

147
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="25dp"
android:text="@string/example_autocompletetextview"
android:textColor="#5031F4"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/autoCompleteTextView1"
android:layout_below="@+id/textView2"
android:layout_marginTop="58dp"
android:text="@string/txt_tv1"
android:textSize="18sp"
android:textColor="#EC7500"/>

<AutoCompleteTextView
android:id="@+id/autoCompleteTextView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView1"
android:layout_marginTop="17dp"
android:ems="10">
148
<requestFocus/>
</AutoCompleteTextView>

</RelativeLayout>
Bước 3: Định nghĩa các hằng số yêu cầu trong file res/values/string.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">AutoCPTextView</string>
<string name="action_settings">Settings</string>
<string name="example_autocompletetextview">Example about
AutoCompleteTextView</string>
<string name="txt_tv1">Type your city name</string>

</resources>
Bước 4: Hiệu chỉnh file scr/MainActivity.java để xử lý các sự kiện
package com.example.autocptextview;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;

public class MainActivity extends Activity {


//khai báo một AutoCompleteTextView có tên là autocptv
AutoCompleteTextView autocptv;
//khai báo mảng citiescó kiểu dữ liệu là chuỗi chứa tên các thành
phố
String[] cities = { "HA NOI", "HO CHI MINH", "DA
NANG","CAN THO","HAI PHONG","VUNG TAU","BINH
DUONG" };
@Override
149
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Buột autocptv vào autoCompleteTextView1
autocptv=(AutoCompleteTextView)
findViewById(R.id.autoCompleteTextView1);

ArrayAdapter<String> adapter =
newArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line, cities);

autocptv.setThreshold(1);
autocptv.setAdapter(adapter);
}

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
/* Inflate the menu; this adds items to the action bar if it is present.
*/
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}
Giải thích
- Trong class MainActivity, đầu tiên bạn tạo một mảng có kiểu dữ
liệu là String để chứa tên của các thành phố:
String[] cities = {
"HA NOI",
"HO CHI MINH",
"DA NANG",
"CAN THO",

150
"HAI PHONG",
"VUNG TAU",
"BINH DUONG" };
- Đối tượng ArrayAdapter quản lý mảng các chuỗi được hiển thị
bởi AutoCompleteTextView. Ở đây, ta cho danh sách rớt xuống đơn giản
theo các thành phố có ký tự trùng khớp nên ta chọn
simple_dropdown_item_1line.
ArrayAdapter<String> adapter =
newArrayAdapter<String>(this,android.R.layout.simple_dropdown_item
_1line, cities);
- Phương thức .setThreshold() thiết lập số kí tự tối thiểu người
dùng phải gõ trước khi các đề nghị xuất hiện như một bảng thả xuống.
autocptv.setThreshold(1);
- Danh sách đề nghị hiển thị cho AutoCompleteTextView được
chứa từ đối tượng ArrayAdapter.
autocptv.setAdapter(adapter);
Bước 5: Chạy chương trình với máy ảo ta có giao diện như sau:

Hình 6.10. Giao diện khi chạy chương trình về AutoCompleteTextView

151
Gõ vào vùng AutoCompleteTextView ký tự “H” ta có giao diện
như sau:

Hình 6.11. Giao diện khi chạy chương trình về AutoCompleteTextView


 Button
Button là một nút nhấn đẩy có thể được nhấn, click bởi người dùng
để thực hiện một tương tác.
Các thuộc tính của Button bao gồm:
Các thuộc tính của nó được kế thừa từ lớp android.widget.TextView

Thuộc tính Mô tả
android:autoText Nếu được thiết lập, TextView này sẽ có
phương pháp nhập văn bản và có thể tự động
sửa các lỗi thông thường
android:drawableBottom Vùng có thể vẽ được vẽ bên dưới văn bản
android:drawableRight Vùng có thể vẽ được vẽ bên phải văn bản
android:editable Nếu được thiết lập TextView có một phương
pháp nhập văn bản.
android:text Đây là văn bản (text) hiển thị trên button
152
Các thuộc tính kế thừa từ lớp android.view.View

Thuộc tính Mô tả
android:background Vùng có thể vẽ được sử dụng như một
background
android:id Cung cấp một tên nhận dạng cho View
android:contentDescription Text mô tả chính nội dung của View
android:onClick Đây là tên của phương thức khi View được
click
android:visibility Điều khiển khả năng có thể nhìn thấy ban
đầu của view
Ví dụ: Ta thiết kế một giao diện như sau:
Trên Activity_main ta có một TextView, ba EditText và một Button.
Người dùng sẽ nhập các số bất kỳ vào ba EditText. Khi người dùng
nhấn Button “Calculate product of numbers” thì chương trình sẽ thực
hiện việc nhân ba số từ ba EditText và hiển thị kết quả thông qua thông
báo (dùng Toast).

Hình 6.12. Giao diện chương trình nhân ba số


153
Ta sẽ làm các bước như sau:
Bước 1: Tạo một ứng dụng Android có tên là GUIButton. Và tạo
giao diện như yêu cầu. Hiệu chỉnh file res\layout\activity_main.xml như
sau
<RelativeLayoutxmlns:android=http://schemas.android.com/apk/re
s/android
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="22dp"
android:layout_marginTop="16dp"
android:text="@string/txt_tv1"/>

<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="54dp"
android:ems="10"
android:inputType="text"
android:text="@string/enter_text1">

<requestFocus/>

</EditText>

154
<EditText
android:id="@+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText1"
android:layout_below="@+id/editText1"
android:layout_marginTop="16dp"
android:ems="10"
android:inputType="text"
android:text="@string/enter_text2">
</EditText>

<EditText
android:id="@+id/editText3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText2"
android:layout_centerVertical="true"
android:ems="10"
android:inputType="text"
android:text="@string/enter_text3"/>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/editText3"
android:layout_centerHorizontal="true"
android:layout_marginTop="31dp"
android:text="@string/click_button"/>

</RelativeLayout>

Bước 2: Định nghĩa các hằng số trong file res\values\strings.xml


<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<string name="app_name">GUIButton</string>
<string name="action_settings">Settings</string>
<string name="txt_tv1">Example about Button</string>
<string name="enter_text1"/>
155
<string name="enter_text2"/>
<string name="enter_text3"/>
<string name="click_button">Calculate product of 3
numbers</string>
</resources>

Bước 3: Viết code cho chương trình ở file


scr\com.example.guibutton\MainActivity.java
package com.example.guibutton;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {


//khai báo các EditText và Button
private EditText edText1,edText2,edText3;
private Button btnProduct;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//khai báo hàm xử lý sự kiện của nút nhấn, ta có thể thay đổi tên
của nó tùy ý
addListenerOnButton();
}
Private void addListenerOnButton() {
//Tìm các EditText và Button
edText1 = (EditText)findViewById(R.id.editText1);
edText2 = (EditText)findViewById(R.id.editText2);
edText3 = (EditText)findViewById(R.id.editText3);

156
btnProduct = (Button)findViewById(R.id.button1);
//Đăng ký sự kiện Click trên nút nhấn
btnProduct.setOnClickListener(new OnClickListener(){

@Override
publicvoid onClick(View view){

//Khai báo ba biến String để chứa nội dung 3 EditText


String t1=edText1.getText().toString();
String t2=edText2.getText().toString();
String t3=edText3.getText().toString();
//Chuyển nội dung thành số nguyên
int i1= Integer.parseInt(t1);
int i2= Integer.parseInt(t2);
int i3= Integer.parseInt(t3);
// Tính phép nhân 3 số
int product=i1*i2*i3;
// Hiển thị kết quả dùng Toast
Toast.makeText(getApplicationContext(),

String.valueOf(product),Toast.LENGTH_LONG).show();
}
});
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
/* Inflate the menu; this adds items to the action bar if it is present.
*/
getMenuInflater().inflate(R.menu.main, menu);
return true; }
}
Chú ý: Phương thức String.valueOf (int value) lấy giá trị của
một biến value đang có kiểu dữ liệu là int chuyển thành kiểu dữ liệu
chuỗi String.
Bước 4: Chạy chương trình mô phỏng với máy ảo và nhập ba số
vào ba EditText, sau đó, nhấn nút nhấn “Calculate product of 3
numbers”. Lưu ý là ta phải nhập số và không được bỏ trống bất kỳ một
EditText nào. Ta được kế quả như sau:

157
Hình 6.13. Giao diện khi chạy chương trình nhân ba số
 ImageButton
ImageButton là một AbsoluteLayout. AbsoluteLayout cho phép
bạn xác định chính xác vị trí của các đối tượng con của nó. ImageButton
giống như một Button nhưng một hình ảnh được thay thế cho một text.
Người dùng có thể nhấn hoặc click lên nó.
Các thuộc tính của ImageButton
- Các thuộc tính kế thừa từ lớp android.widget.ImageView

Thuộc tính Mô tả
android:adjustViewBounds Nếu được thiết lập là true thì ImageView
điều chỉnh các biên của nó để giữ tỷ lệ
các cạnh với vùng có thể vẽ (chứa hình
ảnh) của nó
android:baseline Đây là đọ lệch các đường cơ bản trong
view
android:baselineAlignBottom Nếu là true, image view sẽ là đường cơ
bản được canh theo các cạnh dưới của nó
158
android:cropToPadding Nếu là true, image view sẽ được crop
(cúp cắt) để vừa với vùng đệm của nó
android:src Thiết lập hình ảnh của ButtonImage như
là nội dung của ImageView

- Các thuộc tính kế thừa từ lớp android.view.View

android:background Đây là vùng có thể vẽ (drawable) được sử


dụng như background
Android:contentDescription Text mô tả chính nội dung của view
Android:id Tên nhận dạng của View
Android:onClick Tên của phương thức trong trường hợp
View được gọi ra khi nó được click
Android:visibility Thiết lập trạng thái được nhìn thấy của view
Ví dụ: Tạo một ứng dụng Android có giao diện như sau:

Hình 6.14. Giao diện thiết kế chương trình về ImageButton


159
Trong Activity này ta có hai TextView và một ImageButton. Yêu
cầu khi ta nhấn vào ImageButton thì nó hiện ra một câu thông báo cho
biết bạn vừa nhấn vào ImageButton.
Trình tự thực hiện yêu cầu trên như sau:
Bước 1: Tạo một ứng dụng Android tên là GUIImageButton. Kéo
hai TextView và một ImageButton vào vùng thiết kế.
Hiệu chỉnh file activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tv1_txt"/>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"

160
android:layout_marginTop="55dp"
android:text="@string/tv2_txt"
android:textSize="18sp"
android:textColor="#FF3F48"/>

<ImageButton
android:id="@+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/textView1"
android:layout_below="@+id/textView2"
android:layout_marginTop="43dp"
android:adjustViewBounds="true"
android:baselineAlignBottom="false"
android:contentDescription="@string/imgbt_text"
android:cropToPadding="false"
android:maxHeight="100dp"
android:maxWidth="200dp"
android:scaleType="fitCenter"
android:src="@drawable/fun1"/>

</RelativeLayout>
Giải thích: Ở đây, ta chú ý đến hình ảnh của ImageButton. Mặc
định ta có hình chú robot. Để thay đổi hình ảnh này ta có thể tạo một thư
mục “drawable” trong thư mục chứa ứng dụng GUIImage:
GUIImage\res\drawable. Tại thư mục này ta chép vào hình ảnh ta cần
thêm vào cho ImageButton. Các hình này nên có đuôi .png, .jpeg. Ví dụ
ở đây ta có hình: fun1.png. Và trong file xml ta khai báo như sau:
android:src="@drawable/fun1"

161
Bước 2: Định nghĩa các hằng số trong file strings.xml như sau:
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUIImageButton</string>
<string name="action_settings">Settings</string>
<string name="tv1_txt">Example about ImageButton</string>
<string name="tv2_txt">Let you click on the ImageButton</string>
<string name="imgbt_text">This is a imagebutton</string>
</resources>
Bước 3: Viết code chương trình trong file MainActivity.java
như sau:
package com.example.guiimagebutton;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.Toast;

public class MainActivity extends Activity {


//khai báo ImageButton tên là imgbt
ImageButton imgbt;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

162
//Tạo một chương trình con xử lý sự kiện cho ImageButton
addLisenerOnButton();
}

Private void addLisenerOnButton() {


//Tìm ImageButton, buột nó vào imageButton1
imgbt=(ImageButton) findViewById(R.id.imageButton1);

//Đăng ký sự kiện Click trên ImageButton imgbt


imgbt.setOnClickListener(new OnClickListener(){
@Override
//Hiển thị thông báo ra màn hình khi ImageButton được nhấn

Publicvoid onClick(View v) {
Toast.makeText(MainActivity.this,
"The ImageButton Clicked. Thank you",
Toast.LENGTH_LONG).show();
}
});
}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is //present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Bước 4: Chạy mô phỏng chương trình với máy ảo và nhấn vào
ImageButton ta có được kết quả như sau:

163
Hình 6.14. Giao diện khi chạy chương trình về ImageButton

 CheckBox
CheckBox là một công tắc lựa chọn, được on/off bởi người dùng.
Nó có các thuộc tính như sau:
Các thuộc tính của nó được kế thừa từ lớp android.widget.TextView

Thuộc tính Mô tả
android:autoText Nếu được thiết lập, TextView này sẽ có
phương pháp nhập văn bản và có thể tự động
sửa các lỗi thông thường
android:drawableBottom Vùng có thể vẽ được vẽ bên dưới văn bản
android:drawableRight Vùng có thể vẽ được vẽ bên phải văn bản
android:editable Nếu được thiết lập TextView có một phương
pháp nhập văn bản
android:text Đây là văn bản (text) hiển thị trên button

164
Các thuộc tính kế thừa từ lớp android.view.View

Thuộc tính Mô tả
android:background Vùng có thể vẽ được sử dụng như một
background
android:id Cung cấp một tên nhận dạng cho View
android:contentDescription Text mô tả chính nội dung của View
android:onClick Đây là tên của phương thức khi View được
click
android:visibility Điều khiển khả năng có thể nhìn thấy ban
đầu của view
Ví dụ: Ta xây dựng một ứng dụng Android có tên là
GUICheckBox với giao diện như sau:

Hình 6.15. Giao diện chương trình ví dụ về CheckBox


Như vậy, ta có ba CheckBox với ba sự lựa chọn. Yêu cầu: khi
người dùng chọn hoặc bỏ chọn bất kỳ một CheckBox nào thì đều hiện ra
thông báo cho biết trạng thái của ba CheckBox. Trạng thái chọn là: true;
trạng thái bỏ chọn là: false. Ta chú ý đến phương thức
CheckBox.isChecked() sẽ trả về giá trị true/false tương ứng CheckBox
được chọn/bỏ chọn.
165
Bước 1: Tạo ứng dụng Android với tên là GUICheckBox với
giao diện như yêu cầu. Ta hiệu chỉnh file res/layout/activity_main.xml
như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<CheckBox
android:id="@+id/checkBox3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/checkBox2"
android:layout_below="@+id/checkBox2"
android:text="@string/chb2_txt"/>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/textView1"
android:layout_marginTop="32dp"
android:text="@string/tv2_txt"
android:textColor="#FF0000"
166
android:textSize="15sp"/>

<CheckBox
android:id="@+id/checkBox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView2"
android:layout_marginLeft="57dp"
android:layout_marginTop="23dp"
android:text="@string/chb1_txt"/>

<CheckBox
android:id="@+id/checkBox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/checkBox1"
android:layout_below="@+id/checkBox1"
android:text="@string/chb3_txt"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_alignParentTop="true"
android:layout_marginTop="14dp"
android:text="@string/tv1_txt"/>
</RelativeLayout>
Bước 2: Định nghĩa các hằng số trong file res/values/string.xml
như sau:
167
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUICheckbox</string>
<string name="action_settings">Settings</string>
<string name="tv1_txt">Ví dụ về Checkbox</string>
<string name="tv2_txt">Bạn đã sử dụng ngôn ngữ lập trình
nào?</string>
<string name="chb1_txt">JAVA</string>
<string name="chb2_txt">VISUAL C++</string>
<string name="chb3_txt">ASSEMBLY</string>

</resources>

Bước 3: Viết chương trình chính trong file MainActivity.java


như sau:
package com.example.guicheckbox;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.Toast;

public class MainActivity extends Activity {


//khai báo ba Checkbox
public CheckBox chk1,chk2,chk3;

@Override

168
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//tạo ba chương trình con xử lý sự kiện cho ba Checkbox


addListenerOnCheck1();
addListenerOnCheck2();
addListenerOnCheck3();

//phuongthuccho Checkbox1-Java
Private void addListenerOnCheck1() {
// Tìm và buột ba Checkbox
chk1=(CheckBox) findViewById(R.id.checkBox1);
chk2=(CheckBox) findViewById(R.id.checkBox2);
chk3=(CheckBox) findViewById(R.id.checkBox3);
//Đăng ký sự kiện Click cho chk1
chk1.setOnClickListener(new OnClickListener(){

@Override
Publicvoid onClick (View v){
//khai báo một bộ đệm chuỗi result
StringBuffer result=new StringBuffer();

result.append("Java Selection: ").append(chk1.isChecked());


result.append("\nVisual C++ Selection:
").append(chk2.isChecked());
result.append("\nASSEMLY Selection:
").append(chk3.isChecked());

169
Toast.makeText(MainActivity.this,result.toString(),
Toast.LENGTH_LONG).show();
}
});
}
//phuongthuccho Checkbox2-Visual C++
Private void addListenerOnCheck2() {
// TODO Auto-generated method stub
chk1=(CheckBox) findViewById(R.id.checkBox1);
chk2=(CheckBox) findViewById(R.id.checkBox2);
chk3=(CheckBox) findViewById(R.id.checkBox3);

chk2.setOnClickListener(new OnClickListener(){
@Override
publicvoid onClick(View v) {

StringBuffer result=new StringBuffer();


result.append("Java Selection: ").append(chk1.isChecked());
result.append("\nVisual C++ Selection:
").append(chk2.isChecked());
result.append("\nASSEMBLY Selection:
").append(chk3.isChecked());
Toast.makeText(MainActivity.this,result.toString(),
Toast.LENGTH_LONG).show();

}
});
}
//phuongthuccho Checkbox3-ASSEMBLY
Private void addListenerOnCheck3() {
// TODO Auto-generated method stub
chk1=(CheckBox) findViewById(R.id.checkBox1);

170
chk2=(CheckBox) findViewById(R.id.checkBox2);
chk3=(CheckBox) findViewById(R.id.checkBox3);

chk3.setOnClickListener(new OnClickListener(){

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
StringBuffer result=new StringBuffer();
result.append("JAVA
Selection:").append(chk1.isChecked());
result.append("\nVisual C++:
").append(chk2.isChecked());
result.append("\nASSEMBLY Selection:
").append(chk3.isChecked());

Toast.makeText(MainActivity.this,result.toString(),
Toast.LENGTH_LONG).show();
}
});
}

@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

}
Giải thích:
Ở đây ta khai báo biến result là một StringBuffer. Đối tượng này có
phương thức .append() có nghĩa là buột nội dung vào bộ đệm chuỗi này.
Ta có thể buột nhiều chuỗi vào StringBuffer thông qua phương thức
.append(String)
Dòng lệnh:

171
result.append("Java Selection:
").append(chk1.isChecked());
buộc chuỗi “Java Selection:” vào bộ đệm result. Sau đó buộc tiếp
trạng thái của Checkbox chk1 vào. Do đó, nếu chk1 đang được chọn
(checked) thì giá trị của result sau dòng lệnh đó là: Java Selection: True
Tương tự cho dòng lệnh bên dưới để buột tiếp các chuỗi vào result,
ta lưu ý \n là dùng để tạo xuống dòng ở thông báo, tương tự enter trong
soạn thảo văn bản.
result.append("\nVisual C++ Selection:
").append(chk2.isChecked());
Bước 4: Chạy chương trình và chọn nhấn vào các CheckBox ta
được kết quả như sau:

Hình 6.16. Giao diện chạy chương trình ví dụ về CheckBox


 ToogleButton
Một ToggleButton hiển thị trạng thái check/uncheck (đánh dấu/ko
đánh dấu) như một button. Nó cơ bản là một button on/off với đèn báo.

172
Các thuộc tính

Thuộc tính Mô tả
android:textOff Text trên nút nhấn khi nó không được check
(đánh dấu)
android:textOn Text trên nút nhấn khi nó được check
android:checked Trả về giá trị true/false tương ứng trạng thái
checked/ unchecked

Các thuộc tính của nó được kế thừa từ lớpandroid.widget.TextView

Thuộc tính Mô tả
android:autoText Nếu được thiết lập, TextView này sẽ có
phương pháp nhập văn bản và có thể tự
động sửa các lỗi thông thường
android:drawableBottom Vùng có thể vẽ được vẽ bên dưới văn bản
android:drawableRight Vùng có thể vẽ được vẽ bên phải văn bản
android:editable Nếu được thiết lập TextView có một
phương pháp nhập văn bản
android:text Đây là văn bản (text) hiển thị trên button

Các thuộc tính kế thừa từ lớp android.view.View

Thuộc tính Mô tả
android:background Vùng có thể vẽ được sử dụng như một
background
android:id Cung cấp một tên nhận dạng cho View
android:contentDescription Text mô tả chính nội dung của View
android:onClick Đây là tên của phương thức khi View được
click
android:visibility Điều khiển khả năng có thể nhìn thấy ban
đầu của view
Ví dụ: Viết ứng dụng có giao diện như sau:

173
Hình 6.17. Giao diện ví dụ về ToggleButton
Ta thấy trên layout chính có ba TextView, hai ToggleButton, và
một Button. ToggleButton1 hiển thị trạng thái của đèn, nó mang hai giá
trị ON và OFF. ToggleButton2 hiển thị trạng thái của động cơ, nó mang
hai giá trị STOP và RUN. Yêu cầu:
- Khi nhấn vào ToggleButton1 thay đổi trạng thái của đèn thì
chương trình hiện ra thông báo trạng thái đèn đã được thay đổi.
- Khi nhấn vào ToggleButton2 thay đổi trạng thái motor thì chương
trình hiện ra thông báo trạng thái motor đã thay đổi.
- Khi nhấn vào Button “Click on me” thì chương trình hiện ra
thông báo trạng thái hiện tại của đèn và động cơ.
Ta thực hiện các bước như sau:
Bước 1: Tạo ứng dụng Android có tên là GUIToggleButton với giao
diện như yêu cầu, hiệu chỉnh file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools=http://schemas.android.com/tools
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"

174
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_marginTop="148dp"
android:layout_toLeftOf="@+id/toggleButton2"
android:gravity="left|right|center_vertical|center_horizontal"
android:text="@string/bt1_txt"/>

<ToggleButton
android:id="@+id/toggleButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView3"
android:checked="true"
android:gravity="left|right|center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textOff="@string/tgbt1_off"
android:textOn="@string/tgbt1_on"/>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/textView3"
android:layout_alignBottom="@+id/textView3"

175
android:layout_alignLeft="@+id/toggleButton1"
android:layout_marginLeft="15dp"
android:gravity="center_vertical|center_horizontal|left"
android:text="@string/tv2_txt"/>

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/toggleButton2"
android:layout_alignParentRight="true"
android:layout_marginBottom="15dp"
android:layout_marginRight="48dp"
android:text="@string/tv3_txt"/>

<ToggleButton
android:id="@+id/toggleButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/button1"
android:layout_alignLeft="@+id/textView3"
android:layout_marginBottom="72dp"
android:checked="false"
android:gravity="center_vertical|center_horizontal|left"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textOff="@string/tgbt2_off"
android:textOn="@string/tgbt2_on"/>

<TextView
android:id="@+id/textView1"

176
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="36dp"
android:text="@string/tv1_txt"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</RelativeLayout>

Bước 2: Định nghĩa cách hằng số trong file res\values\strings.xml


<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUIToggleButton</string>
<string name="action_settings">Settings</string>
<string name="tv1_txt">Ví dụ về ToogleButton</string>
<string name="tv2_txt">Light</string>
<string name="tv3_txt">Motor</string>
<string name="bt1_txt">Click on me</string>
<string name="tgbt1_on">ON</string>
<string name="tgbt1_off">OFF</string>
<string name="tgbt2_on">RUN</string>
<string name="tgbt2_off">STOP</string>

</resources>

Bước 3: Viết chương trình tạo các phương thức xử lý trong file
MainActivity.java. Chú ý cùng một kết quả nhưng có nhiều cách viết
khác nhau để cho các bạn có thể rút kết được nhiều bài học.

177
package com.example.guitogglebutton;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ToggleButton;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {


private ToggleButton tgbt1,tgbt2;
private Button bt1;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstance-State);
setContentView(R.layout.activity_main);
//Tạo hai chương trình con xử lý cho Button và cho
ToggleButton_Light
addListennerOnButton();
addListennerOnToggleButton_Light();
//Khai báo và tìm ToggleButton motor
//ban co the su dung tgbt2 da khai bao o day thay cho tgbtmotor
ToggleButton tgbtmotor=(ToggleButton)
findViewById(R.id.toggleButton2);

//Đăng ký xử lý sự kiện Click cho tgbtmotor


tgbtmotor.setOnClickListener(new OnClickListener(){

178
@Override
publicvoid onClick(View v) {
//Hiển thị thông báo mỗi lần ToggleButton motor được nhấn
Toast.makeText(getApplicationContext(), "You have changed the
motor's status",Toast.LENGTH_SHORT).show();
}
});
}
// Chương trình xử lý nút ToggleButton Light
Private void addListennerOnToggleButton_Light() {
//Tìm hai ToggleButton
tgbt1=(ToggleButton) findViewById(R.id.toggleButton1);
tgbt1.setOnClickListener(new OnClickListener(){

@Override
Publicvoid onClick(View v) {
//Hiển thị thông báo khi ToggleButton Light được click
Toast.makeText(getBaseContext(),"You have just changed the
light's status",Toast.LENGTH_SHORT).show();
}
});
}

//Hàm xử lý nút nhấn


Private void addListennerOnButton() {

//Tìm hai ToggleButton và một nút Button


tgbt1=(ToggleButton) findViewById(R.id.toggleButton1);
tgbt2=(ToggleButton) findViewById(R.id.toggleButton2);
179
bt1=(Button) findViewById(R.id.button1);
//Đăng ký xử lý sự kiện Click cho bt1
bt1.setOnClickListener(new OnClickListener(){

@Override
Publicvoid onClick(View v) {
StringBuffer message=new StringBuffer();
message.append("Light Status: ").append(tgbt1.getText());
message.append("\nMotor Status: ").append(tgbt2.getText());

Toast.makeText(getApplicationContext(),message.toString(),
Toast.LENGTH_LONG).show();

}
});
}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Bước 4: Chạy chương trình demo với máy ảo.
- Khi ta nhấn ToggleButton1 thay đổi trạng thái đèn ta có kết quả
như sau:

180
Hình 6.18. Giao diện chương trình ví dụ ToggleButton khi chạy trên máy
ảo
- Khi ta nhấn ToogleButton2 thay đổi trạng thái motor ta có kết quả
như sau:

Hình 6.19. Giao diện chương trình ví dụ ToggleButton khi thao tác trên
máy ảo
181
- Khi nhấn Button “Click on me” ta có kết quả như sau:

Hình 6.20. Giao diện chương trình ví dụ ToggleButton khi thao tác trên
máy ảo
 RadioGroup
RadioGroup được dùng để nhóm nhiều RadioButton lại với nhau.
Khi đó, nếu ta chọn (checked) một radiobutton nào đó thì tất cả các
radiobutton khác thuộc RadioGroup này sẽ bị bỏ chọn (unchecked).
Các thuộc tính cơ bản như sau:

Thuộc tính Mô tả
android:checkedButton Id của radiobutton con của radiogroup này
được mặc định đánh dấu chọn (checked)

Các thuộc tính kế thừa từ lớp android.view.View

Thuộc tính Mô tả
android:background Vùng có thể vẽ được sử dụng như một
background
android:id Cung cấp một tên nhận dạng cho View
android:contentDescription Text mô tả chính nội dung của View
182
android:onClick Đây là tên của phương thức khi View được
click
android:visibility Điều khiển khả năng có thể nhìn thấy ban
đầu của view
Ví dụ:
Viết chương trình với layout như yêu cầu gồm: hai TextView, một
RadioGroup gồm ba Radio Button con, một Button.
Người dùng sẽ chọn lựa trang web mà mình hay đọc nhất, chỉ chọn
một trong ba. Sau đó nhấn nút Button “Biểu quyết” nó sẽ hiện ra tin nhắn
thông báo phương án người dùng đã chọn.

Hình 6.21. Giao diện chương trình ví dụ về RadioGroup


Ta thực hiện các bước sau:
Bước 1: Tạo một ứng dụng android có tên là GUIRadioGroup, với
layout theo yêu cầu. Hiệu chỉnh file res\layout\activity_main.xml như sau:
RelativeLayoutxmlns:android="http://schemas.android.com/apk/re
s/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
183
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="35dp"
android:layout_marginTop="18dp"
android:text="@string/tv1_txt"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="#F00000"/>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/radioGroup1"
android:layout_marginTop="22dp"
android:layout_toRightOf="@+id/textView2"
android:background="#00EBEE"
android:text="@string/bt1_txt"
android:textSize="12sp"/>

<RadioGroup
android:id="@+id/radioGroup1"

184
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="36dp">

<RadioButton
android:id="@+id/radio0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:checked="true"
android:text="@string/rdbt1_txt"
android:textSize="12sp"/>

<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rdbt3_txt"
android:textSize="12sp"/>

<RadioButton
android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rdbt2_txt"
android:textSize="12sp"/>
</RadioGroup>

185
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/radioGroup1"
android:layout_below="@+id/textView1"
android:layout_marginTop="20dp"
android:text="@string/tv2_txt"/>

</RelativeLayout>

Bước 2: Định nghĩa các hằng số tương ứng trong file


res\values\string.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<string name="app_name">GUIRadioGroup</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string name="tv1_txt">Ví dụ về RadioGroup</string>
<string name="tv2_txt">Bạn hay đọc trang báo nào nhất?</string>
<string name="rdbt1_txt">http://dantri.com.vn</string>
<string name="rdbt2_txt">http://vietnamnet.vn</string>
<string name="rdbt3_txt">http://tuoitre.vn</string>
<string name="bt1_txt">Biểu quyết</string>
</resources>

Bước 3: Viết chương trình cho các sự kiện và phương thức xử lý


tại file MainActivity.java như sau:

186
package com.example.guiradiogroup;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

RadioGroup rdg;
RadioButton rdbtchon;
Button btchon;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addListennerOnBieuquyet();

Private void addListennerOnBieuquyet() {


// TODO Auto-generated method stub
rdg=(RadioGroup)findViewById(R.id.radioGroup1);

187
btchon=(Button)findViewById(R.id.button1);
btchon.setOnClickListener(new OnClickListener(){

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
//layid cua RadioButton duocchontrong RadioGroup
int selected=rdg.getCheckedRadioButtonId();
//gan RadioButton duocchonchordbtchon
rdbtchon=(RadioButton)findViewById(selected);

Toast.makeText(MainActivity.this,rdbtchon.getText().toString(),
Toast.LENGTH_SHORT).show();

});
}

@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

Bước 4: Chạy mô phỏng chương trình với máy ảo.


188
- Chọn http://vietnamnet.vn và nhấn “Biểu quyết” ta được kết quả
như sau

Hình 6.22. Giao diện chương trình về RadioGroup khi tác trên máy ảo
- Chọn http://dantri.com.vn và nhấn “Biểu quyết” ta được kết quả
như sau:

Hình 6.23. Giao diện chương trình về RadioGroup khi tác trên máy ảo

189
190
Chương 7
LISTVIEW

Thực chất ListView cũng là một View, một UI Control, tuy nhiên
ta tách ra thành một chương riêng bởi vì nó có khá nhiều vấn đề để trình
bày và được sử dụng nhiều. Chúng ta đã được tìm hiểu TextView dùng
thể hiển thị các thông tin, tuy nhiên khi muốn hiển thị thông tin dưới
dạng danh sách thì sử dụng TextView lại kém hiệu quả, rất khó quản lý
và xử lý dữ liệu. Do đó, Android cung cấp cho chúng ta một số đối tượng
cho phép hiển thị danh sách tốt hơn, quản lý dễ dàng hơn, một trong số
đó là ListView. ListView là đối tượng nâng cao được sử dụng khá nhiều
trong lập trình ứng dụng, hình dưới hình ảnh minh họa cho việc sử dụng
ListView trong các ứng dụng.

Hình 7.1. Ví dụ về ứng dụng ListView


Chúng ta có thể sử dụng trực tiếp ListView có sẵn của Android để
hiển thị thông tin, nhưng nội dung hiển thị trong ListView chỉ là ký tự.
Nếu muốn các item của ListView hiển thị nhiều thông tin hơn như hình
trên chúng ta cần thiết kế lại layout cho mỗi item của ListView. Trước
191
tiên, chúng ta sẽ tìm hiểu về cách sử dụng ListView có sẵn của Android
cho các ứng dụng cơ bản, khi đã nắm rõ về cách sử dụng chúng ta sẽ thiết
kế lại layout cho ListView để phục vụ cho các ứng dụng phức tạp hơn.
Để dễ dàng nắm bắt được cách sử dụng ListView chúng ta hãy cùng
tham khảo qua các ví dụ dưới đây.
Ví dụ 1: Thiết kế ứng dụng hiển thị danh sách sinh viên trong lớp
và xử lý sự kiện nhấn chọn vào mỗi sinh viên trong danh sách.
Ví dụ này nhằm làm quen với việc sử dụng ListView do đó, chúng ta
sẽ sử dụng một danh sách sinh viên cố định được lưu trong file xml.
Tạo một Project mới có tên ListView_ex1.
Chúng ta sẽ tạo giao diện đơn giản gồm:
- TextBox: Hiển thị tên danh sách.
- ListBox: Hiển thị danh sách sinh viên.
Ở ví dụ này, tôi dùng LinearLayout để thiết kế:

Hình 7.2. Thiết kế giao diện dùng ListView


192
Code file activity_main.xml
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:gravity="center"
android:text="Danh sách sinh viên trong lớp"/>

<ListView
android:id="@+id/lstName"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>

</LinearLayout>

193
Ở đoạn code trên các bạn lưu ý một số điểm sau:
- android:id=“@+id/lstname” đâu là id của ListView dùng để tuy
xuất khi lập trình code
- ListView cũng hỗ trợ một số thuộc tính khác như: margin,
padding, gravity,… nhưng tôi không để cập ở đây vì mục đích của ví dụ
này chỉ giúp các bạn biết cách hiển thị nội dung của một danh sách lên
ListView, các bạn có thể thêm các thuộc tính ấy vào khi thiết kế giao
diện trong file xml và xem sự thay đổi.
Tiếp theo, chúng ta sẽ tạo một file xml chứa danh sách các sinh
viên. Để tạo file này, tại cửa sổ Project Explorer bạn vào thư mục res/
value tạo một file Android XML.
Tiếp theo, chúng ta sẽ thêm vào một mảng để chứa danh sách sinh
viên. Ở cửa sổ đang mở file DanhSachSinhVien.xml các bạn nhấn vào
nút Add… để thêm vào một mảng.
Ở hộp thoại hiện ra, bạn chọn mục String Array và chọn OK để
hoàn tất.

Hình 7.3. Thêm mảng chứa danh sách sinh viên

194
Hình 7.4. Thêm mảng chứa danh sách sinh viên
Tiếp theo, chúng ta sẽ thêm các item (phần tử) vào trong mảng
như sau:
Tại cửa sổ hiển thị mảng vừa tạo, bạn nhấn vào nút Add… để thêm
phần tử

Hình 7.5. Các thành phần tài nguyên của mảng


Ở hộp thoại thêm phần tử, chọn mục item và nhấn OK để hoàn tất.

195
Sau khi đã tạo xong danh sách sinh viên, tiếp theo chúng ta sẽ viết
code để hiển thị danh sách này lên ListView khi chạy ứng dụng. Bạn mở
file MainActivity.java và nhập vào code sau:

Hình 7.6. Tạo các Item cho mảng


Cuối cùng là nhập giá trị cho item (ở đây item có kiểu String để
chứa tên sinh viên). Chọn Item ở của sổ bên trái và nhấp giá trị ở mục
Value*. Làm tương tự cho đủ số sinh viên.

Hình 7.7. Nhập giá trị cho các Item của mảng

196
Dưới đây là nội dung file DanhSachSinhVien.xml sau khi đã thêm
các sinh viên:
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string-arrayname="DanhSachSinhVien">
<item>Đặng Thị Bích Ly</item>
<item>Nguyễn Ngọc Bích</item>
<item>Hoàng Ngọc Vân</item>
<item>Nguyễn Khải Minh</item>
<item>Nguyễn Ngọc Toàn</item>
<item>Lê Tuấn Dũng</item>
</string-array>

</resources>
Ngoài cách thêm mảng và các item trong mảng như đã hướng dẫn ở
trên, bạn cũng có thể mở trực tiếp file DanhSachSinhVien.xml sau đó
nhập vào code như trên và lưu lại. Khi đã thành thạo bạn nên sử dụng
cách viết code để thực hiện thao tác nhanh và đơn giản hơn.
Lập trình để hiển thị danh sách lên ListView và sử lý một số sự
kiện đơn giản.
package com.example.listview_ex1;

import java.util.ArrayList;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;

197
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

// Khai báo các đối tượng sử dụng


private ListView lstName;
private String[] arrDanhSach;
private ArrayAdapter<String>adapterlist;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Tạo mảng chứa danh sách sinh viên và lấy dữ liệu từ file
// DanhSachSinhVien.xml (DataSource)
arrDanhSach =
getResources().getStringArray(R.array.DanhSachSinhVien);
// Lấy đối tượng ListView dựa vào id
lstName = (ListView) findViewById(R.id.lstName);
// Gán DataSource và ArrayAdapter
adapterlist = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,
arrDanhSach);
// Đưa DataSource và ListView
lstName.setAdapter(adapterlist);
// Sử lý sự kiện Nhấn vào item trên ListView
lstName.setOnItemClickListener(new
AdapterView.OnItemClickListener() {

198
@Override
Publicvoid onItemClick(AdapterView<?> arg0,
View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
String msg;
msg = "Sự kiện 'OnItemClickListener'";
msg = msg + "\nBạn vừa chọn sinh viên ở vị trí: ";
msg = msg + String.valueOf(arg2);
msg = msg + "\n" + "Tên: " +
arrDanhSach[arg2].toString();
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT).show();
}
});
// Sử lý sự kiện nhấn và giữ vào item trên ListView
lstName.setOnItemLongClickListener(new
AdapterView.OnItemLongClickListener() {
@Override
Publicboolean
onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long
arg3) {
// TODO Auto-generated method stub
String msg;
msg = "Sự kiện 'OnItemLongClickListener'";
msg = msg + "\nBạn vừa chọn sinh viên ở vị trí: ";
msg = msg + String.valueOf(arg2);
msg = msg + "\n" + "Tên: " +
arrDanhSach[arg2].toString();
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT) .show();
Returnfalse;

199
}
});
}

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Code trên được chú thích khá chi tiết, tôi chỉ lưu ý các bạn một số
điểm sau:
- Dòng code: “adapterlist = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, arrDanhSach);” sẽ gán DataSource
vào ArrayAdapter để hiển thị lên ListView. Bạn để ý các đối số:
+ this: chính là context của Activity hiện tại, bạn có thể
viết MainActivity.this, tuy nhiên khi viết là this thì bạn có thể copy
dòng code này đi bất kỳ Activity nào thì nó cũng hiểu.
+ android.R.layout.simple_list_item_1: đây chính là layout cho các
item mà Android đã xây dựng sẵn, để xem nội dung của layout này, bạn
nhấn Ctrl + nhấn chuột vào dòng lệnh này, cửa sổ
simple_list_item_1.xml sẽ được mở ra.
+ arrDanhSach: chính là dữ liệu truyền vào để hiển thị lên
ListView
- Phần xử lý sự kiện sẽ được thảo luận rõ hơn ở chương khác, ở
đây tôi chỉ muốn nêu ra hai sự kiện quan trọng được dùng nhiều nhất của
ListView đó setOnItemClickListener và
setOnItemLongClickListener
+ setOnItemClickListener: xảy ra khi nhấn vào một item trên
ListView, khi đó ListView sẽ trả về các đối số, trong đó đối số int arg2
cho biết vị trí của item được nhấn trên ListView. Vị trí đầu tiên trong

200
ListView là 0, dựa vào vị trí này ta có thể xác định được item được nhấn
và nội dung của item đó.
+ setOnItemLongClickListener: xảy ra khi nhấn và giữ vào item từ
2 đến 3 giây, cũng tương tự như setOnItemClickListener, sự kiện này
cũng trả về đối số cho biết vị trí item được nhấn.
- Ở phần này, tôi có sử dụng Toast dùng để hiển thị thông tin các
item được nhấn. Toast thường được sử dụng để hiển thị các thông báo và
tự mất sau khoảng thời gian nhất định.
Kết quả khi chạy trên máy ảo:

Hình 7.8. Kết quả chạy trên máy ảo của bài tập Danh sách sinh viên
Khi bạn nhấn vào tên sinh viên trong danh sách, tùy vào thời gian
nhấn mà ListView sẽ xử lý các sự kiện tương ứng.
Ví dụ 2: Thiết kế ứng dụng nhập vào danh sách sinh viên và hiển thị
trên ListView. Muốn xóa sinh viên nào thì nhấn và giữ vào sinh viên đó.
Giao diện ứng dụng gồm:
- Hai EditText để nhập Tên và Mã số sinh viên.

201
- Một Button để thêm sinh viên vào danh sách.
- Một ListView để hiển thị danh sách sinh viên
Code file activity_main.xml
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<EditText
android:id="@+id/etTen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào Tên sinh viên">

<requestFocus/>
</EditText>

<EditText
android:id="@+id/etCode"

202
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào Mã số sinh viên"
android:numeric="decimal"/>

<Button
android:id="@+id/btnThem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Thêm sinh viên"/>

<ListView
android:id="@+id/lstSinhVien"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>

</LinearLayout>
Code trên khá quen thuộc khi bạn đã học qua Layout và các View
cơ bản của Android, ở đây tôi sử dụng một thuộc tính quen thuộc là
grayvity=center_horizontal cho LinearLayout do đó tất cả các đối
tượng trong LinearLayout sẽ được tự động canh giữa màn hình. Ngoài
ra, EditText có thêm thuộc tính android:numeric=“decimal” để quy
định chỉ cho phép nhập số vào EditText.
Code file MainActivity.java
package com.example.listview_ex2;

import java.util.ArrayList;
import android.os.Bundle;

203
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

// Khai báo đối tượng sử dụng


private EditText etTen, etMSSV;
private Button btnThem;
private ListView lstDanhSach;
private ArrayAdapter<String>adapterDanhSach;
private ArrayList<String>arrDanhSach;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Lấy đối tượng dựa vào id


etTen = (EditText) findViewById(R.id.etTen);
etMSSV = (EditText) findViewById(R.id.etMSSV);
btnThem = (Button) findViewById(R.id.btnThem);

204
lstDanhSach = (ListView)
findViewById(R.id.lstSinhVien);

// Khai báo ArrayList và gán vào Adapter


arrDanhSach = new ArrayList<String>();
adapterDanhSach = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, arrDanhSach);

// Gán ArrayAdapter cho ListView


lstDanhSach.setAdapter(adapterDanhSach);

// Xử lý sự kiện nhấn nút Thêm Sinh Viên


btnThem.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
// Kiểm tra tên và mã số sinh viên đã được
nhập chưa
String msg;
msg = "Tên: " + etTen.getText().toString();
msg = msg + "\nMSSV: " + etMSSV.getText().toString();
if (!etTen.getText().toString().equals("")
&&
!etMSSV.getText().toString().equals("")) {
// Thêm sinh viên vào danh sách
arrDanhSach.add(msg);
// Cập nhật danh sách
adapterDanhSach.notifyDataSetChanged();
etTen.setText("");

205
etTen.requestFocus();
etMSSV.setText("");
}
}
});

// Xử lý sự kiện nhấn vào sinh viên để xóa


lstDanhSach.setOnItemLongClickListener(new
AdapterView.OnItemLongClickListener() {
@Override
Publicboolean onItemLongClick(AdapterView<?>
arg0,View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
String msg;
msg = "Bạn vừa xóa sinh viên:\n";
msg = msg + arrDanhSach.get(arg2);
// Xóa sinh viên trong danh sách
arrDanhSach.remove(arg2);
adapterDanhSach.notifyDataSetChanged();

// Hiện thông báo xóa sinh viên


Toast.makeText(MainActivity.this, msg,

Toast.LENGTH_SHORT).show();
Returnfalse;
}
});
}

@Override
206
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

Code có nhiều chỗ giống với ví dụ 1, các sự kiện nhấn đã được tìm
hiểu ở phần trước do đó không được để cập chi tiết ở đây. Để nắm rõ
cách sử dụng ListView các bạn cần lưu ý một số điểm sau:
- Xử lý sự kiện thêm sinh viên:
+ !etTen.getText().toString().equals("")&&
!etMSSV.getText().toString().equals("")) là lệnh dùng để kiểm tra đã
nhập tên và mã số sinh viên đầy đủ chưa, trước khi thêm vào danh sách.
+ arrDanhSach.add(msg): lệnh này dùng để thêm một sinh viên
vào danh sách đã tạo.
+ adapterDanhSach.notifyDataSetChanged(): dùng để cập nhật
ListView khi danh sách sinh viên thay đổi
- Xử lý sự kiện xóa sinh viên:
+ arrDanhSach.remove(arg2): Lệnh này để xóa item thứ arg2 trong
danh sách. Trong đó, đối số arg2 là vị trí của sinh viên trong ListView
được trả về từ sự kiện OnItemLongClickListener.
+ Toast: dùng để hiện thông báo đã xóa sinh viên.
Kết quả mô phỏng trên máy ảo:

207
Hình 7.9. Giao diện chương trình khi xóa tên sinh viên ra khỏi danh sách
sinh viên
Khi nhập đầy đủ tên và mã số sinh viên thì mới có thể thêm sinh
viên được, bạn có thể thử để trống một trong hai hoặc cả hai ô tên và mã
số sinh viên, sau đó nhấn “Thêm Sinh Viên” để kiểm tra ứng dụng có
thêm sinh viên không.
Ở phần trên, chúng ta đã tìm hiểu về ListView, chức năng, hiển thị
danh sách lên ListView. Tuy nhiên, với cách làm trên chỉ ListView chỉ
có thể hiển thị các thông tin đơn giản dạng chuỗi, nhưng trong xây dựng
các ứng dụng thực tế, yêu cầu hiển thị rất đa dạng. Để ListView có thể
hiển thị được nhiều dạng thông tin hơn chúng ta cần thiết kế lại layout
cho các item của ListView và sửa lại Adapter, phần này chúng ta sẽ giải
quyết vấn đề này.
Để hiểu rõ cách thực hiện chúng ta sẽ làm một ví dụ đơn giản, ở
ví dụ này chúng ta sẽ thiết kế một ứng dụng nhỏ cho phép thêm các sinh
viên học tiếng anh vào một danh sách. Mỗi item trong danh sách sẽ
gồm: Tên sinh viên, ngành học, lớp đăng ký học và giới tính. Ở ví dụ
này chúng ta chỉ tập trung phân tích các phần liên quan đến ListView,
các code liên quan sẽ được đề cập đến ở phần khác. Hình dưới là giao
diện sử dụng:
208
Hình 7.10. Giao diện ứng dụng thêm tên sinh viên và ngành
Các đối tượng được sử dụng:
- Hai EditText: dùng để nhập tên sinh viên và ngành học
- Hai RadioButton: để chọn giới tính
- Hai RadioButton: dùng để chọn lớp học
- Một Button: thêm sinh viên vào danh sách
- ListView được sử dụng trong ứng dụng này là ListView đã được
thiết kế lại, mỗi item của ListView chứa:
+ Image: hiển thị giới tính sinh viên
+ TextView: hiển thị tên sinh viên
+ TextView: hiển thị ngành học sinh viên
+ TextView: hiển thị lớp học
Đầu tiên, chúng ta sẽ thiết kế giao diện cho ứng dụng:
Code file activity_main.xml
209
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<EditText
android:id="@+id/edTen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào tên sinh viên">

<requestFocus/>
</EditText>

<EditText
android:id="@+id/edNganh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào ngành học"/>

210
<RadioGroup
android:id="@+id/radioGroup2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">

<RadioButton
android:id="@+id/radGioiTinh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Nam"/>

<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nữ"/>
</RadioGroup>

<RadioGroup
android:id="@+id/radioGroup1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rad246"
android:layout_width="0dp"

211
android:layout_height="wrap_content"
android:layout_weight="3"
android:checked="true"
android:text="Lớp 3/5/7"/>

<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Lớp 2/4/6"/>
</RadioGroup>

<Button
android:id="@+id/btnThem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Thêm Sinh Viên"/>

<ListView
android:id="@+id/lstDanhSach"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>

Tiếp theo, chúng ta sẽ thiết kế lại layout cho mỗi item trong
ListView, bạn tạo một layout mới có tên listview_item.xml, file này
dùng để thiết kế layout cho mỗi item trong ListView, nội dung file
listview_item.xml như sau:
212
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical">

<ImageView
android:id="@+id/imgGioiTinh"
android:layout_width="30dp"
android:layout_height="30dp"
android:background="@drawable/boyicon"
android:paddingLeft="10dp"/>
</LinearLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="2"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"

213
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:id="@+id/tvTen"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:text="Dinh Quang Hiep"
android:textSize="20sp"/>

<TextView
android:id="@+id/tvLop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:text="lớp 2/4/6"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<TextView
android:id="@+id/tvNganh"
android:layout_width="0dp"
android:layout_height="wrap_content"

214
android:layout_weight="2"
android:text="Ngành: "
android:textSize="15sp"
android:textStyle="italic"/>
</LinearLayout>
</LinearLayout>

</LinearLayout>
Lưu ý: Để hiển thị hình ảnh về giới tính sinh viên (Nam/Nữ) bạn
cần copy hai hình ảnh vào thư mục res/ drawable-hdpi (hoặc thư mục
res/ drawable). Hình nam có tên “boyicon”, nữ có tên “girlicon”.
Để gán layout item ta vừa thiết kế vào ListView ta sẽ lập trình lại
Adapter cho ListView kế thừa từ lớp ArrayAdapter và override lại
phương thức getView. Ta cần tạo một class để lưu trữ thông tin mỗi sinh
viên và một class để lập trình lại Adapter, tại Package
com.example.listview_ex3 (Pakage chứa MainActivity.java)bạn tạo
thêm hai class MyAdapter.java và Student.java.
Code file Student.java như sau:
package com.example.listview_ex3;

public class Student {


private String Ten;
private String Nganh;
private Boolean GioiTinh;
private Boolean Lop;

public Student(String Ten, String Nganh, Boolean GioiTinh,


Boolean Lop) {
this.Ten = Ten;
this.Nganh = Nganh;
this.GioiTinh = GioiTinh;

215
this.Lop = Lop;
}

public String getTen() {


returnTen;
}

publicvoid setTen(String ten) {


Ten = ten;
}

public String getNganh() {


returnNganh;
}

publicvoid setNganh(String nganh) {


Nganh = nganh;
}

public Boolean getGioiTinh() {


returnGioiTinh;
}

Publicvoid setGioiTinh(Boolean gioiTinh) {


GioiTinh = gioiTinh;
}

public Boolean getLop() {


returnLop;

216
}

publicvoid setLop(Boolean lop) {


Lop = lop;
}

}
Class Student.java chứa thông tin của mỗi sinh viên gồm: Tên,
Ngành học, Giới tính và Lớp.
Class MyAdapter là phần quan trọng nhất trong ví dụ này, ở file
này ta sẽ viết lại Adapter cho listview được kế thừa từ lớp ArrayAdapter,
code file MyAdapter.java:
package com.example.listview_ex3;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class MyAdapter extends ArrayAdapter<Student> {

private Context context;


// layout của item được thiết kế lại
PrivateintmLayout;
// danh sach sinh viên với kiểu Student
217
private ArrayList<Student>arrlist;

public MyAdapter(Context context, int resource,


ArrayList<Student> list) {
super(context, resource, list);
// TODO Auto-generated constructor stub
this.context = context;
this.mLayout = resource;
this.arrlist = list;
}

@Override
public View getView(int position, View convertView, ViewGroup
parent) {
// TODO Auto-generated method stub
// Dùng khi muốn tạo một View từ filw xml
LayoutInflater inflater = (LayoutInflater) context

.getSystemService(context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(mLayout, null);
// Lấy các đối tượng trong layout item
TextView tvTen = (TextView)
convertView.findViewById(R.id.tvTen);
TextView tvNganh = (TextView)
convertView.findViewById(R.id.tvNganh);
TextView tvLop = (TextView)
convertView.findViewById(R.id.tvLop);
ImageView imgGioiTinh = (ImageView) convertView
.findViewById(R.id.imgGioiTinh);
// Gán các giá trị nhận được từ mỗi item trong danh sách
sinh viên vào

218
// các đối tượng trong layout item để hiển thị
tvTen.setText(arrlist.get(position).getTen());
tvNganh.setText(arrlist.get(position).getNganh());
// Kiểm tra lớp và giới tính để hiển thị đúng hình
if (arrlist.get(position).getLop()) {
tvLop.setText("Lớp 3/5/7");}
else {
tvLop.setText("Lớp 2/4/6");
}
if (arrlist.get(position).getGioiTinh()) {

imgGioiTinh.setBackgroundResource(R.drawable.boyicon);}
else {

imgGioiTinh.setBackgroundResource(R.drawable.girlicon);
}
return convertView;
}

}
Code trên ta đã override lại phương thức getView, phương thức
này sẽ trả về một View. Các dòng code đã được chú thích chi tiết. Các
đối số quan trọng cần lưu ý:
- position: là vị trí của item trong danh sách sinh viên
- convertView: dùng để xử lý item trong listview, thay đổi các
thành phần trong item này
Cuối cùng là code file MainActivity.java:
package com.example.listview_ex3;

import java.util.ArrayList;

219
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.RadioButton;

public class MainActivity extends Activity {

private ListView lstDanhSach;


private EditText edTen, edNganh;
private RadioButton radGioiTinh, radLop;
private Button btnThem;
private ArrayList<Student>arrStudent = new
ArrayList<Student>();
private Student mStudent;
private MyAdapter myAdapter;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// lấy đối tượng dựa vào id
lstDanhSach = (ListView)
findViewById(R.id.lstDanhSach);
220
edTen = (EditText) findViewById(R.id.edTen);
edNganh = (EditText) findViewById(R.id.edNganh);
radGioiTinh = (RadioButton)
findViewById(R.id.radGioiTinh);
radLop = (RadioButton) findViewById(R.id.rad246);
btnThem = (Button) findViewById(R.id.btnThem);
// Gán row Layout và data source cho adapter mới được tạo
myAdapter = new MyAdapter(this,
R.layout.listview_item, arrStudent);
lstDanhSach.setAdapter(myAdapter);

btnThem.setOnClickListener(new ThemSinhVien());
lstDanhSach.setOnItemLongClickListener(new
AdapterView.OnItemLongClickListener() {
@Override
Publicboolean onItemLongClick(AdapterView<?> arg0,
View arg1, int arg2, long arg3) {
// TODO Auto-generated method stub
// Xóa item và cập nhật danh sách
arrStudent.remove(arg2);
myAdapter.notifyDataSetChanged();
returnfalse;
}
});
}

Publicclass ThemSinhVien implements OnClickListener {


@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub

221
// lấy các thông số của sinh viên và lưu vào một
biến với kiểu
// Student đã được ta lập trình
mStudent = new
Student(edTen.getText().toString(), edNganh
.getText().toString(),
radGioiTinh.isChecked(),
radLop.isChecked());
// Thêm sinh viên vào mảng và cập nhật
arrStudent.add(mStudent);
myAdapter.notifyDataSetChanged();
edTen.setText("");
edNganh.setText("");
edTen.requestFocus();
}
}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Tới đây, ứng dụng đã hoàn thành, bạn có thể mô phỏng trên máy
ảo để kiểm tra kết quả. Custom lại ListView được sử dụng rất nhiều
trong thực tế, do đó bạn hãy tìm hiểu kỹ phần này và tự mình thiết kế
lại layout cho item, thay các đối tượng và tìm cách điều khiển các đối
tượng trong item.

222
Chương 8
HIỂN THỊ HÌNH ẢNH VÀ MENU

8.1. GALLERY VÀ IMAGEVIEW


8.1.1. Gallerys
Gallery là một đối tượng cho phép quản lý hình ảnh trong ứng dụng
của bạn dưới dạng danh sách hình ảnh cuộn ngang. Gallery cũng hỗ trợ
các sự kiện cơ bản cho phép bạn điều khiểu dễ dàng. Hình ảnh dưới là
một gallery cơ bản:

Hình 8.1. Hình ảnh một Gallery ảnh


Với Gallery bạn có thể trình chiếu ảnh trên ứng dụng, dùng cho các
ứng dụng muốn thay đổi hình nền hoặc chọn chức năng chọn ảnh làm gì đó.
8.1.2. ImageView
ImageView là đối tượng cho phép bạn hiển thị hình ảnh từ ảnh lưu
cố định, ảnh lưu trong máy hoặc ảnh tải từ Internet. ImageView cũng hỗ
trợ các sự kiện cơ bản cho phép bạn điều khiển.
Để hiểu rõ hơn về Gallery và ImageView chúng ta sẽ cùng làm ví
dụ sau:
Ví dụ: Thiết kế ứng dụng hiển thị hình ảnh lưu trong ứng dụng.
Với ví dụ này, chúng ta sẽ dùng Gallery để hiển thị danh sách hình
ảnh, cho phép người dùng chọn ảnh và ảnh được chọn sẽ được phóng to
trên ImageView.
Layout của ứng dụng được thiết kế như sau:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
223
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bộ sưu tập ảnh"
android:textStyle="bold"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"/>

<Gallery
android:id="@+id/gallery1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spacing="10dp"/>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

224
android:text="Ảnh đang chọn"
android:layout_gravity="center_horizontal"
android:textAppearance="?android:attr/textAppearanceLarge"/>

<ImageView
android:id="@+id/imgSelect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/back4"/>

</LinearLayout>
Lưu ý: Bạn cần sao chép ảnh vào thư mục mục res/ drawable-
hdpi (hoặc thư mục res/ drawable). Các ảnh có tên là back4, back5,
back6, back7, back8.
Để Gallery có thể hiển thị hình ảnh, ta cần tạo cho nó một Adapter
được kế thừa từ lớp BaseAdapter, dưới đây là code của lớp
GalleryAdapter (file GalleryAdapter.java nằm cũng Package với file
MainActivity.java):
package com.example.gallery_image;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.TextView;

publicclass GalleryAdapter extends BaseAdapter {

225
private Context context;
// Mảng chứa ảnh sẽ hiển thị trên gallery
Privateint[] arrImage;

// Hàm Khởi tạo


public GalleryAdapter(Context context, int[] arrImage) {
super();
this.context = context;
this.arrImage = arrImage;
}

@Override
publicint getCount() {
// TODO Auto-generated method stub
returnarrImage.length;
}

@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
returnnull;
}

@Override
publiclong getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}

226
// Đây là phương thức quan trọng sẽ trả về một Wiew để Gallery
hiển thị
// Để hiển thị hình ảnh ta sẽ dùng ImageView
@Override
PublicView getView(int arg0, View arg1, ViewGroup arg2) {
// TODO Auto-generated method stub
// Khởi tạo đối tượng Image để trả về
ImageView img = new ImageView(context);
// img sẽ hiển thị hình ảnh trong mảng chứa ảnh được
truyền vào, vị trí
// ảnh phụ thuộc vào đối số arg0, đây là đối số vị trí của
View trong
// Gallery
img.setImageResource(arrImage[arg0]);
// Đặt lại kích thước của Image
img.setLayoutParams(newGallery.LayoutParams(150,
150));
return img;
}

}
Code trên tôi đã chú thích rõ ràng, tuy nhiên bạn cần chú ý một số
điểm quan trọng sau:
- Ở ví dụ này, chúng ta hiển thị hình ảnh được tích hợp trong ứng
dụng, để quản lý các hình ảnh này ta sẽ dùng id của chúng, id này là
một số kiểu Integer, do đó ở trên ta khai báo một mảng kiểu Integer để
chứa các hình ảnh muốn hiển thị. Code lấy id của hình ảnh sẽ được viết
ở file MainActivity.java
- Cũng giống như ListView, phương thức getView sẽ được viết
lại để trả về View chúng ta mong muốn, ở đây ta muốn trả về
ImageView để hiển thị do đó sẽ khai báo ImageView img và trả về
bằng lệnh return img.

227
Sau khi đã viết code cho GalleryAdapter, tiếp theo chúng ta sẽ viết
code cho ứng dụng ở file MainActivity.java:
package com.example.gallery_image;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Gallery;
import android.widget.ImageView;
publicclass MainActivity extends Activity {

// Khai báo các đối tượng sử dụng


ImageView imgSelect;
Gallerygallery;
// Lấy id của các ảnh tích hợp trong ứng dụng để quản lý chúng
int[] arrImage = { R.drawable.back4, R.drawable.back5,
R.drawable.back6,
R.drawable.back7, R.drawable.back8 };
// Khai báo một adapter cho Gallery từ lớp GalleryAdapter ta lập trình
GalleryAdapter imgAdapter;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Lấy các đối tượng dựa vào id của chúng
imgSelect = (ImageView) findViewById(R.id.imgSelect);
228
gallery = (Gallery) findViewById(R.id.gallery1);
// Gán context và mảng chứa hình ảnh cho imgAdapter
imgAdapter = new GalleryAdapter(this, arrImage);
// Gán imgAdapter cho gallery để hiển thị hình ảnh
gallery.setAdapter(imgAdapter);
// Xử lý sự kiện chọn một hình ảnh để hiển thị trên
ImageView
gallery.setOnItemSelectedListener(new
OnItemSelectedListener() {

@Override
Publicvoid onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
// TODO Auto-generated method stub
// Gán ảnh muốn hiển thị cho ImageView, ảnh lấy từ
mảng đã
// khai báo ở trên, ảnh phụ thuộc vào vị trí đang chọn
// của Gallery
// arg2: vị trí đang chọn của Gallery

imgSelect.setImageResource(arrImage[arg2]);
}

@Override
Publicvoid onNothingSelected(AdapterView<?>
arg0) {
// TODO Auto-generated method stub

}
});
}
229
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}
}
Từ GalleryAdapter đã được xây dựng, việc sử dụng Gallery gần
giống với sử dụng ListView. Tính năng trượt qua lại để chọn ảnh đã được
Android xây dựng sẵn, do đó chung ta không phải viết code. Một số điểm
quan trọng ở code trên:
- Sự kiện được dùng để xác định chọn ảnh trên Gallery là
setOnItemSellectListener khi sự kiện này được gọi nó sẽ trả về vị trí
của ảnh được chọn trên Gallery thông qua đối số arg2 (vị trí này bắt đầu
từ 0). Dựa vào đối số arg2 ta có thể xác định được ảnh trong mảng quản
lý ảnh (mảng arrImage) từ đó hiển thị ảnh này ra ImageView.
- Image cho phép ta hiển thị hình ảnh bằng cách sử dụng các
phương thức setImageBitmap, setImageDrawable, setImageResource,
setImageURI,… có thể hiện thị hình ảnh từ máy, thẻ nhớ, trên web,… Ở
code trên dùng phương thức setImageResource để đặt hình ảnh trong, đối
số của phương thức này là id của ảnh.
 Để hiện ảnh từ internet bạn có thể dùng phương thức
setImageURI. Yêu cầu ứng dụng của bạn phải được quyền truy cập
intertnet. Ngoài ra, bạn cũng có thể viết code để tải ảnh về và dùng
phương thức setImageDrawable để ImageView hiển thị ảnh. Bạn tham
khảo thêm các tài liệu nâng cao để biết cách sử dụng các tính năng này.
 Phương thức setImageDrawable cũng được sử dụng để hiện ảnh
được tích hợp theo ứng dụng, bạn thử đoạn code sau để hiển thị ảnh
back5 lên ImageView:
imgSelect.setImageDrawable(getResources().getDrawable(R.drawable.
back5));
 Bạn cũng có thể đặt lại kích cỡ ImageView trong lúc run-time để
phù hợp với hình ảnh hiển thị bằng câu lệnh:

230
imgSelect.setLayoutParams(new LayoutParams(x, y));. Với x là width
(chiều rộng của ảnh), y là height (chiều cao của ảnh).
 ImageView cũng hỗ trợ các sự kiện như setOnClickListener
hay setOnLongClickListener. Code sau sẽ xử lý sự kiện khi nhấn vào
ImageView ứng dụng sẽ thoát.
imgSelect.setOnClickListener(new OnClickListener() {
@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
finish();
}
});
Đây là kết quả mô phỏng trên máy ảo:

Hình 8.2 Kết quả mô phỏng Gallery


Cải tiến ứng dụng: các bạn thấy ứng dụng trên muốn chọn ảnh hiển
thị phải chọn trực tiếp trên Gallery, chúng ta sẽ tạo thêm hai nút nhấn,
cho phép thay đổi ảnh bằng các nút nhấn này.

231
Màn hình ứng dụng:

Hình 8.3. Ứng Gallery đơn giản


Gợi ý lập trình: Sử dụng phương thức setSelection và
getSelectedItemPositon của Gallery.
btnAnhTruoc.setOnClickListener(new OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
if (gallery.getSelectedItemPosition() > 0) {

gallery.setSelection(gallery.getSelectedItemPosition() - 1,
true);
}
}
});

232
btnAnhSau.setOnClickListener(new OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
if (gallery.getSelectedItemPosition() <arrImage.length - 1) {
gallery.setSelection(gallery.getSelectedItemPosition() + 1,
true);
}
}
});

8.2. MENU
Menu rất hữu dụng cho việc bổ sung thêm các tùy chọn cho ứng
dụng mà không cần hiển thị lên giao diện, do đó tìm hiểu cách sử dụng
menu sẽ giúp chúng ta thiết kế ứng dụng chuyên nghiệp hơn và hiệu suất
cao hơn. Có hai loại chính là Option menu và Context menu:
Option menu: là menu chính của activity, ở các máy đời cũ có hỗ
trợ phim menu, khi nhấn phím này Option menu sẽ hiện lên, ở các máy
đời mới ko hỗ trợ phim này, nếu chạy Android 3.0 trở lên sẽ có thanh
ActionBar và menu nằm ở thanh này.
Context menu: menu riêng của từng đối tượng (View) trên
Activity, khi muốn hiện menu của đối tượng nào ta chỉ cần chạm và giữ
trên đối tượng đến khi menu hiện. Lưu ý: Phải cài đặt hiển thị Context
menu cho đối tượng thì mới hiển thị được menu khi chạm và giữ.
Hình ảnh dưới là hiển thị cho Options menu khi chạm vào biểu
tượng menu trên thanh ActionBar (máy ảo được sử dụng là Samsung
Galaxy S3 – android 4.2). Các biểu tượng trên thanh ActionBar là các
Option menu nhưng được hiển thị dạng icon để dễ dàng thao tác. Khi
nhấn vào nút menu các menu còn lại sẽ hiện ra dưới dạng danh sách.

233
Hình 8.4. Màn hình thiết kế Menu
Hình ảnh dưới là Context menu khi bạn chạm và giữ trên đối
tượng đã đăng ký Context menu.

Hình 8.5. Context Menu

234
Chúng ta sẽ cùng tìm hiểu qua cách tạo Option menu trước, có hai
cách để tạo là khởi tạo lúc chạy chương trình (viết code trong file java) và
tạo trước menu trong file xml. Chúng ta sẽ cùng tìm hiểu cả hai cách trên.
Cách 1: Tạo menu bằng cách viết code trong file xml:
Tạo file xml có tên mymenu.xml trong thư mục menu:

Hình 8.6.Thư mục menu


Trong file mymenu.xml các bạn nhập code sau:
<?xmlversion="1.0"encoding="utf-8"?>
<menuxmlns:android="http://schemas.android.com/apk/res/androi
d">

<item
android:id="@+id/menuExit"
android:icon="@drawable/ic_action_cancel"
android:showAsAction="always"
android:title="Thoát"/>
<item
android:id="@+id/menuCaiDat"
android:icon="@drawable/ic_action_settings"
android:showAsAction="always"
android:title="Cài đặt"/>
<item
android:id="@+id/menuAbout"
android:icon="@drawable/ic_action_about"

235
android:showAsAction="always"
android:title="Thông Tin"/>
<item
android:id="@+id/menuChinhSua"
android:showAsAction="never"
android:title="Chỉnh Sửa"/>
<item
android:id="@+id/menuHuongDan"
android:showAsAction="never"
android:title="Hướng Dẫn"/>

</menu>
Mỗi item có nhiều nhiều thuộc tính, nhưng các bạn lưu ý một số
thuộc tính sau:
- android:id=“@+id/menuExit”: Dòng này sẽ gán id cho item,
chúng ta sẽ dùng id được gán ở đây để xử lý các sự kiện
- android:icon=“@drawable/ic_action_cancel”: Dòng này để
gán biểu tượng cho item. Từ bản Android 3.0 trở đi có hỗ trợ thanh
ActionBar, các menu nếu được cho phép hiện trên thanh ActionBar sẽ
có biểu tượng được gán ở dòng lệnh này. Lưu ý: Các bạn phải để hình
ảnh muốn hiện vào thư mục res/ drawable (hoặc thư mục res/
drawable-hdpi).
- android:showasaction=“always”: Dòng này để item luôn hiện
trên thanh ActionBar (hiện icon). Ngoài ra còn có mộ số lựa chọn khác
như: never, ifRom,…bạn có thể chọn để tìm hiểu chức năng của chúng.
- Android:Title=“Thoát”: Dòng này dùng để ghi tiêu đề cho item.
Các item nếu không có icon mà được gán hiện ở thanh ActionBar thì sẽ
hiện tiêu đề được gán ở đây, khi nhấn vào nút menu sẽ hiển thị danh sách
các item và trên danh dách này cũng sẽ hiện tên được gán ở đây.
- Thứ tự các item trong khi viết code cũng là thứ tự hiển thị trên
ứng dụng.
Cách 2: Tạo menu bằng coding (lúc Run-time)
Chúng ta sẽ override lại phương thức onCreateOptionMenu như sau:
236
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
//Đối số 1 là nhóm
//Đối số 2 là Id cho Menu Item
//Đối số 3 là thứ tự xuất hiện của Menu Item
//Đối số 4 là tiêu đề cho Menu Item
int idItem;
MenuItem menuItem;
//Item Thoát
idItem = 100;
menu.add(0, idItem, 0, "Thoát");
menuItem = menu.getItem(0);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTI
ON_ALWAYS);
menuItem.setIcon(R.drawable.ic_action_cancel);
//Item Cài Đặt
idItem = 101;
menu.add(0, idItem, 1, "Cài đặt");
menuItem = menu.getItem(1);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTI
ON_ALWAYS);
menuItem.setIcon(R.drawable.ic_action_settings);
//Item Thông tin
idItem = 102;
menu.add(0, idItem, 2, "Thông tin");
menuItem = menu.getItem(2);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTI
ON_ALWAYS);

237
menuItem.setIcon(R.drawable.ic_action_about);
//Item Chỉnh sửa
idItem = 103;
menu.add(0, idItem, 3, "Chỉnh sửa");
menuItem = menu.getItem(3);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTI
ON_NEVER);
//Item Hướng dẫn
idItem = 104;
menu.add(0, idItem, 4, "Hướng dẫn");
menuItem = menu.getItem(4);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTI
ON_NEVER);
returntrue;
}
Ở trên ta dùng phương thức add, các đối số của phương thức đã
được ghi chú trong code. Các bạn thấy các thông số cũng tương tự khi ta
tạo menu bằng file xml. Lưu ý: Khi tạo menu bằng cách coding thì để xử
lý các sự kiện cho menu, ta dùng id đã tạo ở trên (biến idItem).
Tới đây, bạn đã biết được hai cách để tạo menu, tiếp theo chúng ta
sẽ viết code để sử lý các sự kiện bằng cách thực hiện một ví dụ sau:
Ví dụ: Thiết kế ứng dụng hiển thị menu, xử lý khi nhấn vào item
thoát sẽ tắt chương trình, nhấn vào các item còn lại sẽ hiện thông báo đã
nhấn vào item đó.
Code file activity_main.xml
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
238
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Menu và Context Menu"/>

</RelativeLayout>
Code file MainActivity.java
package com.example.menu_ex;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

publicclass MainActivity extends Activity {

239
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
// Khởi tạo menu
getMenuInflater().inflate(R.menu.mymenu, menu);

return true;
}

@Override
Publicboolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.menuExit:
finish();
break;
case R.id.menuCaiDat:
Toast.makeText(this, "Bạn vừa chọn mục 'Cài đặt'",
Toast.LENGTH_SHORT).show();
break;
case R.id.menuAbout:
Toast.makeText(this, "Ứng dụng hiện Menu",
Toast.LENGTH_SHORT).show();
240
break;
case R.id.menuChinhSua:
Toast.makeText(this, "Bạn vừa chọn mục 'Chỉnh sửa'",
Toast.LENGTH_SHORT).show();
break;
case R.id.menuHuongDan:
Toast.makeText(this,"Bạn vừa chọn mục 'Hướng
dẫn'",
Toast.LENGTH_SHORT).show();
break;

}
Returnsuper.onOptionsItemSelected(item);
}

}
Để khởi tạo và xử lý các sự kiện cho menu ta sẽ override lại hai
phương thức là onCreateOptionsMenu và onOptionsItemSelected
- Phương thức onCreateOptionsMenu: để thêm các menu vào
ActionBar. Dòng lệnh để thêm menu:
getMenuInflater().inflate(R.menu.mymenu, menu); Lệnh này sẽ thêm
các item có trong file mymenu.xml vào thanh ActionBar. Do đó, chỉ ghi
dòng lệnh này khi sử dụng cách thêm menu từ file xml, khi tạo trực tiếp
menu trong lúc runtime các bạn không ghi câu lệnh này.
- Phương thức onOptionsItemSelected: dùng để xử lý xự kiện
nhấn vào một item trên menu. Phương thức này sẽ trả về một MenuItem
là Item trong người dùng nhấn vào. Dựa vào Item này ta sẽ lấy id của nó
bằng phương thức getItemId (câu lệnh lấy id: item.getItemId()). Từ id
lấy được so sánh với các id của các Item ta đã tạo trước để xác định
người dùng đã nhấn vào item nào và xử lý chính xác cho item đó. Do có
nhiều id nên kiểm tra bằng cấu trúc switch … case, các sự lệnh xử lý ở
trên khá đơn giản, các bạn đã gặp nhiều ở những phần trước. Khi muốn
xử lý cho chức năng khác, các bạn chỉ cần thay câu lệnh hiện Toast bằng
khối lệnh xử lý là được.
241
Đây là kết quả mô phỏng trên máy ảo:

Hình 8.7. Giao diện ứng dụng Menu và Context menu


Tiếp tục ứng dụng trên, ta sẽ thêm vào giao diện hai đối tượng là
Button và TextView để dùng ContextMenu.
Code file activity_main.xml sau khi thêm hai đối tượng:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
242
android:text="Menu và Context Menu"/>

<Button
android:id="@+id/btnChangeColor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="74dp"
android:text="Đổi màu chữ"/>

<TextView
android:id="@+id/tvChangeStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btnChangeColor"
android:layout_centerVertical="true"
android:text="Đổi kiểu chữ"
android:textAppearance="?android:attr/textAppearanceLarge"/>

</RelativeLayout>
Cách tạo Context menu cũng giống hoàn toàn cách tạo Option
menu, các bạn tạo hai file changecolormenu.xml và
changestylemenu.xml trong thư mục menu (cùng thư mục với file
mymenu.xml).
Code file changecolormenu.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<menuxmlns:android="http://schemas.android.com/apk/res/android">

<item
243
android:id="@+id/cmenuXanh"
android:title="Chữ Màu Xanh"/>
<item
android:id="@+id/cmenuDo"
android:title="Chữ Màu Đỏ"/>
<item
android:id="@+id/cmenutVang"
android:title="Chữ Màu Vàng"/>

</menu>
Code file changestylemenu.xml:
<?xmlversion="1.0"encoding="utf-8"?>
<menuxmlns:android="http://schemas.android.com/apk/res/android">

<item
android:id="@+id/cmenuNormal"
android:title="Bình thường"/>
<item
android:id="@+id/cmenuBold"
android:title="Chữ đậm"/>
<item
android:id="@+id/cmenuItalic"
android:title="Chữ nghiêng"/>
<item
android:id="@+id/cmenuBoldItalic"
android:title="Chữ đậm - nghiêng"/>

</menu>

244
Code để khởi tạo và xử lý sự kiện cho Context menu được viết
trong file MainActivity.java, dưới đây là code gồm cả phần Option menu
ở ví dụ trên và phần xử lý cho Context menu:
package com.example.menu_ex;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.transition.Visibility;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

publicclass MainActivity extends Activity {

// Khai báo đối tượng sử dụng


private Button btnChangeColor;
private TextView tvChangeStyle;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Lấy các đối tượng dựa vào id
245
btnChangeColor = (Button)
findViewById(R.id.btnChangeColor);
tvChangeStyle = (TextView)
findViewById(R.id.tvChangeStyle);
// Đăng ký Context Menu cho các đối tượng
registerForContextMenu(btnChangeColor);
registerForContextMenu(tvChangeStyle);
}

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
// Khởi tạo menu
getMenuInflater().inflate(R.menu.mymenu, menu);
return true;
}

@Override
Publicvoid onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
// TODO Auto-generated method stub
if (v.getId() == R.id.btnChangeColor) {
getMenuInflater().inflate(R.menu.changecolormenu, menu);
} elseif (v.getId() == R.id.tvChangeStyle) {
getMenuInflater().inflate(R.menu.changestylemenu, menu);
}
super.onCreateContextMenu(menu, v, menuInfo);
}

246
@Override
Publicboolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.menuExit:
finish();
break;
case R.id.menuCaiDat:
Toast.makeText(this, "Bạn vừa chọn mục 'Cài đặt'",
Toast.LENGTH_SHORT).show();
break;
case R.id.menuAbout:
Toast.makeText(this, "Ứng dụng hiện Menu",
Toast.LENGTH_SHORT)
.show();
break;
case R.id.menuChinhSua:
Toast.makeText(this, "Bạn vừa chọn mục 'Chỉnh sửa'",
Toast.LENGTH_SHORT).show();
break;
case R.id.menuHuongDan:
Toast.makeText(this, "Bạn vừa chọn mục 'Hướng dẫn'",
Toast.LENGTH_SHORT).show();
break;

}
returnsuper.onOptionsItemSelected(item);
}

247
@Override
publicboolean onContextItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.cmenuDo:
btnChangeColor.setTextColor(Color.RED);
break;
case R.id.cmenutVang:
btnChangeColor.setTextColor(Color.YELLOW);
break;
case R.id.cmenuXanh:
btnChangeColor.setTextColor(Color.GREEN);
break;
case R.id.cmenuNormal:
tvChangeStyle.setTypeface(null,
Typeface.NORMAL);
break;
case R.id.cmenuBold:
tvChangeStyle.setTypeface(null,
Typeface.BOLD);
break;
case R.id.cmenuItalic:
tvChangeStyle.setTypeface(null,
Typeface.ITALIC);
break;
case R.id.cmenuBoldItalic:
tvChangeStyle.setTypeface(null,
Typeface.BOLD_ITALIC);
break;
}

248
returnsuper.onContextItemSelected(item);
}
}

Để đăng ký cho đối tượng nào hiện Context menu, bạn dùng câu
lệnh registerForContextMenu(Đối tượng muốn đăng ký), ở ví dụ này
chúng ta đăng ký cho Button và TextView nên đối tượng đăng ký sẽ là:
btnChangeColor và tvChangeStyle. Cũng giống như Option menu,
Context menu cũng có hai phương thức quan trọng bạn cần lưu ý là:
- Phương thức onCreateContextMenu: Phương thức này dùng để
khởi tạo Context menu khi người dùng chạm và giữ vào đối tượng đã
đăng ký Context menu. Có nhiều đối tượng được đăng ký, do đó trước
khi khởi tạo menu để hiển thị, chúng ta cần xác định đối tượng nào được
gọi để hiển thị menu. Phương thức onCreateContextMenu sẽ cho chúng
ta biết câu trả lời qua đối số View v, dựa vào đối số này chúng ta sẽ lấy
id của đối tượng được nhấn bằng lệnh v.getId từ đó xác định được chính
xác đối tượng muốn hiện Context menu. Sau đó, chúng ta sẽ dùng câu
lệnh: getMenuInflater().inflate(R.menu.changecolormenu, menu); để
khởi tạo menu, bạn lưu ý chỗ changecolormenu đây là tên file
changecolormenu.xml ta tạo để chứa menu. Tương tự, khi muốn hiện
menu cho đối tượng khác ta cũng xác định đối tượng và thay chỗ
changecolormenu bằng tên file xml chứa menu của đối tượng đó.
- Phương thức onContextItemSelect: dùng để xử lý sự kiện khi
người dùng chọn vào một item trong Context menu. Cách xử lý cũng
tương tự như Option menu.

249
250
Chương 9
XỬ LÝ SỰ KIỆN

Xư lý sự kiện (Event Handling) là cách hữu ích để thu thập dữ liệu


tương tác của người dùng với các thành phần tương tác trên ứng dụng
của ta như xử lý nút nhấn button hoặc chạm vào màn hình…
Cách làm việc của Android là duy trì một hàng đợi sự kiện. Ở đó
khi các sự kiện xảy ra sẽ được xếp vào hàng đợi này và sau đó chúng sẽ
xóa đi từ hàng đợi theo nguyên tắc là ai vào xếp hàng trước sẽ ra trước
(FIFO: first-in, first-out). Bạn có thể bắt lấy các sự kiện trong chương
trình của bạn và đưa ra hành động tương ứng theo yêu cầu. Sau đây là ba
khái niệm liên quan đến Xử lý sự kiện Android:
Event Listeners (Trình lắng nghe sự kiện): Lớp View này chủ
yếu liên quan đến việc xây dựng một giao diện người dùng Android,
tương tự lớp View này cung cấp một số Event Listeners. Event Listener
là một đối tượng, nó sẽ nhận thông báo khi một sự kiện xảy ra.
Event Listeners Registration (Đăng ký lắng nghe sự kiện):
Event Registration (đăng ký sự kiện) là quá trình Event Handler đăng ký
với Event Listener để Handler (Trình xử lý) này được gọi khi Event
Listener bắn phát sự kiện nào đó.
Event Handlers (Trình xử lý sự kiện): Khi một sự kiện xảy ra và
chúng ta đã đăng ký và Event listener bắn phát sự kiện đó thì Event
Listener gọi các Event Handler đã đăng ký để xử lý sự kiện đó.
Các Event Listener và Event Handler
Event Handler Event Listener và Mô tả
onClick() OnClickListener()
Nó được gọi khi người dùng click hoặc chạm hoặc
focus (tập trung) trung bất kỳ widget nào giống
như nút nhấn, chữ, hình ảnh,… Bạn sẽ sử dụng
trình xử lý sự kiện onClick() để xử lý sự kiện như
thế này.
onLongClick() OnLongClickListener()
Nó được gọi khi người dùng click hoặc chạm
(touch) hoặc tập trung (focus) vào bất kỳ widget
nào giống như nút nhấn, văn bản, hình ảnh,…

251
trong một hoặc nhiều giây. Bạn sẽ sử dụng trình xử
lý sự kiện onLongClick() để xử lý sự kiện như thế
này.
onFocusChange() OnFocusChangeListener()
Được gọi khi widget không còn focus nó nữa, ví
dụ người dùng đã đi xa đối tượng view này. Bạn sẽ
sử dụng trình xử lý sự kiện OnFocusChange() để
xử lý sự kiện như thế.
onKey() OnFocusChangeListener()
Được gọi khi người dùng được focus trên một đối
tượng và nhấn hoặc thả phím cứng trên thiết bị.
Bạn sẽ sử dụng trình xử lý sự kiện onKey() để xử
lý sự kiện như thế này.
onTouch() OnTouchListener()
Được gọi khi người dùng nhấn phím, nhả phím
hoặc bất sự di chuyển cử động nào trên màn hình.
Bạn sẽ sử dụng trình xử lý sự kiện onTouch() để
xử lý các sự kiện như thế này.
onMenuItemClick() OnMenuItemClickListener()
Được gọi khi người dùng chọn một đối tượng
menu. Bạn sử dụng trình xử lý sự kiện
onMenuItemClick() để xử lý sự kiện này.

Có nhiều Event Listener đáp ứng như một phần của lớp View giống
như OnHoverListener, OnDragListener,… có thể cần cho ứng dụng của
bạn. Vì vậy, các bạn nên tham khảo tài liệu chính thức và mới nhất của
phát triển ứng dụng Android trong trường hợp bạn đang phát triển các
ứng dụng phức tạp.
Event Listeners Registration (Đăng ký lắng nghe sự kiện)
Đăng ký lắng nghe sự kiện là quá trình xử lý sự kiện (Event
Handler) đăng ký với Event Listener để Event Handler tương ứng được
gọi khi Event Listener bắn phát sự kiện đó. Mặc dù có nhiều cách để
đăng ký các event listener của bạn cho bất kỳ sự kiện nào, nhưng ở đây ta
chỉ liệt kê ba cách. Bạn có thể chọn sử dụng bất kỳ cách nào tùy điều
kiện thích hợp.
- Sử dụng một lớp ẩn bên trong (Anonymous Inner Class)

252
- Sử dụng lớp Activity thi hành giao diện Listener.
- Sử dụng file Layout activity_main.xml để chỉ định trực tiếp một
trình xử lý sự kiện (event handler).
Các ví dụ về xử lý sự kiện

9.1. ĐĂNG KÝ EVENT LISTENER BẰNG VIỆC SỬ DỤNG MỘT


LỚP ẨN BÊN TRONG
Ở đây bạn sẽ tạo một thực thi ẩn của trình listener và nó sẽ hữu ích
nếu mỗi lớp (class) chỉ thực hiện một điều khiển đơn và bạn sẽ thuận lợi
là không phải thiết lập các đối số cho trình xử lý sự kiện. Trong cách tiếp
cận này, các phương thức trình xử lý sự kiện có thể truy cập dữ liệu riêng
tư của Activity.
Nhưng nếu bạn áp dụng trình xử lý này cho nhiều điều khiển, bạn
sẽ phải cắt và dán mã (code) cho trình xử lý sự kiện và nếu code cho
trình xử lý sự kiện là dài thì nó sẽ gây khó khăn nếu gặp trục trặc và sửa
chữa. Tuy nhiên, cách nào cũng có ưu và khuyết điểm, bạn có thể tùy
chọn cách nào bạn cảm thấy thích hợp.
Ví dụ sau sẽ trình bày từng bước để chúng ta biết cách sử dụng lớp
Listener riêng để đăng ký và bắt sống sự kiện click. Bạn có thể thực hiện
tương tự với listener của bạn cho bất kỳ loại sự kiện yêu cầu khác.
Yêu cầu: Thiết kế giao diện như sau:

Hình 9.1. Giao diện ứng dụng tăng font chữ


253
Khi nhấn vào nút LARGE FONT thì chữ “HELLO YOU!” tăng
kích thước lên và đổi sang màu đỏ, khi nhấn nút SMALL FONT thì chữ
“HELLO YOU!” giảm kích thước và chuyển sang màu xanh dương.
Bước 1: Tạo một ứng dụng android có tên là Handler1, với layout
theo yêu cầu. Hiệu chỉnh file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="22dp"
android:text="@string/large_btn"
android:background="#41E7FE"/>

<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"

254
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:background="#41E7FE"
android:text="@string/small_btn"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="93dp"
android:layout_marginTop="42dp"
android:text="@string/hello_world"
android:layout_centerInParent="true"
android:gravity="center"/>

</RelativeLayout>

Bước 2: Định nghĩa các hằng số tương ứng trong file


res\values\string.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">Handler1</string>
<string name="action_settings">Settings</string>
<string name="hello_world">HELLO YOU!</string>
<string name="large_btn">LARGE FONT</string>
<string name="small_btn">SMALL FONT</string>
</resources>

255
Bước 3: Viết chương trình cho các sự kiện và phương thức xử lý
tại file MainActivity.java như sau:
package fivemen.com;

import android.os.Bundle;
import android.app.Activity;
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//find both the buttons
Button large_button=(Button)findViewById(R.id.button1);
Button small_button=(Button)findViewById(R.id.button2);

large_button.setOnClickListener(new
View.OnClickListener() {
@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
TextView hello_txt=(TextView)findViewById(R.id.textView1);
hello_txt.setTextSize(30);
hello_txt.setTextColor(Color.RED);
256
}
});

small_button.setOnClickListener(new
View.OnClickListener() {
@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
TextView hello_txt=(TextView)
findViewById(R.id.textView1);
hello_txt.setTextSize(14);
hello_txt.setTextColor(Color.BLUE);
}
});
}

@Override
publicboolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

Giải thích: Ở trên ta có hai sự kiện click, mỗi sự kiện click trên
một Button ta viết riêng một trình xử lý sự kiện. Như vậy ta đăng ký hai
trình xử lý sự kiện cho Event Listener là OnClickListener. Như vậy mỗi
nút nhấn ta thiết lập một đăng ký.
large_button.setOnClickListener();
small_button.setOnClickListener();
257
Đối với cách viết này ta thấy khá đơn giản nếu có ít sự kiện và các
công việc xử lý là đơn giản.
Để thay đổi màu sắc của chuỗi chữ HELLO YOU! Ta phải khai
báo thư viện:
import android.graphics.Color;
Và ta dùng thuộc tính .setTextColor(). Ở đây, ta muốn TextView
hello_txt là màu đỏ ta viết lệnh sau:
hello_txt.setTextColor(Color.RED);
Còn muốn thay đổi kích thước của chữ HELLO YOU! Ta dùng
thuộc tính .setTextSize() của TextView hello_txt
Bước 4: Chạy mô phỏng với máy ảo
- Khi nhấn vào nút LARGE FONT ta nhận được kết quả như sau:

Hình 9.2. Kết quả khi nhấn nút tăng font


- Khi nhấn vào SMALL FONT ta được kết quả như sau:

258
Hình 9.3. Kết quả khi nhấn nút giảm font

9.2. ĐĂNG KÝ SỰ KIỆN SỬ DỤNG ACTIVITY THỰC THI GIAO


DIỆN LISTENER
Ở đây lớp Activity của bạn thực thi giao diện Listener và bạn đặt
phương thức xử lý handler vào main Activity và sau đó bạn gọi
setOnClickListener(this).
Cách tiếp cận này thì tốt nếu ứng dụng của bạn chỉ có một điều
khiển đơn của một loại Listenner đó, nếu không bạn sẽ phải lập trình để
kiểm soát điều khiển (control) đã phát sinh sự kiện. Hơn nữa, bạn không
thể bỏ qua các đối số Listener, cho nên sẽ khó khăn nếu bạn làm việc với
nhiều điều khiển.
Ví dụ sau chỉ ra từng bước chúng ta sử dụng lớp Listener để đăng
ký và bắt sống sự kiện click. Bạn có thể làm tương tự cho listener của
bạn cho bất kỳ loại sự kiện nào khác.
Yêu cầu: Giống như bài ví dụ ta mới vừa làm. Thiết kế giao diện
như sau:

259
Hình 9.4. Giao diện ứng dụng xử lý xử kiện sử dụng lớp Listener
Khi nhấn vào nút LARGE FONT thì chữ “HELLO YOU!” tăng
kích thước lên và đổi sang màu đỏ, khi nhấn nút SMALL FONT thì chữ
“HELLO YOU!” giảm kích thước và chuyển sang màu xanh dương.
Bước 1: Tạo một ứng dụng android có tên là Handler2, với layout
theo yêu cầu. Hiệu chỉnh file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button
android:id="@+id/button1"

260
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="22dp"
android:text="@string/large_btn"
android:background="#41E7FE"/>

<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:background="#41E7FE"
android:text="@string/small_btn"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="93dp"
android:layout_marginTop="42dp"
android:text="@string/hello_world"
android:layout_centerInParent="true"
android:gravity="center"/>

</RelativeLayout>

261
Bước 2: Định nghĩa các hằng số tương ứng trong file
res\values\string.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">Handler2</string>
<string name="action_settings">Settings</string>
<string name="hello_world">HELLO YOU!</string>
<string name="large_btn">LARGE FONT</string>
<string name="small_btn">SMALL FONT</string>

</resources>
Bước 3: Viết chương trình cho các sự kiện và phương thức xử lý
tại file MainActivity.java như sau:
package fivemen.com;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
import android.graphics.Color;

publicclass MainActivity extends Activity implements


OnClickListener {

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
262
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//find both the buttons
Button large_btn = (Button) findViewById(R.id.button1);
Button small_btn=(Button) findViewById(R.id.button2);

// register click event with large button


large_btn.setOnClickListener(this);
// register click event with small button
small_btn.setOnClickListener(this);
}

//---Implement the OnClickListener callback


@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub

if (v.getId()== R.id.button1)
{
TextView
hello_txt=(TextView)findViewById(R.id.textView1);
hello_txt.setTextSize(30);
hello_txt.setTextColor(Color.RED);
}

if (v.getId()==R.id.button2)
{
TextView
hello_txt=(TextView)findViewById(R.id.textView1);
263
hello_txt.setTextSize(14);
hello_txt.setTextColor(Color.BLUE);
}

}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}

}
Giải thích: Ở cách làm này, ta có hai sự kiện Click trên hai nút
nhấn, nhưng ta chỉ đăng ký chung là sự kiện Click trên Activity. Có
nghĩa là bất kỳ một trong hai nút nhấn này được nhấn (click) thì trình
lắng ngheOnClickListenter đều phát sinh sự kiện và trình xử lý sự kiện
OnClick() được gọi ra. Vì OnClickListenter(this) là Listener (trình lắng
nghe) của Activity ta đang thao tác, nên có bao nhiêu đối tượng trên
Activity đăng ký sự kiện Click thì nó đều gọi trình xử lý sự kiện
OnClick(). Do đó, khi nhảy vào trình xử lý sự kiện này ta phải đi tìm xem
nút nhấn nào đã được nhấn, tức là tìm được ID của nút nhấn được nhấn.
Ở đây ta tìm nó bằng thuộc tính .getID() và ta so sánh xem nó với ID của
các nút nhấn ta đã biết để có hành động xử lý tương ứng.
if (v.getId()== R.id.button1)
{
TextView
hello_txt=(TextView)findViewById(R.id.textView1);
hello_txt.setTextSize(30);
hello_txt.setTextColor(Color.RED);
}

264
Ở đây nếu ID của nút nhấn phát sinh sự kiện Click nếu đúng bằng
ID của nút button1 (nút Large Font) thì ta tăng cỡ chữ lên và thay đổi
màu chữ.
Bước 4: Chạy mô phỏng với máy ảo ta có kết quả như sau:
- Nhấn vào nút LARGE FONT:

Hình 9.5. Kết quả khi nhấn Large Font


- Nhấn vào nút SMALL FONT:

Hình 9.6. Kết quả khi nhấn nút Small Font


265
9.3. ĐĂNG KÝ SỰ KIỆN BẰNG CÁCH SỬ DỤNG FILE LAYOUT
ACTIVITY_MAIN.XML ĐỂ CHỈ ĐỊNH TRỰC TIẾP MỘT TRÌNH
XỬ LÝ SỰ KIỆN
Ở đây, chúng ta đặt các trình xử lý sự kiện trong lớp Activity mà
không thực hiện một giao diện Listener hoặc gọi bất kỳ một phương thức
Listener nào. Bạn sẽ sử dụng file layout (activity_main.xml) để chỉ định
trình xử lý sự kiện thông qua thuộc tính android:onClick cho sự kiện
Click. Bạn có thể điều khiển các sự kiện Click một cách riêng biệt cho
các điều khiển (control) khác nhau thông qua các phương thức xử lý sự
kiện khác nhau.
Phương thức xử lý sự kiện này phải là một hàm trả về và lấy View
như một đối số. Tuy nhiên, tên phương thức là tùy ý và lớp main không
cần thực thi bất kỳ giao diện đặc biệt nào.
Các tiếp cận này không cho phép bạn vượt qua các đối số Listener
và các nhà phát triển Android sẽ khó khăn để hiểu phương thức nào của
trình xử lý sự kiện đã được sử dụng cho control cho đến khi họ xem file
main.xml. Hơn nữa, bạn không thể xử lý bất kỳ các sự kiện khác ngoài sự
kiện Click theo cách tiếp cận này.
Trở lại ví dụ trước đó, cùng yêu cầu nhưng ta sẽ áp dụng các tiếp
cận này:
Bước 1: Tạo một ứng dụng android có tên là Handler3, với layout
theo yêu cầu. Hiệu chỉnh file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<Button

266
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="22dp"
android:text="@string/large_btn"
android:background="#41E7FE"
android:onClick="doLarge"/>

<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/button1"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:background="#41E7FE"
android:text="@string/small_btn"
android:onClick="doSmall"/>

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

android:layout_marginLeft="93dp"
android:layout_marginTop="42dp"
android:text="@string/hello_world"
android:layout_centerInParent="true"

267
android:gravity="center"/>

</RelativeLayout>

Ở đây ta chú ý ở Button1 dòng code: android:onClick="doLarge"


doLarge là một hàm trả về ta sẽ viết trong file .java sau. Tên
doLarge do ta đặt, ta có thể đặt tên khác nhưng phải trùng với tên hàm ta
viết trong file .java. Hàm doLarge sẽ có nhiệm vụ tăng kích thước chuỗi
chữ lên và thay đổi màu của chuỗi chữ HELLO YOU! Tương tự ta xem
xét dòng codeandroid:onClick="doSmall"

Bước 2: Định nghĩa các hằng số tương ứng trong file


res\values\string.xml
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">Handler3</string>
<string name="action_settings">Settings</string>
<string name="hello_world">HELLO YOU!</string>
<string name="large_btn">LARGE FONT</string>
<string name="small_btn">SMALL FONT</string>

</resources>

Bước 3: Viết code cho các hàm doLarge và doSmall trong file
MainActivity.java
package fivemen.com;

import android.os.Bundle;
import android.app.Activity;

268
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

publicclass MainActivity extends Activity {

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
// Implement the event handler for the Small Font button
Publicvoid doSmall(View v){
//find the text view
TextView hello_txt=(TextView) findViewById(R.id.textView1);
//change text size
hello_txt.setTextSize(14);
//change text Color to Blue
hello_txt.setTextColor(Color.BLUE);
return;
}
// Implement the event handler for the Large Font Button
Publicvoid doLarge(View v){
//find the text view
TextView hello_txt=(TextView) findViewById(R.id.textView1);
//change text size
hello_txt.setTextSize(32);
//change text Color to RED

269
hello_txt.setTextColor(Color.RED);
return;
}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}

Nhận thấy ở đây ta không cần tìm ID của các Button, bởi vì tương
ứng mỗi Button tương tác ta đã gán luôn thuộc tính OnClick và gọi một
hàm tương ứng với sự kiện đó. Lưu ý các hàm ở đây đều là hàm trả về
có return;
Bước 4: Chạy mô phỏng với máy ảo ta có kết quả tương tự các hai
ví dụ trước đó.

270
Chương 10
CÁC ACTIVITY VÀ INTENT

Nội dung chương này nhắc lại về Activity và Intent. Thật ra, hai
vấn đề này đã được trình bày ở chương 4: Các thành phần cơ bản trong
Android. Tuy nhiên ở chương 4, các bạn chương tìm hiểu về cách lập
trình, chưa làm quen các đối tượng điều khiển, chưa biết các thiết kế giao
diện và các phương thức xử lý sự kiện… Cho nên để đưa ra mục tiêu bạn
hiểu rõ về Intent và sử dụng Intent để liên kết các Activity là điều khó
khăn. Vì vậy, các nội dung về Activity, Intent ở chương 4 với mục đích
giới thiệu là chính. Nhưng ở chương này thì lại khác, sau khi các bạn đã
nắm được gần hết các vấn đề cơ bản, chúng ta có thể dễ dàng tiếp cận và
sử dụng hiệu quả các Activity và Intent hơn.

10.1. ACTIVITY
Như ta đã biết, một activity là một cửa sổ chứa giao diện người
dùng của ứng dụng. Thông thường, các ứng dụng có một hoặc nhiều hơn
một Activity và sự hỗ trợ chính của Activity là tương tác với người dùng.
Từ lúc Activity xuất hiện trên màn hình cho đến khi nó được ẩn đi, nó đi
qua một số trạng thái, được biết như là chu kỳ sống của Activity. Hiểu
biết chu kỳ sống của Activity giúp ta đảm bảo rằng ứng dụng của ta làm
việc đúng. Các khái niệm về trạng thái, chu kỳ sống được trình bày ở
chương 4, các bạn có thể xem lại.
Đầu tiên chúng ta xem cách để tạo một Activity như thế nào. Để
tạo một Activity bạn tạo một lớp Java class extends the Activity:
package net.learn2develop.Activities;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
/**Được gọi khi Activity được tạo đầu tiên */
@Override
public void onCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
271
}
}
Lớp activity của bạn sẽ được tải các thành phần UI (giao diện
người dùng) của nó sử dụng file XML được định nghĩa trong thư mục
res/layout . Trong ví dụ này, nó sẽ tải (load) giao UI từ file main.xml
setContentView(R.layout.main);
Mỗi Activity bạn có trong ứng dụng của bạn, bạn phải khai báo
trong file AndroidManifest.xml giống như sau:
<?xml version=”1.0” encoding= ”utf-8”?>
<manifest xmlns:android=
”http://schemas.android.com/apk/res/android”
package= ”net.learn2develop.Activities”
android:versionCode= ”1”
android:versionName= ”1.0”>
<application android:icon= ”@drawable/icon”
android:label= ”@string/app_name”>
<activity android:name= ”.MainActivity”
android:label= ”@string/app_name”>
<intent-filter>
<actionandroid:name=”android.intent.action.MAIN” />
<category
android:name=”android.intent.category.LAUNCHER”/>
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion=”9”/>
</manifest>
Ẩn Title (tựa đề) Activity
Bạn có thể ẩn đi title của một activity nếu bạn muốn (giống như khi
bạn chỉ muốn hiển thị một cập nhật trạng thái đến người dùng). Để làm
điều này, sử dụng phương thức requestWindowFeature() và chọn hằng
số Window.FEATURE_NO_TITLE như sau:
272
Ví dụ một giao diện không ẩn và ẩn title như sau:

Hình 10.1. Giao diện chưa ẩn và đã ẩn title


Ví dụ ở giao diện trên để ẩn title ta thêm dòng lệnh ẩn title trong
file.java trong hàm OnCreate () của activity cần ẩn. Lưu ý ta phải nhập
thêm thư viện Window
package com.example.guibutton;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.view.Window; //nhập thư viện Window

publicclass MainActivity extends Activity {

273
private EditText edText1,edText2,edText3;
private Button btnProduct, bttotal;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Ẩn title của Activity
requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_main);
}

10.2. LIÊN KẾT CÁC ACTIVITY SỬ DỤNG CÁC INTENT


Một ứng dụng Android có thể không có hoặc có nhiều Activity.
Khi ứng dụng của bạn có nhiều hơn một Activity, bạn có thể cần định vị
một activity từ một activity khác. Trong Android, bạn định vị các activity
với nhau thông qua Intent.
Cách tốt nhất để hiểu được khái niệm rất quan trọng nhưng hơi trừu
tượng này là trải nghiệm nó và xem nó hoạt động như thế nào? Sau đây
ta tạo ra các activity khác từ một project đã có và định vị giữa hai activity
với nhau.
Từ một activity ta gọi một activity khác ta có thể dùng một tên lớp
(class name) của activity muốn gọi. Ta thường có hai cách tiếp cận: một
là chỉ gọi activity mới ra (Activity mới phải được đặt trong cùng project
với activity gốc), activity gọi không truyền dữ liệu gì qua activity được
gọi; hai là khi gọi activity mới, activity gốc truyền dữ liệu qua activity
mới được gọi.
Trường hợp thứ nhất, một activity gọi một activity mới không
truyền dữ liệu:
Ta xem một ví dụ sau: Ta có một có Activity là MainActivity và
một Activity khác tên là Child Activity
Giao diện Main Activity: Khi nhấn vào nút Open a Child Activity
thì Child Activity được gọi ra

274
Hình 10.2. Giao diện Activity chính
Giao diện Child Activity: Khi nhấn vào nút Return MainActivity
thì Child Activity đóng lại mở Main Activity ra

Hình 10.3. Giao diện Child Activity


Để làm được yêu cầu trên ta làm như sau:
Tạo mới Android Project. Lưu ý khi ta tạo mới một Android
Project, ta thống nhất tên gọi và đóng gói. Ví dụ ở đây tôi đặt tên Project
và đóng gói (package) như sau (các bạn có thể thay đổi các tên cho dễ
nhớ với các bạn):
275
Hình 10.4. Giao diện tạo mới Android Project
- Đầu tiên ta tạo layout cho Main Activity trong file
res/activity_main.xml
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"

276
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/TextView1"
android:layout_width="match_parent"
android:layout_height="50sp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:background="#EFF794"
android:gravity="center"
android:text="@string/hello_world"
android:textColor="#1300FE"
android:textSize="16sp">
</TextView>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/TextView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="64dp"
android:background="#3BF6FF"
android:text="@string/bt1_txt"/>

</RelativeLayout>
- Tạo một Activity mới theo trình tự sau:
Vào File\New\Other…

277
Hình 10.5. Giao diện tạo một Activity mới
Chọn Android Activity

Hình 10.6. Giao diện tạo Activity theo hướng dẫn của phần mềm
278
Hộp thoại tạo Android Activity mở ra:

Hình 10.7. Giao diện tạo mới Activity


Đặt tên cho Activity là ChildActivity

Hình 10.8. Đặt tên cho Activity mới tạo


279
- Ta có ChildActivity mới được tạo ra. Ta thiết kế layout cho nó
như yêu cầu tại file res/layout/activity_child.xml
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ChildActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="50sp"
android:background="#4BCC1E"
android:gravity="center"
android:text="@string/tv1_child_txt"
android:textSize="18sp"/>

<Button
android:id="@+id/buttonback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginLeft="32dp"
android:layout_marginTop="52dp"
android:text="@string/bt1_child_txt"/>
280
</RelativeLayout>

Khai báo các hằng số trong file res/value/string.xml như sau:


<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">GUIIntent</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world! This is a Main
Activity</string>
<string name="label_child_activity">ChildActivity</string>
<string name="bt1_txt">Open a Child Activity</string>
<string name="tv1_child_txt">Hello. I am a Child
Activity</string>
<string name="bt1_child_txt">Return MainActivity</string>
<string name="title_activity_activity2">Activity2</string>

</resources>

- Mở file res/AndroidManifest.xml lên và ta thấy nếu ta làm như


hướng dẫn trên thì Activity mới tạo bằng Wizard đã được khai báo sẵn.
Điều này rất tiện cho chúng ta nếu không chúng ta phải tự gõ vào. Lưu ý,
tất cả các Activity đều phải được khai báo trong file
AndroidManifest.xml. Đoạn tô đậm bên dưới là phần tự phát sinh cho
ChildActivity khi ta tạo nó bằng Wizard.
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="nguyenvanhiep.fivemen"
android:versionCode="1"
android:versionName="1.0">

281
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="nguyenvanhiep.fivemen.MainActivity"
android:label="@string/app_name">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
//khai báo child activity

<activity
android:name="nguyenvanhiep.fivemen.ChildActivity" //tên
Activity mới
android:label="@string/label_child_activity"> //được khai báo
ở strings.xml
<intent-filter>
<actionandroid:name="nguyenvanhiep.fivemen.CHILDACTIVI
TY"/>

<categoryandroid:name="android.intent.category.DEFAULT"/>

282
</intent-filter>

</activity>
</application>

</manifest>
Dòng lệnh
<action
android:name="nguyenvanhiep.fivemen.CHILDACTIVITY"/>
ta khai báo tên của bộ lọc intent cho activity. Những activity khác muốn
gọi activity này phải gọi thông qua tên này. Ở đây, ta sử dụng
nguyenvanhiep.fivemen thêm vào để hạn chế tối đa các ứng dụng khác
có bộ lọc intent (intent-filter) trùng tên. Vì nếu chỉ để CHILDACTIVITY
có thể dễ dẫn đến một ứng dụng khác đặt cùng tên và khi đó nhiều hơn
một activity được gọi ra. Khi đó xảy ra đụng độ Activity và chúng ta phải
chọn lựa.
Dòng lệnh
<categoryandroid:name="android.intent.category.DEFAULT"/>
cần được thêm vào để activity này có thể được khởi động từ một activity
khác bằng cách sử dụng phương thức startActivity().
Nếu bạn tạo một Activity mới theo cách thủ công bạn phải tự khai
báo phần này.
<activity android:name=".NewActivity" //ta phải khai tên
chính xác Activity
android:label="@string/some_app_name">
<intent-filter>
<action
android:name="android.intent.action.VIEW" />
<category
android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>

283
Như vậy, theo cách làm hướng dẫn từ đầu tới đây, ta sẽ có hai file
.java để viết code cho hai Activity. Nếu ta làm theo Wizard như hướng
dẫn thì các khai báo mặc định sẽ được tạo sẵn. Nếu không ta phải tự tạo
file ChildActivity.java và tự khai báo theo cú pháp sau:
Bước khai báo một Activity mới trong file AndroidManifest.xml
xong, ta thực hiện việc gọi một Activity mới từ một Activity theo cú
pháp sau:
Intent activityIntent= new Intent(this, NewActivity.class);
startActivity(activityIntent);
Dòng đầu là tạo một Intent mới có tên là activityIntent (tên này ta
có thể thay đổi). NewActivity được thay thế bằng tên chính xác của
Activity ta muốn gọi ra.
Tuy nhiên, ta để ý từ this ở đây có nghĩa chỉ những activity trong
cùng project với nhau mới được gọi nhau.
Dòng thứ hai là khởi động một Activity thông qua Intent tên
activityIntent
Như vậy, như phân tích nãy giờ từ MainActivity ta muốn gọi
ChildActivity ta có thể sử dụng cú pháp như sau:
Intent childIntent = new Intent(this, ChildActivity.class));
StartActivity(childIntent);
Thực hiện hoàn chỉnh cho
filesrc/nguyenvanhiep.fivemen/MainActivity.java để viết code như sau:
Lưu ý các dòng in đậm
package nguyenvanhiep.fivemen;
//khai báo thư viện
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.content.Intent; /

284
public classMainActivityextends Activity {

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//Tìm Button “Open a Child Activity”


Button bt1;
bt1=(Button)findViewById(R.id.button1);

bt1.setOnClickListener(new OnClickListener(){

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
startActivity(new
Intent("nguyenvanhiep.fivemen.CHILDACTIVITY"));

}
});
}

@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
285
}
Ta thấy dòng lệnh để khởi động một ChildActivity ở đây là:
startActivity(new Intent("nguyenvanhiep.fivemen.CHILDACTIVITY"));
hoặc:
Intent childIntent = new Intent("nguyenvanhiep.fivemen.CHILDACTIVITY"));
StartActivity(childIntent);
Khi ta ghi đầy đủ như trên thì có một lợi thế là các Activity của các
ứng dụng Android khác project đang cùng chạy trên thiết bị vẫn có thể sử
dụng cú pháp này để gọi ChildActivity ra. Còn nếu ta ghi
startActivity(new Intent(this,ChildActivity.class)); thì chỉ những
Activity trong cùng project với ChildActivity mới gọi được.
Tiếp theo mở file src/nguyenvanhiep.fivemen/ChildActivity.java để
viết code cho ChildActivity như sau: Lưu ý các dòng in đậm
package nguyenvanhiep.fivemen;

import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ChildActivity extends Activity {

protectedvoid onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);

Button btback;

286
btback=(Button)findViewById(R.id.buttonback);

btback.setOnClickListener(new OnClickListener(){

publicvoid onClick(View v) {
// TODO Auto-generated method stub
finish(); //kết thúc Activity đang mở

}
});
}
}

Trường hợp thứ hai, một activity gọi một activity mới và
truyền dữ liệu qua Activity mới:
Ý tưởng: Đính kèm một Bundle (đóng gói) đến Intent. Bundle sẽ
chứa dữ liệu để được sử dụng cho Activity mới.
Cú pháp:
- Java trong Activity gốc như sau:
Intent activityIntent= new Intent(this, NewActivity.class);
Bundle newActivityInfo= new Bundle();
newActivityInfo.putBlah(…); // putDouble, putString, etc.
activityIntent.putExtras(newActivityInfo);
startActivity(activityIntent);
- Java trong Activity mới
Intent intent = getIntent();
Bundle info = intent.getExtras();
if (info != null) { /* Retrieve vals with info.getBlah(...)*/}
Đặt dữ liệu vào đóng gói Bundle: các kiểu dữ liệu có thể đưa vào
đóng gói như sau: putBoolean, putBooleanArray, putDouble,
putDoubleArray, putString, putStringArray, putStringArrayList, etc. Tất
287
cả dữ liệu đưa vào đóng gói phải là dạng các đối số: key (từ khóa) và
value (giá trị). Các giá trị phải là một trong các loại tiêu chuẩn (int,
double,..) hoặc mảng của chúng.
Nhận dữ liệu từ đóng gói Bundle: getBoolean, getBooleanArray,
getDouble, getDoubleArray, getString, getStringArray,
getStringArrayList, etc. Chúng nhận lấy các key (dạng các String) như
những đối số.
Để hiểu rõ ta xem một ví dụ như sau:
Ta có một Activity cho nhập chiều cao, cân nặng vào. Sau đó, nó sẽ
tính toán chỉ số BMI và đưa ra kết luận về tình trạng của bạn trên một
Activity khác.

Hình 10.9. Giao diện ứng dụng kiểm tra BMI


Các bước thực hiện như sau:
Bước 1: Tạo ứng dụng Android.
Bước 2: Thiết kế Layout cho Main Activity như yêu cầu gồm: ba
TextView, hai EditText, một Button. Chỉnh file
res/layout/activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"

288
android:id="@+id/RelativeLayout1"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginTop="38dp"
android:text="@string/title"
android:textAppearance="?android:attr/textAppearanceMedium"/>

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="34dp"
android:text="@string/cannang"
android:textAppearance="?android:attr/textAppearanceMedium"/>

289
<EditText
android:id="@+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/textView2"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/textView2"
android:inputType="numberDecimal"
android:ems="10"></EditText>

<requestFocus/>

<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView2"
android:layout_marginTop="28dp"
android:text="@string/chieucao"
android:textAppearance="?android:attr/textAppearanceMedium"
/>

<EditText
android:id="@+id/editText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/textView3"

290
android:layout_alignRight="@+id/editText1"
android:layout_toRightOf="@+id/textView3"
android:inputType="numberDecimal"
android:ems="10"/>

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editText2"
android:layout_below="@+id/editText2"
android:layout_marginTop="49dp"
android:text="@string/tieptuc_btn"/>

</RelativeLayout>

Bước 3: Tạo một Activity mới tên là Ketqua, nên tạo bằng Wizard.
Và ta thiết kế layout theo yêu cầu gồm một TextView và một Button.
Chỉnh file res\layout\activity_ketqua.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".KetquaActivity">

291
<TextView
android:id="@+id/thongbao"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="48dp"
android:text="@string/txt_ketqua"
android:textAppearance="?android:attr/textAppearanceMedium"/>

<Button
android:id="@+id/btnOk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/thongbao"
android:layout_below="@+id/thongbao"
android:layout_marginRight="26dp"
android:layout_marginTop="68dp"
android:text="@string/btn_ketqua"/>

</RelativeLayout>

Bước 4: Định nghĩa các hằng số trong file res\value\string.xml như


bên dưới. Để tiện thiết kế layout thì ngay khi ta thiết kế layout cho
MainActivity ta đã định nghĩa các hằng số cho file String.xml rồi. Sau đó
khi ta tạo Activity mới ta lại định nghĩa thêm. Ở đây, người viết gộp
chung cả hai lại nên có chút khó khăn cho bạn đọc
<?xmlversion="1.0"encoding="utf-8"?>
<resources>
<stringname="app_name">ChiSoChieuCao</string>

292
<stringname="action_settings">Settings</string>
<stringname="chieucao">Chiều cao (m)</string>
<stringname="cannang">Cân nặng(kg)</string>
<stringname="title">Hãy nhập vào chiều cao, cân nặng của
bạn</string>
<stringname="tieptuc_btn">Tiếp tục</string>
<stringname="title_activity_ketqua">KetquaActivity</string>
<stringname="hello_world">Hello world!</string>
<stringname="txt_ketqua">Chỉ số BMI của bạn là: </string>
<stringname="btn_ketqua">OK</string>

</resources>
Đến đây ta có giao diện của hai Activity như sau:

Hình 10.10. Giao diện hai Activity sau khi ta thiết kế xong phần Layout
Bước 5: Khai báo Activity ketqua và Intent trong file
res\AndroidManifest.xml như sau:
<?xmlversion="1.0"encoding="utf-8"?>

293
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="nguyenvanhiep.fivemen"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="nguyenvanhiep.fivemen.MainActivity"
android:label="@string/app_name">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
//ACTIVITY KET QUA
<activity
android:name="nguyenvanhiep.fivemen.KetquaActivity"
android:label="@string/title_activity_ketqua">

294
<intent-filter>
<actionandroid:name="nguyenvanhiep.fivemen.KETQUAACTIVIT
Y"/>

<categoryandroid:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>

</manifest>

Bước 6: Viết code cho MainActivity. Chỉnh sửa file


scr\nguyenvanhiep.fivemen\ MainActivity.java như sau (Chú ý các dòng
chú thích).
package nguyenvanhiep.fivemen;
//khai bao thu vien
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {


//khai bao cac Button va EditText se su dung
Button tieptuc;
EditText chieucao,cannang;

295
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//Tìm các Button va EditText

tieptuc=(Button) findViewById(R.id.button1) ;
chieucao=(EditText)findViewById(R.id.editText2);
cannang=(EditText)findViewById(R.id.editText1);

//Thiết lập các phương thức xử lý sự kiện click tren Button


//tieptuc
tieptuc.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub

//khai báo hai biến để đọc nội dung từ hai EditText tương ứng
Double nang
=Double.parseDouble(cannang.getText().toString());
Double cao=Double.parseDouble(chieucao.getText().toString());

//Khai báo Intent mới liên kết Activity Ketqua


Intent atvtkq=
newIntent("nguyenvanhiep.fivemen.KETQUAACTIVITY");
//khai báo Bundle(đóng gói) mới tên là thongso
Bundle thongso=new Bundle();

296
//đưa các dữ liệu riêng lẻ vào đóng gói dulieu theo nguyên tắc
// “key”, value.Ta phải nhớ các key này để bên Activity nhận dữ
//liệu nhận đúng dữ liệu
thongso.putDouble("nangnumber",nang);
thongso.putDouble("caonumber",cao);

//Đưa Bundle vào Intent với tên là dulieugoi


atvtkq.putExtra("dulieugoi",thongso);

//mở Activity Ketqua và truyền dữ liệu qua


startActivity(atvtkq);

}
});

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;

}
}
Bước 7: Viết code cho KetquaActivity. Chỉnh sửa file
scr\nguyenvanhiep.fivemen\ KetquaActivity.java như sau (Chú ý các
dòng chú thích).
package nguyenvanhiep.fivemen;

297
//Khai báo thư viện
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.view.Window;
import android.content.Intent;
import android.widget.Button;
import android.widget.TextView;

publicclass KetquaActivity extends Activity {


//khai báo các biến sử dụng
Button btn_OK;
TextView txt_thongbao;
Double BMI;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ẩn title Activity ket qua
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_ketqua);
//Tìm Button và TextView
btn_OK=(Button)findViewById(R.id.btnOk);
txt_thongbao=(TextView)findViewById(R.id.thongbao);

//lấy Intent gọi Activity này và đặt tên là caller


Intent caller=getIntent();
//Tạo một đóng gói Bundle mới tên là dulieunhan và gán
dulieunhan bằng dulieugoi

298
Bundle dulieunhan=caller.getBundleExtra("dulieugoi");
// có Bundle dulieunhan rồi ta lấy các thông số chiều cao, cân nặng
dựa vào các key “cao”, “nang”

Double chieucao=dulieunhan.getDouble("caonumber");
Double cannang=dulieunhan.getDouble("nangnumber");
//Tính chỉ số BMI theo tổ chức Y tế thế giới
BMI=cannang/(chieucao*chieucao);
//Làm tròn và chỉ lấy hai số lẻ
BMI= (double) Math.round(BMI*100)/100;
//Khai báo biến chuỗi kq và tính toán chuỗi kết quả theo các nghiên
cứu bạn tìm được

String kq;

if(BMI<16) {kq="Bạn thuộc dạng đại gia có nghĩa là da


bọc xương đấy";}
elseif(BMI<17) {kq="Bạn suy dinh dưỡng cấp độ 2 rồi, ăn
như hạm vào!";}
elseif(BMI<18.5) {kq="Bạn suy dinh dưỡng rồi á, đề nghị
ăn bồi bổ nhiều nhiều";}
elseif(BMI<24.9) {kq="Chiều cao và cân nặng bình
thường, đề nghị duy trì";}
elseif(BMI<29) {kq="Bạn hơi bị nhiều ký, bắt đầu béo
phì, giảm cân gấp";}
elseif(BMI<35) {kq="Mức độ béo phì của bạn bắt đầu
nghiêm trọng, quyết liệt giảm cân hơn nữa";}
else kq="Bạn có thể lăn rồi, hết phương cứu chữa";
//Khai báo bộ đệm chuỗi tên là tket để buột các thông tin cần hiển
thị vào
StringBuffer tket=new StringBuffer();

299
tket.append("Chỉ số BMI của bạn là:
").append(BMI).append("\n").append(kq);

//hiển thị thông tin ra TextView


txt_thongbao.setText(tket);

//Xử lý sự kiện khi nhấn nút OK


btn_OK.setOnClickListener(new View.OnClickListener()
{

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
finish(); //kết thúc Activity Ketqua
}
});
}

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.ketqua, menu);
returntrue;
}

}
Chạy chương trình và nhập kết quả chiều cao, cân nặng sẽ có kết
quả như sau:

300
Hình 10.11. Giao diện chương trình tính chỉ số BMI khi chạy

301
302
Chương 11
DỊCH VỤ XÁC ĐịNH VỊ TRÍ

Chương này chúng ta sẽ cùng tìm hiểu về cách thiết kế ứng dụng để
hiển thị bản đồ và xác định vị trí của một hoặc nhiều đối tượng trên bản
đồ dựa vào dịch vụ Google Maps. Sử dụng dịch vụ Google Maps giúp
ích cho chúng ta rất nhiều trong việc xây dựng các ứng dụng tìm kiếm
địa chỉ, xác định vị trí đối tượng hoặc tìm đường đi. Ngoài ra, một trong
những ứng dụng rất phổ biến đối với lĩnh vực điện tử đó là điều khiển và
giám sát đối tượng thông qua vị trí của đối tượng. Dựa vào tọa độ nhận
được từ thiết bị chuyên dụng gắn trên đối tượng di chuyển truyền về
chúng ta có thể biết chính xác vị trí của đối tượng trên bản đồ và tốc độ
di chuyển của đối tượng dựa vào đó có thể đưa ra các lệnh điều khiển
phù hợp.

11.1. HIỂN THỊ BẢN ĐỒ LÊN ỨNG DỤNG


Google Maps là một trong nhiều dịch vụ mà Google thiết kế sẵn và
phát hành cho nhiều nền tảng hệ điều hành di động. Do đó, chúng ta có
thể dễ dàng tích hợp Google Maps vào các ứng dụng chạy trên hệ điều
hành Android. Để hiểu rõ hơn cách lập trình đưa Google Maps lên ứng
dụng, chúng ta sẽ cùng làm ví dụ đầu tiên sau đây:
Ví dụ1: Thiết kế ứng dụng hiển thị bản đồ Google Maps.
Để ứng dụng hiển thị bản đồ Google Maps chúng ta có thể thực
hiện theo các bước sau:
- Tải thư viện Google Play services từ máy chủ Google (đã bao
gồm cả Google Map API).
- Import thư viện Google Play services vào Workspace.
- Đăng ký nhận Key từ Google để chèn vào ứng dụng.
- Tạo Project và lập trình để hiển thị bản đồ.
11.1.1. Tải thư viện Google Play services
Có nhiều cách để tải thư viện Google Play services, bạn có thể tải
xuống trực tiếp các bản được nén lại trên mạng Internet và giải nén
chúng vào thư mục “sdk\extras\google”, Eclipse sẽ tự động cập nhật thư
viện. Ngoài ra, còn một cách đơn giản hơn đó là sử dụng trình quản lý
Android SDK Manager đi kèm với bộ SDK, chỉ với một vài thao tác
303
đơn giả chúng ta có thể tải xuống các thư viện, các nhân máy ảo, các bản
cập nhật SDK,… thông qua trình quản lý này.
Để tải thư viện Google Play services chúng ta làm như sau: mở
cửa sổ Android SDK Manager (chọn menu Window>>Android SDK
Manager). Tại cửa sổ này, chúng ta sẽ đánh dấu tick vào các mục muốn
tải xuống, trong đó quan trọng nhất là mục Google Play services để phục
vụ cho ví dụ chúng ta đang thực hiện.
Lưu ý:Nếu máy tính nào đã cài đặt Google Play Services thì tại cột
Status tương ứng sẽ hiện trạng thái Installed, lúc này bạn không cần
phải tải lại nữa và có thể bỏ qua bước này.

Hình 11.1. Cài đặt thư viện Google Play sevices


Sau khi đánh dấu tick vào mục Google Play services, bạn nhấn vào
nút Install một packages… (ở đây tôi chỉ chọn một mục nên nút nhấn sẽ
hiện Install một packages, nếu bạn chọn nhiều mục thì nút nhấn sẽ hiện
số mục mà bạn đã chọn). Lúc này, một cửa sổ mới hiện ra yêu cầu chấp
nhận các điều khoản mà Google đưa ra, chọn mục Accept License và
nhấn vào nút Install để bắt đầu quá trình tải xuống từ máy chủ Google và
cài đặt, tất cả sẽ được thực hiện một cách tự động.
304
Lưu ý: Nếu bạn muốn phát triển ứng dụng thành phiên bản thương
mại thì nên đọc kỹ những điều khoản này, vì khi bạn thương mại hóa ứng
dụng phải tuân thủ nghiêm ngặt một số điều khoản được đề ra.
11.1.2. Import thư viện Google Play services vào Workspace
Sau khi quá trình tải về và cài đặt hoàn tất, để có thể làm việc với
thư viện Google Play Services chúng ta cần Import chúng vào
Workspace. Chọn menu File >> Import…
Chọn mục Android >> Existing Android Code Into Workspace
và nhấn vào nút Next để qua bước tiếp theo.

Hình 11.2. Import thư viện Google Play sevices


Ở cửa sổ này, bạn sẽ chọn chính xác đường dẫn tới thư viện
Google Play Services vừa tải xuống ở bước trên, thư viện này nằm bên
trong thư mục skd, đường dẫn thường có dạng như sau:
…\sdk\extras\google\google_play_services\libproject\google-play-
services_lib (phần dấu … phía trước là tùy vào việc bạn lưu sdk ở đâu
trong ổ đĩa). Sau khi chọn xong đường dẫn, bạn phải chắc chắn dấu tick
305
phía trước thư viện google-play-services_lib đã được chọn, cuối cùng
chọn Finish để hoàn tất.
Khi thư viện đã được thêm vào (trên cửa sổ Package Explorer
xuất hiện một project có tên google-play-services_lib), bạn nhấn phải
chuột vào thư viện vừa thêm google-play-services_lib và chọn mục
Properties. Ở cửa sổ hiện ra, chọn mục Android và đánh dấu tick vào
mục Is Library và nhấn OK để hoàn tất
11.1.3. Đăng ký nhận Key từ Google để chèn vào ứng dụng
Để ứng dụng của bạn có thể truy cập máy chủ Google và tải xuống
các dữ liệu bản đồ để hiển thị cần phải thêm Maps Key vào ứng dụng.
Các Key này hoàn toàn miễn phí. Mục đích của việc thêm Key này để
giúp Google có thể theo dõi ứng dụng của chúng ta trong hệ thống của
Google như Google Play Store và theo dõi việc ứng dụng của chúng ta sử
dụng các nguồn tài nguyên khác của Google như Google Maps,… Theo
thông báo từ Google, Google Maps Android API v2 (chúng ta sẽ làm
việc với phiên bản này) sử dụng một hệ thống quản lý Key mới, do đó
các Key đã đăng ký từ Google Maps Android v1 sẽ không thể làm việc
với bản này. Chúng ta sẽ nhận Key này từ Google APIs Console bằng
cách cung cấp hai thành phần là mã SHA1 và Package Names của ứng
dụng. Key này sẽ được thêm vào AndroidMainfest.xml.
a. Lấy mã SHA1
Maps API Key được sinh ra dựa trên chứng nhận cho mỗi ứng
dụng, chứng nhận này thường được gọi là mã SHA1. Google sẽ sử dụng
mã SHA1 để xác định ứng dụng của bạn, để có mã SHA1 chúng ta tiến
hành như sau:
Mở cửa sổ Command Line, sau đó chuyển đường dẫn hiện hành
sang thư mục cài đặt Java. Đường dẫn thường là C:\Program
Files\Java\[Phiên bản Java]\bin
Lưu ý: Khi bạn cài bản Java khác thì đường dẫn sẽ khác. Để có
đường dẫn chính xác, bạn hãy tuy cập đến thư mục cài Java.

Hình 11.3. Chọn đường dẫn thư mục cài Java

306
Trên cửa sổ CMD, tại đường dẫn đến thư mục bin của Java, bạn
nhập vào đoạn mã sau:
keytool -list -v -keystore <your_keystore_name> -alias
androiddebugkey
Trong đó: <your_keystore_name> là đường dẫn của file
debug.keystore. Với những máy tính cài Window 7 đường dẫn mặc định
như sau: C:\Users\<username>\.android\debug.keystore. Với
<username>là tên người dùng trên máy tính.
Đối với Window XP và Window 8 hoặc một số phiên bản Window
khác cũng có thư mục tương tự chứa file debug.keystore. Đây là chứng
nhận mà Eclipse sử dụng để đánh dấu ứng dụng của bạn vì vậy nó chỉ có
thể chạy trên máy ảo Android hoặc các thiết bị debug.
Sau khi nhập mã lệnh với các thông tin được điền đầy đủ bạn nhấn
Enter để thực thi lệnh, một thông báo yêu cầu nhập vào password xuất
hiện, bạn nhập vào: “android” và nhấn Enter lần nữa để hoàn tất.
Chương trình sẽ trả về các thông tin liên quan nhưng bạn chỉ cần quan
tâm đến giá trị mã SHA1 để sử dụng cho việc nhận Key.

Hình 11.4. Lấy mã SHA1 bằng dòng lệnh trên CMD


Ngoài cách lấy mã SHA1 như đã trình bày ở trên, còn một cách
đơn giản hơn nhưng chỉ áp dụng trên IDE Eclipse. Cách thực hiện như
sau: chọn menu Window >> Preferences. Cửa sổ Preferences hiện ra,
chọn vào mục Android >> Buid như hình sau:

307
Hình 11.5. Lấy mã SHA1 từ Eclipse
Mục SHA1 fingerprint chứa mã chúng ta cần tìm. Nếu bạn nào đã
làm cách trên có thể kiểm chứng lại hai mã chúng ta nhận được là hoàn
toàn giống nhau.
b. Sử dụng mã SHA1 để nhận Key từ Google
Bạn vào trang web sau: https://code.google.com/apis/console
đăng nhập với tài khoản Google của bạn. Sau khi đăng nhập thành công,
ở trang web mới hiện ra bạn chọn vào nút Create Project để tạo Project
mới:

Hình 11.6. Tạo Project trênGoogle APIs Console


308
Tại cửa sổ Pop-up, bạn điền các thông tin cần thiết và tick vào các
mục như hình trên và nhấn Create để hoàn tất.
Cũng tại trang web đang làm việc, bạn chọn mục APIs & auth >>
APIs. Kéo xuống tìm đến mục Google Maps Android API v2 và nhấn
chọn nút OFF (hiện tại đang ở trạng thái OFF) để chuyển trạng thái của
nó, nhớ đánh dấu chọn vào mục đồng ý với các điều khoản của Google.
Nhấn vào nút Accept để chuyển qua trang tạo Key.
Nếu thành công mục Google Maps API v2 sẽ chuyển trạng thái
sang ON. Tiếp theo, chọn mục APIs & auth >> Credentials nhấn vào
nút Create new Key

Hình 11.7. Tạo Key trên Google APIs Console


Cửa sổ Create new Key hiện ra, bạn chọn vào nút Android Key
để nhận Key bản đồ lập trình cho các máy điện thoại chạy hệ điều hành
Android. Một cửa sổ Pop-up hiện ra yêu cầu chúng ta nhập vào mã
SHA1 và Package Name, cú pháp nhập như sau “SHA1;package”
Ví dụ:
“9C:0E:FA:45:9C:B5:87:F3:6E:85:03:FC:F6:AB:4F:F2:18:6B:56:
30;com.example.map_ex”.
Nhấn Creat để hoàn tất.

309
Hình 11.8. Nhập mã SHA1 và Package Name
Tại trang web chúng ta đang làm việc sẽ hiện Key cho chúng ta,
chúng nằm ở mục API Key.
Lưu ý: Mặc dù bạn có thể sử dụng mã SHA1 của debug.store để
nhận Key cho phép chạy ứng dụng trên máy ảo hoặc thiết bị debug (dùng
cho việc thử nghiệm và phát triển ứng dụng), nhưng nó không hợp lệ nếu
bạn triển khai ứng dụng của bạn thành file apk và đưa chúng lên Android
Market (hoặc phân phối ứng dụng bằng cách khác).
11.1.4. Lập trình hiển thị và điều khiển bản đồ.
Mọi chuẩn bị về Key, thư viện đã hoàn tất, chúng ta sẽ bắt đầu tạo
project. Mở Eclipse lên và tạo một project mới. Chọn menu File >> New
>> Android Application Project, cửa sổ khởi tạo project hiện ra, bạn
điền vào các mục như sau:

310
Hình 11.9. Khởi tạo Project Map_ex trên Eclipse
Lưu ý: Mục Package Name bạn phải điền giống Pakage Name đã
sử dụng để nhận Key.
Nhấn Next để chuyển qua các cửa sổ tiếp theo và thực hiện các
thao tác tạo Project như bình thường.
Sau khi khởi tạo xong, chúng ta cần thêm thư viện google-play-
services_lib vào ứng dụng để có thể sử dụng Google Maps API. Nhấn
phải chuột vào project Map_ex vừa tạo và chọn mục Properties, ở cửa
sổ hiện ra bạn chọn mục Android như hình dưới:

311
Hình 11.10. Thêm thư viên cho project Map_ex
Nhấn vào nút Add, tại cửa sổ mới hiện ra bạn chọn thư viện
google-play-services_lib, và nhấn OK để hoàn tất. Tiếp theo, chúng ta
sẽ tiến hành viết code cho layout, chương trình điều khiển và khai báo
một số quyền trong AndroidMainfest.
Đầu tiên, chúng ta sẽ xây dựng giao diện cho ứng dụng, với mục
đích là tạo ứng dụng cơ bản để hiển thị bản đồ, không điều khiển hay có
chức năng nào khác do đó trên Layout bạn chỉ chèn thêm một đoạn code
sau để hiển thị bản đồ:
<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>
312
Google Maps sử dụng Fragment để hiển thị bản đồ, bạn có thể coi
nó giống các đối tượng khác để dễ dàng tiếp cận và làm việc với nó. Nếu
ứng dụng có nhiều đối tượng trên layout, muốn hiển thị bản đồ ở vị trí
nào thì chỉ cần chèn thẻ fragment ở vị trí tương ứng. Như chúng ta đã
thảo luận ở những chương trước, mỗi đối tượng (View) trong Android
đều được kế thừa từ một class và Google Map cũng không ngoại lệ,
GoogleMap được kế thừa từ lớp SupportMapFragment.
Đây là code hoàn chỉnh của activity_main.xml:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>

</LinearLayout>
Khi sử dụng Google Map, ngoài những khai báo mặc định thường
có khi tạo ứng dụng trong AndroidMainfest chúng ta cần khai báo thêm
một số thông tin khác như quyền (permission) cho phép ứng dụng truy
cập các tính năng của điện thoại, khai báo Map Key, phiên bản
GoogleMap sử dụng,…
313
Mỗi quyền trong Andorid được xác định bằng một chuỗi, cấu trúc
chung như sau:
<uses-permissionandroid:name="permission_name"/>
Ví dụ một số quyền được mô tả sẵn bởi Android:
android.permission.INTERNET
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.ACCESS_NETWORK_STATE
Nếu ứng dụng của bạn cần truy xuất vào một tính năng được hệ
điều hành Android bảo vệ bằng quyền truy xuất, bạn phải mô tả quyền
cần truy xuất trong AndroidMainfest bằng thẻ <uses-permission>. Khi
ứng dụng được cài đặt vào thiết bị, trong quá trình cài đặt sẽ có yêu cầu
người sử dụng xác nhận các quyền đã đăng ký trong AndroidMainfest,
nếu người dùng đồng ý thì ứng dụng sẽ được phép cài vào điện thoại.
Nếu đã được chấp nhận các quyền truy xuất lúc cài đặt, khi thực thi ứng
dụng có thể truy cập đến các tính năng được bảo vệ mà có thể không cần
thông báo đến người sử dụng. Trong ứng dụng này để sử dụng dịch vụ
Google Maps chúng ta cần sử dụng các quyền truy xuất sau:
<uses-
permissionandroid:name="android.permission.ACCESS_NETWORK_ST
ATE"/>
ACCESS_NETWORK_STATE cho phép ứng dụng có thể kiểm
tra trạng thái kết nối Internet để quyết định tải dữ liệu bản đồ từ máy chủ
về bằng cách nào.
<uses-
permissionandroid:name="android.permission.INTERNET"/>
INTERNET cho phép ứng dụng có thể kết nối Internet và tải dữ
liệu bản đồ từ máy chủ GooleMap.
Lưu ý: Một số ứng dụng tra cứu bản đồ đã tải xuống (hoặc tự thiết
kế) dữ liệu bản đồ và tích hợp chúng vào ứng dụng. Do đó, chúng cho
phép bạn xem bản đồ mà không cần kết nối mạng. Tuy nhiên, ứng dụng
chúng ta đang xây dựng ở đây không tích hợp bản đồ nên muốn hiển thị
được bản đồ, yêu cầu thiết bị phải có kết nối Internet để tải về dữ liệu bản
đồ từ máy chủ của Google.
<uses-
permissionandroid:name="android.permission.WRITE_EXTERNAL_STO
RAGE"/>

314
WRITE_EXTERNAL_STORAGE cho phép ứng dụng có thể lưu
dữ liệu bản đồ lên các thiết bị có hỗ trợ bộ nhớ ngoài.
Những quyền mà tôi đề cập dưới đây được Google khuyến cáo sử
dụng nhưng các bạn có thể bỏ qua nếu ứng dụng của bạn không sử dụng
đến vị trí hiện tại của thiết bị. Ở ví dụ này, tôi sẽ sử dụng các quyền này
để khi các bạn phát triển ứng dụng có thể sử dụng thêm chức năng GPS
của thiết bị.
<uses-
permissionandroid:name="android.permission.ACCESS_COARSE_LOC
ATION"/>
ACCESS_COARSE_LOCATION cho phép ứng dụng xác định vị trí
dạng thô dựa vào vị trí của trạm thu phát điện thoại hoặc vị trí WIFI.
<uses-
permissionandroid:name="android.permission.ACCESS_FINE_LOCATI
ON"/>
ACCESS_FINE_LOCATION cho phép ứng dụng truy cập vị trí
chính xác hơn dựa vào hệ thống định vị toàn cần GPS, để có thể dùng
tính năng này yêu cầu điện thoại phải hỗ trợ GPS và người sử dụng phải
mở tính năng GPS.
Google Maps Android API sử dụng thư viện OpenGL ES v2 để xây
dựng bản đồ. Nếu OpenGL ES không được cài đặt thì ứng dụng của bạn
sẽ không thể hiển thị bản đồ. Do đó, bạn thêm đoạn code sau vào
AndroidMainfest
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
Tiếp theo, chúng ta sẽ chèn Maps Key vào AndroidMainfest. Tại
đây, Maps API sẽ đọc giá trị Key này và đưa chúng lên máy chủ Google
Maps, nếu được xác nhận thì ứng dụng của bạn có thể truy cập dữ liệu
của Google Maps. Chúng ta sẽ chèn Key bằng đoạn code sau:
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="AIzaSyDDAnSwtcW1Tv4KOsQNWWjYb44bZ7Ar
JxY"/>
Cuối cùng là khai báo phiên bản Google Play services mà chúng ta
dùng để biên dịch ứng dụng, code khai báo như sau:
315
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
Dưới đây là code AndroidManifest.xml hoàn chỉnh:
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="com.example.map_ex"
android:versionCode="1"
android:versionName="1.0">
<!-- Copied from Google Maps Library/AndroidManifest.xml. -->
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="17"/>
<uses-
permissionandroid:name="android.permission.ACCESS_NETWOR
K_STATE"/>
<uses-
permissionandroid:name="android.permission.INTERNET"/>
<!-- External storage for caching. -->
<uses-
permissionandroid:name="android.permission.WRITE_EXTERNA
L_STORAGE"/>
<!-- My Location -->
<uses-
permissionandroid:name="android.permission.ACCESS_COARSE
_LOCATION"/>
<uses-
permissionandroid:name="android.permission.ACCESS_FINE_LO
CATION"/>
<!-- Maps API needs OpenGL ES 2.0. -->
<uses-feature

316
android:glEsVersion="0x00020000"
android:required="true"/>
<!-- End of copy. -->
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="AIzaSyDDAnSwtcW1Tv4KOsQNWWjYb44bZ7ArJx
Y"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/>
<activity
android:name="com.example.map_ex.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppBaseTheme">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>
<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Như đã đề cập ở trên Google Map sử dụng Fragment do đó để hiển
thị cũng như điều khiển Google Map thì class MainActivity phải kế thừa
từ class FragmentActivity. Chúng ta phải chỉnh sửa lại file MainActivity
một chút ở phần lớp kế thừa.
Code hoàn chỉnh MainActivity.java phải sửa lại như sau:

317
package com.example.map_ex;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
publicclass MainActivity extends FragmentActivity {

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Tới đây bạn đã hoàn tất ví dụ trên, chúng ta sẽ cùng Build ứng
dụng và chạy trên máy ảo hoặc thiết bị debug để kiểm tra thành quả đã
thực hiện. Nếu bạn làm đúng như các bước đã hướng dẫn ở trên thì khi
chạy ứng dụng sẽ hiển thị bản đồ trên toàn màn hình điện thoại. Nếu
không hiển thị bản đồ, bạn nên kiểm tra lại thiết bị có kết nối Internet
không, kiểm tra lại Key các bạn dùng có đúng không. Có thể thay đổi vị
trí hiển thị của bản đồ bằng cách kéo thả bản đồ, sử dụng các nút nhấn
phóng to thu nhỏ trên màn hình để xem chi tiết một khu vực nào đó.
Đây là kết quả hiển thị trên máy ảo:

318
Hình 11.11. Kết quả ví dụ 1 “Hiển thị bản đồ”

11.2. ĐIỀU KHIỂN BẢN ĐỒ


Ở ví dụ trước ta thấy bản đổ hiển thị ở chế độ mặc định là kiểu bản
đồ giao thông. Tuy nhiên, trong một số trường hợp chúng ta cần hiển thị
bản đồ ở các chế độ khác như chế độ vệ tinh, kết hợp cả giao thông và vệ
tinh,… Do đó, chúng ta sẽ mở rộng ứng dụng làm ở ví dụ trên để cho
phép người sử dụng có thể thay đổi chế độ hiển thị của bản đồ.
319
Để dễ dàng thao tác trong việc thay đổi chế độ hiển thị chúng ta sẽ
tạo thêm bốn nút nhấn, có chức năng lựa chọn chế độ hiển thị của bản đồ
gồm: chế độ giao thông (Normal), chế độ lai giữa giao thông và vệ tinh
(Hybrid), chế độ vệ tinh (Satellite) và chế độ địa hình (Terain). Chúng ta
sẽ chỉnh sửa lại giao diện và viết code điều khiển như sau:
Code activity_main.xml sau khi thêm nút nhấn:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">

<Button
android:id="@+id/btNor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" Normal "/>

320
<Button
android:id="@+id/btHyd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" Hybrid "/>

</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">

<Button
android:id="@+id/btSat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Satellite"/>

<Button
android:id="@+id/btTer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" Terrain"/>

</LinearLayout>

<fragment
android:id="@+id/map"

321
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>

</LinearLayout>
Để điểu khiển được bản đồ, chúng ta cần tạo một đối tượng có kiểu
GoogleMap và dùng đối tượng này điều khiển bản đồ. Nhưng trước tiên,
chúng ta phải xác định đối tượng điều khiển trên layout bằng cách sử
dụng phương thức findFragmentById:
private GoogleMap mMap = null;
Gán biến mMap vào bản đồ trên layout để điều khiển.
if (mMap == null) {
// Try to obtain the map from the SupportMapFragment.
mMap = ((SupportMapFragment)
getSupportFragmentManager()

.findFragmentById(R.id.map)).getMap();
}
Khi có được đối tượng điều khiển bản đồ, để thay đổi chế độ hiển
thị trên bản đồ chúng ta sẽ viết code cho các sự kiện chạm vào nút chọn
chế độ, sử dụng phương thức setMaptype của GoogleMap để thay đổi
chế độ hiển thị của bản đồ:
mMap.setMapType(MAP_TYPE_NORMAL);
Tương tự đối với các chế độ khác, bạn chỉ cần thay hằng số
MAP_TYPE_NORMAL bằng hằng số tương ứng với chế độ đó.
Lưu ý: Các hằng số chế độ hiển thị bản đồ MAP_TYPE_NORMAL,
MAP_TYPE_NORMAL, MAP_TYPE_SATELLITE,
MAP_TYPE_TERRAIN đã được định nghĩa sẵn trong thư viện
GoogleMap, chúng ta chỉ cần khai báo chúng ở đầu chương trình là có
thể sử dụng.
Code hoàn chỉnh MainActivity.java thêm phần xử lý thay đổi chế
độ hiển thị:
package com.example.map_ex;

322
importstatic
com.google.android.gms.maps.GoogleMap.MAP_TYPE_HYBRID;
importstatic
com.google.android.gms.maps.GoogleMap.MAP_TYPE_NORMAL
;
importstatic
com.google.android.gms.maps.GoogleMap.MAP_TYPE_SATELLI
TE;
importstatic
com.google.android.gms.maps.GoogleMap.MAP_TYPE_TERRAIN
;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.Button;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

publicclass MainActivity extends FragmentActivity {


private GoogleMap mMap = null;
private Button btNor, btHyd, btSat, btTer;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btNor = (Button) findViewById(R.id.btNor);

323
btHyd = (Button) findViewById(R.id.btHyd);
btSat = (Button) findViewById(R.id.btSat);
btTer = (Button) findViewById(R.id.btTer);

if (mMap == null) {
// Try to obtain the map from the
SupportMapFragment.
mMap = ((SupportMapFragment)
getSupportFragmentManager()

.findFragmentById(R.id.map)).getMap();
}

btNor.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub

mMap.setMapType(MAP_TYPE_NORMAL);
}
});
btHyd.setOnClickListener(new View.OnClickListener() {

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub

mMap.setMapType(MAP_TYPE_HYBRID);
}
});
324
btSat.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub

mMap.setMapType(MAP_TYPE_SATELLITE);
}
});
btTer.setOnClickListener(new View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub

mMap.setMapType(MAP_TYPE_TERRAIN);
}
});
}
}
Biên dịch ứng dụng và chạy trên thiết bị sẽ nhận được kết quả
như sau:

325
Hình 11.12. Kết quả hiển thị bản đồ địa hình
Khi chạm vào các nút thay đổi chế độ hiển thị, ngay lập tức bản đồ
sẽ thay đổi chế độ hiển thị theo chế độ bạn đã chọn.
Tới đây, bạn đã biết cách để hiển thị bản đồ lên ứng dụng, thay đổi
chế độ hiển thị của bản đồ cho phù hợp với mục đích sử dụng. Ở ví dụ
tiếp theo này chúng ta sẽ tạo các marker (điểm đánh dấu) trên bản đồ,
dựa vào kinh độ và vĩ độ được nhập từ người sử dụng. Ví dụ này sẽ là
bước đệm để các bạn có thể phát triển các ứng dụng liên quan để giám
sát đối tượng dựa vào tọa độ của đối tượng được gửi về, từ đó hiển thị vị
trí đối tượng lên bản đồ.
326
Ví dụ 2: Từ ứng dụng đã thiết kế ở ví dụ 1, thay đổi giao diện ứng
dụng với hai khung nhập tọa đố, một nút xác nhận và viết code tạo
marker từ tọa độ được nhập ở hai ô nhập dữ liệu.
Để đơn giản, chúng ta sẽ sửa lại giao diện của ứng dụng ở ví dụ
trước, bỏ các nút thay đổi chế độ hiển thị của bản đồ, thay vào đó sẽ thêm
vào hai ô để nhập tọa độ marker trên bản đồ và một nút nhấn để xác nhận
vị trí và thực hiện tạo marker.
Với sự thay đổi giao diện như đã miêu tả ở trên, code
activity_main.xml sẽ được sửa như sau:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<EditText
android:id="@+id/edLongitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào kinh độ"
android:numeric="decimal"/>

<requestFocus/>
<EditText
327
android:id="@+id/edLatitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Nhập vào vĩ độ"
android:numeric="decimal">
</EditText>

<Button
android:id="@+id/btOK"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="OK"/>

<fragment
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"/>

</LinearLayout>
Tiếp theo chúng ta sẽ viết code cho nút OK để tạo marker trên bản
đồ. GoogleMap cung cấp cho chúng ta phương thức addMarker để thêm
marker lên bản đồ. Trong phương thức này, chúng ta cần truyền vào tham
số là tọa độ của marker trên bản đồ. Tọa độ này gồm hai phần: vĩ độ và
kinh độ. Các tọa độ này sẽ giúp ta xác định được vị trí chính xác trên bản
đồ của Google, tọa độ này có được dựa vào việc tính toán các dữ liệu
nhận được từ các vệ tinh GPS. Ngoài ra, dựa vào bản đồ có sẵn của
Google các bạn chọn đến vị trí của một điểm bất kỳ cũng sẽ nhận được
tọa độ của điểm đó.
Code hoàn chỉnh MainActivity.java:

328
package com.example.map_ex;

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

publicclass MainActivity extends FragmentActivity {


private GoogleMap mMap = null;
private Button btOK;
private EditText edLongitude, edLatitude;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btOK = (Button) findViewById(R.id.btOK);
edLongitude = (EditText)
findViewById(R.id.edLongitude);
edLatitude = (EditText) findViewById(R.id.edLatitude);
if (mMap == null) {
// Try to obtain the map from the
SupportMapFragment.
mMap = ((SupportMapFragment)
getSupportFragmentManager()
329
.findFragmentById(R.id.map)).getMap();
}
// 10.827132, 106.635311
btOK.setOnClickListener(new View.OnClickListener() {
@Override
Publicvoid onClick(View arg0) {
// TODO Auto-generated method stub
LatLng marker = new
LatLng(Double.valueOf(edLatitude.getText().toString()),
Double.parseDouble(edLongitude.getText().toString()));
mMap.clear();
mMap.addMarker(new
MarkerOptions().position(marker).title("Marker cua minh"));

mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mar
ker, 7));
}
});
}
}
Ở code trên bạn cần lưu ý đến sự kiện setOnClickListener của nút
nhấn OK, khi xảy ra sự kiện này, ứng dụng sẽ chuyển tọa độ nhập trên hai
ô editText thành kiểu số thực (double) và lưu vào một biến toạn độ
marker. LatLng là một kiểu được cung cấp sẵn trong thư viện của Google
Play services, khi muốn dùng biến để chứa tọa độ điểm tạo marker, chúng
ta sẽ tạo một biến có kiểu LatLng và gán tọa độ vào biến.
Trong phương thức addMarker chúng ta cần truyền vào tham số là
MarkerOption. MarkerOption cho phép chúng ta mô tả chi tiết marker
muốn thêm vào bản đồ như: tọa độ của marker (position), tên marker
(title), biểu tượng marker (icon),… Tại đối số vị trí của marker chúng ta
sẽ đặt biến chứa tọa độ đã tạo ở trên vào. Ngoài ra, bạn có thể thêm một
editText để người dùng nhập tên cho marker, và gán giá trị của editText
này cho đối số title.
Vẫn còn rất nhiều thuộc tính của MarkerOption mà tôi không đề
cập ở đây, khi bắt tay vào thực hành các bạn hay cố gắng sử dụng hết các
thuộc tính để có cái nhìn tổng quát hơn về việc tạo marker trên bản đồ.
330
Khi tạo một marker trên bản đồ để màn hình hiển trị marker vừa
tạo trên bản đồ chúng ta cần phải sử dụng phương thức animateCamera
để thay đổi vị trí hiển thị của bản đồ trên màn hình điện thoại và di
chuyển đến vị trí đặt marker. Trong phương thức animateCamera chúng
ta sẽ truyền vào vị trí của marker muốn hiển thị (đây sẽ là vị trí tâm của
bản đồ được hiển thị) và độ phóng đại (zoom).
Đây là kết quả hiển thị trên thiết bị mô phỏng khi nhập vào tọa độ
của Thành phố Hồ Chí Minh (106.635311,10.827132).

Hình 11.13. Kết quả tạo marker tại Thành phố Hồ Chí Minh

331
11.3. TÌM HIỂU GOOGLEMAP TỪ VÍ DỤ CỦA GOOGLE
Phần này tôi sẽ hướng dẫn các bạn tạo project demo từ code mẫu của
Google để tham khảo. Có khá nhiều code mẫu mà Google cung cấp cho
chúng ta, nhưng ở đây chúng ta chỉ quan tâm đến code mẫu phần bản đồ,
để giúp bạn củng cố những gì đã đọc ở trên đồng thời đây cũng là một
nguồn tài liệu hữu ích để các bạn tham khảo cách điều khiển bản đồ cũng
như học viết code một cách khoa học. Cũng giống như ví dụ ở trên mà các
bạn đã thực hiện, chúng ta phải Import thư viện Google Play services vào
WorkSpace để làm việc với chúng, nếu các bạn đã Import ở ví dụ trước thì
không cần phải làm lại thao tác này. Chúng ta cũng cần đăng ký một Key
cho project demo này, các bước thực hiện tương tự như đã hướng dẫn ở
trên các bạn sử dụng mã SHA1 đã tìm ở trên, tuy nhiên đặc biệt lưu ý bạn
phải thay Package Name thành com.example.mapdemo để phù hợp với
project demo này. Cuối cùng, chúng ta sẽ thực hiện các bước tạo Project từ
code mẫu có sẵn của Google.
Vào menu File >> New >> Other.

Hình 11.14. Tạo Project từ code mẫu của Google


332
Tại cửa sổ New, chọn mục Android Sample Project và nhấn Next
để qua bước kể tiếp. Ở cửa sổ chọn phiên bản Android để Build bạn chọn
phiên bản mới nhất mà bạn hiện có và nhấn OK để qua bước tiếp theo.

Hình 11.15. Chọn Code mẫu muốn tạo project


Tại đây có rất nhiều các code mẫu cho bạn lựa chọn, nếu muốn
tham khảo thêm các ứng dụng khác từ thư viện Google Play services các
bạn có thể chọn bất kỳ một mục nào để tạo project demo tham khảo. Ở
đây, chúng ta đang bàn về việc sử dụng bản đồ do đó chúng ta sẽ chọn
mục maps [Google Play services] và nhấn Finish để hoàn tất.
Chúng ta cần phải thêm thư viện Google Play services cho project
demo này giống như việc thêm thư viện cho các ví dụ đã thực hiện ở trên.
Khi Eclipse khởi tạo project demo, mặc định đã điền sẵn Key trong file
AndroidMainfest nhưng Key này không phù hợp nên ứng dụng demo này
333
sẽ không thể hiện bản đồ khi chạy trên máy ảo hoặc các thiết bị debug.
Chúng ta cần thay Key mặc định bằng Key mà chúng ta đã đăng ký với
Google. Sau khi đổi Key xong, bạn có thể biên dịch chương trình để xem
kết quả.
Trong project demo này Google cung cấp cho chúng ta rất nhiều
các ví dụ về chức năng của bản đồ Google Maps, bạn có thể chọn vào
từng mục để tìm hiểu thêm các chức năng này và quay lại phần code để
tìm hiểu các phương thức, thuộc tính và cách thực hiện khi muốn sử dụng
một chức năng nào đó. Khi đã nắm rõ chức năng và cách sử dụng bạn có
thể áp dụng vào ứng dụng của mình để tăng thêm các tính năng và tính
chuyên nghiệp của ứng dụng.

Hình 11.16. Kết quả project demo

334
Chương 12
LƯU DỮ LIỆU

Ở chương này, chúng ta sẽ cùng thảo luận về một phần quan trọng
trong lập trình, chúng giúp ứng dụng của bạn chuyên nghiệp hơn, phù
hợp với thực tế hơn. Ở những chương trước, chúng ta đã cùng thảo luận
về các đối tượng cơ bản, lập trình ứng dụng với các đối tượng cơ bản
này, và trong các ứng dụng đó chúng ta cho phép người dùng thay đổi
các thông số sao cho phù hợp với mong muốn của từng người (thay đổi
kích cỡ chữ, font chữ, dữ liệu lưu trong ListView, hình nền ứng dụng,
cập nhật vị trí hiện tại trên bản đồ Google Map,…). Tuy nhiên, các thay
đổi này chỉ có tác dụng khi ứng dụng đang hoạt động, còn khi chúng bị
tắt ở lần chạy lại kế tiếp các thay đổi ở lần trước không có tác dụng,
chúng bị đặt về những giá trị mặc định ban đầu do lập trình viên thiết đặt.
Vì vậy, bài toán đặt ra cho chúng ta là phải lưu các dữ liệu cần thiết lên
bộ nhớ (Bộ nhớ không bay hơi: bộ nhớ trong và bộ nhớ ngoài), và khôi
phục các dữ liệu này ở lần chạy kế tiếp của ứng dụng.
Hệ điều hành Android cung cấp cho chúng ta nhiều kiểu lưu dữ liệu
khác nhau để phù hợp với mục đích sử dụng của từng ứng dụng. Chương
này chúng ta sẽ cùng thảo luận về các kiểu lưu trữ đó.

12.1. ĐỌC DỮ LIỆU TĨNH TỪ RESOURCE


Trọng tâm chính của chương này là giúp các bạn có thể lưu dữ liệu
lên bộ nhớ, tuy nhiên tôi cũng muốn giới thiệu với các bạn cách đọc dữ
liệu tĩnh được để lưu ở phần Resource của ứng dụng. Việc đọc dữ liệu
tĩnh đặc biệt hữu ích với các ứng dụng có nhiều dữ liệu cố định (giới
thiệu ứng dụng, hướng dẫn sử dụng, tài liệu tra cứu, lời bài hát-ứng dụng
Karaoke,…), chúng được tạo sẵn khi bạn lập trình và khi người dùng gọi
đến chúng sẽ được hiển thị. Để hiểu rõ hơn cách đọc dữ liệu tĩnh chúng
ta sẽ cùng thực hiện ví dụ sau:
Ví dụ 1: Thiết kế ứng dụng hiển thị lời bài hát.
Ví dụ này sẽ giúp các bạn hiểu hơn về các bước để đọc dữ liệu tĩnh
(lời bài hát) và hiển thị chúng lên màn hình. Ví dụ này cũng là một phần
nhỏ hỗ trợ các bạn có thể lập trình ứng dụng karaoke để hiển thị lời bài hát.
Đầu tiên, chúng ta sẽ tạo một Project mới với các thông số điền vào
như sau:

335
Hình 12.1. Khởi tạo Project Ví dụ 1
Chúng ta cần chuẩn bị trước lời bài hát sau đó tạo các tập tin chứa
lời bài hát và đặt chúng vào thư mục res/drawable của ứng dụng (Nếu
đã có thư mục drawable rồi thì không cần phải tạo thêm, trường hợp chưa
có thì bạn phải tạo mới thư mục này). Để tạo các tập tin chứa lời bài hát
khá đơn giản, ở đây tôi dùng Notepad có sẵn trong Window để tạo. Các
bạn lưu ý: đối với nội dung bằng Tiếng Việt có dấu, các bạn cần lưu lại
tập tin với Encoding chọn là UTF-8.
Sau khi tạo thư mục và tạo tập tin chứa lời bài hát, chúng ta sẽ
thêm các tập tin lời bài hát vào thư mục bằng cách kéo các tập tin và đặt
vào thư mục res/drawable, Eclipse sẽ tự động nhận biết và hiện thông
báo yêu cầu chúng ta xác nhận: sao chép tập tin vào thư mục
res/drawable hay chỉ link đến tập tin.

336
Hình 12.2. Copy files vào ứng dụng
Chúng ta sẽ chọn mục Copy files và nhấn OK để hoàn tất. Dưới
đây là kết quả khi bạn thực hiện đầy đủ các bước trên:

Hình 12.3. Cây thư mục res/lyric_data


Tiếp theo, chúng ta sẽ thiết kế phần giao diện cho ứng dụng, với
yêu cầu như trên chúng ta cần các đối tượng sau:
- Một Button Hiển thị: khi người dùng nhấn vào Button này, lời
bài hát sẽ được hiển thị.
- Một EditText: để người dùng nhập tên bài hát muốn hiển thị lời
- Một EditText: dùng để hiển thị lời bài hát (ở đây tôi chọn
EditText mục đích là muốn mở rộng ứng dụng cho những ví dụ sau).

337
Đây là code file activity_main.xml:
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnHienThi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Read Data"/>
<EditText
android:id="@+id/edTenBaiHat"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="2"
android:ems="10"
android:gravity="top|left"
android:inputType="textMultiLine">
<requestFocus/>
</EditText>
</LinearLayout>

338
<EditText
android:id="@+id/edLoiBaiHat"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="2"
android:ems="10"
android:gravity="top|left"
android:scrollbars="vertical"
android:inputType="textMultiLine">
</EditText>
</LinearLayout>
Để hiển thị lời bài hát, chúng ta cần lập trình cho Button Hiển thị,
sao cho khi người dùng chạm vào nút này, nó sẽ tự động tìm trong thư
mục res/drawable tập tin có tên phù hợp với tên bài hát do người dùng
nhập ở ô edTenBaiHat và hiển thị nội dung lên edLoiBaiHat.
Code file MainActivity.java
package com.example.data_ex;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

339
publicclass MainActivity extends Activity {

private Button btnHienThi;


private EditText edTenBaiHat, edLoiBaihat;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnHienThi = (Button) findViewById(R.id.btnHienThi);
edTenBaiHat = (EditText)
findViewById(R.id.edTenBaiHat);
edLoiBaihat = (EditText)
findViewById(R.id.edLoiBaiHat);
btnHienThi.setOnClickListener(new
View.OnClickListener() {

@Override
Publicvoid onClick(View v) {
// TODO Auto-generated method stub
// Xu ly su kien nhan vao nut Hien Thi
ShowLyric();
}
});
}
Private void ShowLyric() {
InputStream lyric = null;
// Kiem tra neu dung ten bai hat thi doc file tu Resource
drawable
if
(edTenBaiHat.getText().toString().equalsIgnoreCase("untilyou")) {

340
lyric =
this.getResources().openRawResource(R.drawable.until_you);
} elseif (edTenBaiHat.getText().toString()
.equalsIgnoreCase("vichinhemthoi")) {
lyric = this.getResources().openRawResource(
R.drawable.vi_chinh_em_thoi);
} else {
Toast.makeText(MainActivity.this,
"Không tìm thấy lời bài hát này!!!",
Toast.LENGTH_SHORT).show();
}
if (lyric != null) {
InputStreamReader inStreamReader = new
InputStreamReader(lyric);
char[] inBuffer = newchar[100];
int charRead;
String data = "";
try {
while ((charRead =
inStreamReader.read(inBuffer)) > 0) {
String readData =
String.valueOf(inBuffer, 0, charRead);
data += readData;
}
lyric.close();
edLoiBaihat.setText(data.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
341
}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}
}
Các phần cơ bản trong code trên các bạn đã được tìm hiểu ở những
chương trước. Ở đây, chúng ta chỉ lưu ý đến code phục vụ đọc dữ liệu
tĩnh. Để có thể hiểu rõ hơn về thuật toán, ý nghĩa của các lớp cũng như
quy trình đọc, ghi dữ liệu các bạn cần tìm hiểu kỹ ngôn ngữ lập trình
Java, các luồng xử lý dữ liệu trong ngôn ngữ lập trình này. Ở đây, tôi chỉ
lưu ý các bạn những lớp và phương thức cần thiết để thực hiện việc đọc
dữ liệu:
Chúng ta khai báo đối tượng lyric là thể hiện của lớp InputStream
dùng để tạo luồng đọc dữ liệu. Ở code trên, chúng ta thực hiện kiểm tra
tên bài hát, nếu người dùng nhập đúng tên bài hát vào ô edTenBaiHat
(tên bài hát tương ứng với những bài hát đã lưu lời trong Resource) khi
đó phương thức getResources() và openRawResource() sẽ được gọi để
mở tập tin chứa lời bài hát tương ứng (tập tin này đã được chúng ta thêm
vào thư mục res/drawable).
Lưu ý: Ở phần này, chúng ta không chú trọng đến việc xây dựng
thuật toán so sánh và tìm kiếm lời bài hát, với cách so sánh dữ liệu trực
tiếp như chúng ta đã thực hiện ở trên thì bạn bắt buộc phải nhập chính
xác tên bài hát (chỉ cần dư hay thiếu một ký tự bất kỳ thì ứng dụng không
thể tìm được lời bài hát tương ứng). Hầu hết các ứng dụng có chức năng
tương tự đều xây dựng thuật toán tìm kiếm thông minh, hỗ trợ người
dùng từ việc nhập dữ liệu với các từ gợi ý và tìm kiếm tên gần chính xác
(cho phép người dùng nhập sai một vài ký tự). Nếu bạn muốn xây dựng
một ứng dụng Karaoke chuyên nghiệp, thì thuật toán tìm kiếm là một
phần rất quan trọng.
Đến đây, bạn đã lấy được InputStream các công việc xử lý còn lại
tương tự như khi làm việc trên Java thuần túy. Chúng ta sử dụng lớp
InputStreamReader để đọc dữ liệu từ InputStream. Khi đọc nội dung
của tập tin, chúng ta không biết kích thước tập tin, do đó chúng ta sẽ đọc
342
tập tin theo từng khối, mỗi khối là 100 ký tự (số 100 ở đây không phải là
mặc định, có thể chọn tùy ý nhưng nên dựa vào giải thuật xử lý để chọn
cho phù hợp). Phương thức read() để đọc dữ liệu và lưu vào mảng Byte,
đồng thời phương thức này cũng sẽ trả về số byte mà nó vừa đọc.
try {
while ((charRead =
inStreamReader.read(inBuffer)) > 0) {
String readData = String.valueOf(inBuffer,
0, charRead);
data += readData;
}
lyric.close();
edLoiBaihat.setText(data.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bạn cũng lưu ý dòng code: String readData =
String.valueOf(inBuffer, 0, charRead) dùng để chuyển mảng Byte
thành chuỗi ký tự, cho phép đặt vị trí bắt đầu và độ dài muốn chuyển. Ở
đây sử dụng câu lệnh này vì lần đọc cuối cùng, có thể số ký tự đọc được
ít hơn 100. Sau khi đọc xong tập tin (giá trị trả về của phương thức read()
bằng 0), chúng ta sẽ đóng luồng đọc lại và hiển thị nội dung đọc được lên
edLoiBaiHat.
Dưới đây là kết quả:

343
Hình 12.4. Kết quả ví dụ 1 đọc dữ liệu tĩnh
Từ kết quả trên ta thấy, để có thể hiển thị lời bài hát, bạn cần nhập
chính xác tên bài hát, theo như code đã lập trình chúng ta sẽ không nhập
khoảng trắng giữa các chữ trong tên bài hát. Đây là một nhược điểm của
ứng dụng này, để ứng dụng thực tế hơn, chúng ta cần xây dựng thuật toán
tìm kiếm thông minh hơn, hỗ trợ người dùng nhiều hơn và đây sẽ là bài
tập dành cho các bạn luyện tập tư duy lập trình.

12.2. ĐỌC VÀ GHI TẬP TIN


Phần trước chúng ta đã cùng tìm hiểu cách đọc dữ liệu từ các tập
tin tĩnh và hiển thị lên màn hình, các dữ liệu này được đưa vào ứng dụng
khi lập trình, những dữ liệu này là cố định không thể thay đổi nội dung.
Tuy nhiên, trong thực tế nhiều trường hợp ứng dụng của chúng ta cần xử
lý thông tin và lưu chúng vào bộ nhớ để lần hoạt động tiếp theo ứng dụng
sẽ khôi phục lại các dữ liệu đã lưu trước đó, do đó phần này chúng ta sẽ
cùng thảo luận về cách lưu dữ liệu thành tập tin lên bộ nhớ và đọc dữ liệu
từ các tập tin đó.
12.2.1. Lưu dữ liệu ở bộ nhớ trong
Bộ nhớ trong là bộ nhớ được nhà sản xuất tích hợp trong quá trình
sản xuất, chúng được sử dụng để chứa các tập tin hệ thống, dữ liệu
chương trình, và cũng cho phép chúng ta lưu dữ liệu lên bộ nhớ này. Ở
344
phần này, chúng ta sẽ tìm hiểu cách đọc và ghi dữ liệu lên bộ nhớ trong.
Mặc định khi lưu tập tin ở đây chỉ có ứng dụng của chúng ta mới có thể
truy cập tập tin này và khi người dùng xóa ứng dụng khỏi thiết bị thì hệ
thống sẽ xóa toàn bộ tập tin lưu trên bộ nhớ trong. Để hiểu rõ hơn chúng
ta sẽ cùng thực hiện một ví dụ như sau:
Ví dụ 2: Từ ví dụ 1 đã thực hiện ở trên, chỉnh sửa lại ứng dụng để
người dùng có thể hiển thị lời bài hát gốc được lưu trong thư mục
Resource, cho phép chỉnh sửa lại lời bài hát theo ý muốn và lưu lại lên bộ
nhớ trong, sau đó nếu muốn người dùng có thể hiển thị trở lại lời bài hát
đã chỉnh sửa.
Từ yêu cầu nêu trên, chúng ta sẽ thêm vào giao diện của ứng dụng
2 Button:
- Button “Read Internal”: dùng để đọc lời bài hát được lưu ở bộ
nhớ trong
- Button “Write Internal”: dùng để ghi lời bài hát vào bộ nhớ trong.
Ví dụ 2 này tôi không trình bày code phần giao diện, vì nó khá đơn
giản, hầu hết là kế thừa từ ví dụ 1, hy vọng đây cũng là một cách để các
bạn luyện tập lại kỹ năng lập trình giao diện cho ứng dụng. Dưới đây là
giao diện sau khi tôi đã chỉnh sửa cho phù hợp với yêu cầu và sơ đồ thiết
kế layout, dựa vào đây các bạn có thể dễ dàng lập trình giao diện.

Hình 12.5. Giao diện ứng dụng và sơ đồ thiết kế layout


345
Tiếp theo, chúng ta sẽ lập trình cho sự kiện khi người dùng chạm
vào Button Read Internal (hiển thị lại lời bài hát đã lưu) và Button Write
Internal (lưu lại lời bài hát).
Code ghi dữ liệu:
Private void WriteInternal() {
String data = edLoiBaihat.getText().toString();
FileOutputStream fileOut = null;
try {
fileOut =
openFileOutput(edTenBaiHat.getText().toString(),
MODE_PRIVATE);
OutputStreamWriter outStream = new
OutputStreamWriter(fileOut);
outStream.write(data);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Để lưu tập tin dữ liệu lên bộ nhớ, chúng ta sẽ sử dụng lớp


FileOutputStream. Phương thức openFileOutput() được dùng để mở
một tập tin trên bộ nhớ, tham số truyền vào cho phương thức này chính là
tên tập tin mà chúng ta muốn mở. Nếu tập tin đã tồn tại trên bộ nhớ thì sẽ
được mở bình thường và ứng dụng có thể làm việc với tập tin này, trường
hợp tập tin không tồn tại trên bộ nhớ thì nó sẽ được khởi tạo trước với tên
như phần tham số chúng ta truyền vào và sau đó tập tin sẽ được mở để
ứng dụng có thể làm việc.
346
Ngoài ra, trong phương thức openFileOutput() còn có tham số nữa
là MODE. Tham số này cho phép chúng ta thiết đặt quyền truy cập lên
tập tin tạo trên bộ nhớ:
- MODE_PRIVATE (0): là chế độ mặc định, chỉ cho phép ứng
dụng của chúng ta truy cập đến tập tin này.
- MODE_APPEND: với chế độ này nếu tập tin chưa tồn tại trên bộ
nhớ thì nó sẽ được khởi tạo và xử lý bình thường. Trường hợp tập tin đã
tồn tại trên bộ nhớ, thì khi ghi dữ liệu lên tập tin, dữ liệu sẽ ghi tiếp theo
dữ liệu cũ, dữ liệu cũ sẽ không bị xóa (ở chế độ mặc định, dữ liệu sẽ
được ghi từ đầu và đè lên dữ liệu cũ).
- MODE_WORLD_READABLE: chế độ này sẽ cho phép các ứng
dụng khác đọc dữ liệu từ tập tin chúng ta lưu trên bộ nhớ.
- MODE_WORLD_WRITEABLE: chế độ này sẽ cho phép các
ứng dụng khác ghi dữ liệu lên tập tin chúng ta lưu trên bộ nhớ.
Lưu ý: Hai chế độ MODE_WORLD_READABLE và
MODE_WORLD_WRITEABLE rất nguy hiểm, các bạn cần phải cân
nhắc kỹ trước khi sử dụng. Vì với chế độ này chúng ta không thể kiểm
soát được nội dung tập tin.
Lớp OutputStreamWriter được sử dụng để chuyển đổi luồng ký
tự thành luồng Byte. Chúng ta khai báo một thể hiện của lớp
OutputStreamWriter sau đó gán giá trị cho nó thông qua hàm khởi tạo
với tham số truyền vào là thể hiện của lớp FileOutputStream:
OutputStreamWriter outStream = new OutputStreamWriter(fileOut);
Bây giờ, chúng ta có thể thực hiện việc lưu dữ liệu lên tập tin vừa
được mở. Sử dụng phương thức write(String str) để lưu dữ liệu, dữ liệu
muốn lưu phải được chuyển về kiểu String, sau đó truyền dữ liệu này vào
phần điền tham số của phương thức write(). Để chắc chắn tất cả các byte
dữ liệu đã được ghi chúng ta sử dụng phương thức flush(). Cuối cùng
dùng phương thức close() để đóng luồng đang ghi lại.
outStream.write(data);
outStream.flush();
outStream.close();
Tiếp theo, chúng ta sẽ cùng tìm hiểu code đọc dữ liệu từ bộ nhớ
trong. Lưu ý, chúng ta phải xác định được tên tập tin lưu dữ liệu, trường
hợp tập tin không tồn tại trong bộ nhớ, sẽ sinh ra một ngoại lệ. Do đó, ở
đây, chúng ta sẽ đặt đoạn code đọc tập tin trong cấu trúc try {} catch (){}.
347
Code đọc dữ liệu:
Privatevoid ReadInternal() {
FileInputStream fileIn = null;
try {
fileIn =
openFileInput(edTenBaiHat.getText().toString());
InputStreamReader inStream = new
InputStreamReader(fileIn);
char[] inBuffer = newchar[100];
int charRead;
String data = "";
while ((charRead = inStream.read(inBuffer)) > 0)
{
String readData = String.valueOf(inBuffer,
0, charRead);
data += readData;
}
fileIn.close();
edLoiBaihat.setText(data);
inBuffer = newchar[100];
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this,
"Không thể mở tập tin lời bài hát",
Toast.LENGTH_SHORT)
.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
348
}
Code trên cũng tương tự với code đọc dữ liệu tĩnh. Chúng ta sẽ sử
dụng lớp FileInputStream và lớp InputStreamReader để tạo luồng đọc
tập tin. Phương thức openFileInput (String name) dùng để mở tập tin,
tham số truyền vào cho phương thức này là tên tập tin muốn mở. Lưu ý,
khi tập tin không tồn tại sẽ sinh ra ngoại lệ FileNotFoundException,
chúng ta sẽ dựa vào ngoại lệ này để thông báo cho người dùng biết tập
tin này không tồn tại. Phần code còn lại hoàn toàn tương tự phần đọc dữ
liệu tĩnh.
Dưới đây là code hoàn chỉnh file MainActivity.java (bao gồm cả
phần đọc dữ liệu tĩnh), hai hàm ReadInternal() và WriteInternal() viết ở
trên sẽ được đặt trong sự kiện setOnClickListener của các Button đọc và
ghi dữ liệu. Trong code này, chúng ta sử dụng nội dung trong
edTenBaiHat để làm tên tập tin lưu trên bộ nhớ:
package com.example.data_ex;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

349
private Button btnHienThi, btnReadInternal, btnWriteInternal;
private EditText edTenBaiHat, edLoiBaihat;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btnHienThi = (Button) findViewById(R.id.btnHienThi);


btnReadInternal = (Button)
findViewById(R.id.btnReadInternal);
btnWriteInternal = (Button)
findViewById(R.id.btnWriteInternal);
edTenBaiHat = (EditText)
findViewById(R.id.edTenBaiHat);
edLoiBaihat = (EditText)
findViewById(R.id.edLoiBaiHat);
btnHienThi.setOnClickListener(new
View.OnClickListener() {

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
// Xu ly su kien nhan vao nut Hien Thi
ShowLyric();
}
});
btnReadInternal.setOnClickListener(new
View.OnClickListener() {

@Override
350
publicvoid onClick(View v) {
// TODO Auto-generated method stub
ReadInternal();
}
});
btnWriteInternal.setOnClickListener(new
View.OnClickListener() {

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
WriteInternal();
}
});
}
Privatevoid ShowLyric() {
InputStream lyric = null;
// Kiem tra neu dung ten bai hat thi doc file tu Resource
drawable
if
(edTenBaiHat.getText().toString().equalsIgnoreCase("untilyou")) {
lyric =
this.getResources().openRawResource(R.drawable.until_you);
} elseif (edTenBaiHat.getText().toString()
.equalsIgnoreCase("vichinhemthoi")) {
lyric = this.getResources().openRawResource(
R.drawable.vi_chinh_em_thoi);
} else {
Toast.makeText(MainActivity.this,
"Không tìm thấy lời bài hát này!!!",
Toast.LENGTH_SHORT)
351
.show();
}
if (lyric != null) {
InputStreamReader inStreamReader = new
InputStreamReader(lyric);
char[] inBuffer = newchar[100];
int charRead;
String data = "";
try {
while ((charRead =
inStreamReader.read(inBuffer)) > 0) {
String readData =
String.valueOf(inBuffer, 0, charRead);
data += readData;
}
// Dong luong doc du lieu
lyric.close();
// Hien thi du lieu doc duoc ra edLoiBaiHat
edLoiBaihat.setText(data.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Private void ReadInternal() {
FileInputStream fileIn = null;
try {
fileIn =
openFileInput(edTenBaiHat.getText().toString());

352
InputStreamReader inStream = new
InputStreamReader(fileIn);
char[] inBuffer = newchar[100];
int charRead;
String data = "";
while ((charRead = inStream.read(inBuffer)) > 0)
{
String readData = String.valueOf(inBuffer, 0, charRead);
data += readData;
}
fileIn.close();
edLoiBaihat.setText(data);
inBuffer = newchar[100];
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this,
"Không thể mở tập tin lời bài hát",
Toast.LENGTH_SHORT).show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Private void WriteInternal() {
String data = edLoiBaihat.getText().toString();
FileOutputStream fileOut = null;
try {
fileOut =
openFileOutput(edTenBaiHat.getText().toString(),

353
MODE_PRIVATE);
OutputStreamWriter outStream = new
OutputStreamWriter(fileOut);
outStream.write(data);
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Dưới đây là kết quả đạt được sau khi thực hiện hai ví dụ trên, đầu
tiên chúng ta sẽ mở dữ liệu tĩnh sau đó chỉnh sửa và lưu lên bộ nhớ trong
của điện thoại, và mở lại nội dung vừa chỉnh sửa để kiểm tra các chỉnh
sửa của chúng ta có được lưu lại không. Đầu tiên tôi hiển thị lời bài hát
vichinhemthoi từ thư mục Resource, sau đó chỉnh sửa nội dung lời bài
hát, kế đến nhấn vào Button Write Internal để lưu lời bài hát tôi vừa
chỉnh sửa, tiếp tục hiển thị lời bài hát untilyou từ thư mục Resource, cuối
cùng tôi nhấn vào Button Read Internal (nhập tên bài hát muốn mở lại
làvichinhemthoi vào ô edTenBaiHat). Như các bạn thấy trên kết quả là
lời bài hát tôi chỉnh sửa lúc đầu đã hiện lại.

354
Hình 12.6. Kết quả đọc và ghi dữ liệu lên bộ nhớ trong
12.2.2. Đọc và ghi dữ liệu lên bộ nhớ ngoài
Bộ nhớ ngoài là bộ nhớ mở rộng được dùng để tăng cường dung
lượng lưu trữ, bộ nhớ ngoài của điện thoại thường là thẻ nhớ. Do đó, tùy
vào nhu cầu lưu trữ mà người dùng sẽ trang bị thẻ nhớ có dung lượng lớn
hay nhỏ cho phù hợp với nhu cầu và giá tiền. Vì là bộ nhớ tùy chọn nên
không phải tất các các thiết bị đều có bộ nhớ ngoài, do đó khi muốn lưu
dữ liệu lên bộ nhớ ngoài, bạn nên kiểm tra sự tồn tại của bộ nhớ ngoài
trước khi tiến hành lưu dữ liệu. Dưới đây chúng ta sẽ tìm hiểu cách lưu
dữ liệu lên bộ nhớ ngoài.
Ví dụ 3: Từ ứng dụng hiển thị lời bài hát đã thực hiện ở hai ví dụ
trên, chúng ta sẽ chỉnh sửa để ứng dụng này có thể đọc và lưu trữ liệu lên
bộ nhớ ngoài.
Chúng ta cũng sẽ thêm hai Button có chức năng tương tự Read
Internal và Write Internal:
- Button “Read External”: đọc dữ liệu từ bộ nhớ ngoài.
- Button “Write External”: ghi dữ liệu lên bộ nhớ ngoài.
Dưới đây là giao diện ứng sau khi thêm hai Button để đọc và ghi
dữ liệu lên bộ nhớ ngoài, phần code cho giao diện khá dễ dàng, các bạn
có thể dựa theo sơ đồ thiết kế layout sau để thực hiện:

355
Hình 12.7. Thiết kế giao diện cho ví dụ 3
Để code nhìn chuyên nghiệp và dễ quản lý hơn, chúng ta sẽ xây
dựng hai khối lệnh thực hiện chức năng đọc và ghi dữ liệu lên bộ
nhớ ngoài.
Code ghi dữ liệu lên bộ nhớ ngoài:
Private void WriteExternal() {
//Lấy đường dẫn bộ nhớ ngoài (thẻ nhớ) và thêm thư mục
quản lý dữ liệu
File sdCard =
Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath() +
"/Data_ex");
//Tạo thư mục với đường dẫn đã thiết đặt ở trên

356
directory.mkdirs();
File file = new File(directory,edTenBaiHat.getText()
.toString());
try {
FileOutputStream fileOut = new
FileOutputStream(file);
OutputStreamWriter outStream = new
OutputStreamWriter(fileOut);
outStream.write(edLoiBaihat.getText().toString());
outStream.flush();
outStream.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Phần code để ghi dữ liệu giống hoàn toàn với ghi dữ liệu lên bộ
nhớ trong, ở đây chúng ta chỉ phân tích phần code tạo tập tin trên bộ nhớ
ngoài. Để lưu tập tin đâu tiên chúng cần có đường dẫn chứa tập tin trước
và sau đó là tạo tập tin theo đường dẫn vừa có được. Đoạn code sau sẽ
giúp bạn lấy đường dẫn bộ nhớ ngoài và thêm vào đường dẫn này thư
mục Data_ex để quản lý tập tin. Do thư mục Data_ex chưa có trên bộ
nhớ ngoài, vì vậy để có thể lưu tập trin trong thư mục này chúng ta cần
sử dụng phương thức mkdirs() để khởi tạo nó trên bộ nhớ ngoài.
File sdCard =
Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath() +
"/Data_ex");
directory.mkdirs();

357
Code đọc dữ liệu hoàn toàn tương tự như trên, chúng ta cần phải có
được đường dẫn tập tin và mở tập tin để đọc dữ liệu:
Private void ReadExternal() {
File sdCard =
Environment.getExternalStorageDirectory();
File directory = new File(sdCard + "/Data_ex");
File file = new File(directory,edTenBaiHat.getText()
.toString());
try {
FileInputStream fileIn = new
FileInputStream(file);
IputStreamReader inStream = new
InputStreamReader(fileIn);
char[] inBuffer = newchar[100];
int charRead;
String data = "";
while ((charRead = inStream.read(inBuffer)) > 0)
{
String readData = String.valueOf(inBuffer,
0, charRead);
data += readData;
}
fileIn.close();
edLoiBaihat.setText(data);
inBuffer = newchar[100];
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
Toast.makeText(MainActivity.this,
"Không thể mở tập tin lời bài hát",
Toast.LENGTH_SHORT).show();
e.printStackTrace();

358
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Cuối cùng chúng ta sẽ thêm các thủ tục WriteExternal() và
ReadExternal() vào sự kiện người dùng nhấn vào các nút Read External
và Write External.
Lưu ý: Để có thể đọc và ghi dữ liệu lên bộ nhớ ngoài, ứng dụng
của chúng ta cần phải được phân quyền. Do đó, trong
AndroidMainfest.xml, các chúng ta cần chèn thêm các khai báo sau:
<uses-
permissionandroid:name="android.permission.WRITE_EXTERNA
L_STORAGE"/>
<uses-
permissionandroid:name="android.permission.READ_EXTERNAL
_STORAGE"/>

12.2.3. SharedPreferences
Đôi khi ứng dụng của chúng ta chỉ cần lưu một vài giá trị đặc biệt,
khi đó lập trình một đoạn code để đọc ghi tập tin lên bộ nhớ là không cần
thiết, hệ điều hành Android cung cấp cho chúng ta một cách khác để lưu
dữ liệu theo kiểu “Key-Value”, đó là sử dụng SharedPreferences. Chúng
ta sẽ cùng thực hiện một ứng dụng lưu thông tin sinh viên đơn giản để
hiểu rõ hơn về SharedPreferences.
Ví dụ 4: Thiết kế ứng dụng lưu trữ thông tin sinh viên. Nội dung
lưu trữ gồm: Tên, Mã số, Lớp. Khi muốn xem lại thông tin sinh viên, chỉ
cần nhập mã số sinh viên là có thể nhận được đầy đủ các thông tin đã
khai báo ban đầu.
Theo yêu cầu thiết kế như trên, giao diện ứng dụng sẽ gồm các
thành phần sau:
- EditText edName: để nhập tên sinh viên
- EditText edCode: để nhập mã số sinh viên
- EditText edClass: để nhập lớp sinh viên

359
- EditText edLoad: nhập mã số sinh viên muốn xem thông
tin
- Button btnSave: dùng để lưu thông tin
- Button btnLoad: dùng để xem thông tin sinh viên
Dưới đây là giao diện đã thiết kế cùng với sơ đồ thiết kế layout, các
bạn có thể dựa vào đây để lập trình code phần giao diện:

Hình 12.8: Thiết kế giao diện cho ví dụ 4


Phần code xử lý sự kiện khi người dùng nhấn Button btnSave và
btnLoad như sau:
package com.example.data_ex1;
import android.os.Bundle;
import android.app.Activity;
import android.content.SharedPreferences;
360
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity {

private Button btnSave, btnLoad;


private EditText edName, edCode, edClass, edLoad;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSave = (Button) findViewById(R.id.btnSave);
btnLoad = (Button) findViewById(R.id.btnLoad);
edName = (EditText) findViewById(R.id.edName);
edCode = (EditText) findViewById(R.id.edCode);
edClass = (EditText) findViewById(R.id.edClass);
edLoad = (EditText) findViewById(R.id.edLoad);
btnSave.setOnClickListener(new View.OnClickListener()
{

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
SaveData();
}
});

361
btnLoad.setOnClickListener(new View.OnClickListener()
{

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
LoadData();
}
});
}
Private void SaveData() {
SharedPreferences pref =
getSharedPreferences(edCode.getText()
.toString(), MODE_PRIVATE);
String code = edCode.getText().toString();
SharedPreferences.Editor editor = pref.edit();
editor.putString(code + "Name", edName.getText().toString());
editor.putString(code + "Code", edCode.getText().toString());
editor.putString(code + "Class", edClass.getText().toString());
editor.commit();
Toast.makeText(MainActivity.this, "Save",
Toast.LENGTH_SHORT).show();
}
Private void LoadData() {
SharedPreferences pref =
getSharedPreferences(edLoad.getText()
.toString(), MODE_PRIVATE);
String code = edLoad.getText().toString();
String loadName = pref.getString(code + "Name", "NA");
String loadCode = pref.getString(code + "Code", "NA");

362
String loadClass = pref.getString(code + "Class", "NA");
Toast.makeText(
MainActivity.this,"Name: " + loadName + "\nCode: " +
loadCode + "\nClass: "+loadClass,Toast.LENGTH_LONG).show();
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Để ghi dữ liệu cần lấy về đối tượng SharedPreferences thông qua
phương thức getSharedPreferences (String name, int mode), chúng ta
cần truyền vào hai tham số:
- String name: là phần tên tập tin lưu dữ liệu.
- Int mode: tham số này cho phép thiết đặt quyền hạn truy cập tập
tin.Gồm các chế độ: MODE_PRIVATE, MODE_WORLD_READABLE,
MODE_WORLD_WRITEABLE ý nghĩa các chế độ này đã được giải
thích ở phần lưu dữ liệu bộ nhớ trong.
Tiếp theo, chúng ta sẽ tạo đối tượng Editor cho phép chỉnh sửa dữ liệu:
SharedPreferences.Editor editor = pref.edit();
Sử dụng phương thức editor.putXXX(“key”, “value”) để đưa dữ
liệu vào editor với XXX là kiểu dữ liệu muốn lưu:
- editor.putBoolean(key, value): dùng để lưu dữ liệu kiểu Boolean
- editor.putFloat(key, value): dùng để lưu dữ liệu kiểu số thực
- editor.putInt(key, value): dùng để lưu dữ liệu kiểu số nguyên
- editor.putLong(key, value): dùng để lưu dữ liệu kiểu số nguyên
- editor.putString(key, value): dùng để lưu dữ liệu kiểu chuỗi
Lưu dữ liệu bằng cách gọi dòng lệnh: editor.commit()

363
Việc lấy dữ liệu cũng rất đơn giản, chúng ta cũng sẽ tạo một đối
tượng SharedPreferences cũng bằng phương thức
getSharedPreferences(), với hai tham số truyền vào sẽ là tên của tập tin
lưu dữ liệu và chế độ truy cập của tập tin (các tham số này hoàn toán
giống với khi lưu dữ liệu). Sử dụng phương thức sau để lấy dữ liệu:
editor.getXXX(“key”, “default value”)
Câu lệnh trên sẽ trả về giá trị là kiểu dữ liệu bạn muốn lấy (thay
XXX bằng kiểu dữ liệu tương ứng), tham số truyền vào:
- key: điền giống với khi bạn lưu dữ liệu
- default value: là giá trị mặc định của dữ liệu, nếu không đọc được
dữ liệu tương ứng với key bạn nhập vào, phương thức này sẽ trả về giá trị
là giá trị mặc định.
Lựa chọn cách lưu dữ liệu tối ưu nhất
Chúng ta đã cùng thảo luận qua một số cách lưu dữ liệu: lưu tập tin
vào bộ nhớ trong, và bộ nhớ ngoài, sử dụng SharedPreference. Vậy đâu
sẽ là giải pháp tối ưu nhất cho ứng dụng của bạn?
- Nếu dữ liệu bạn muốn lưu có dạng key-value thì
SharedPreference là giải pháp tối ưu nhất cho bạn. Một số dữ liệu phù
hợp với việc sử dụng SharedPreference như tên đăng nhập, mật khẩu,
màu nền, ngày sinh, mã số,… Ngoài ra, bạn cũng không cần quan tâm dữ
liệu sẽ được lưu ở đâu, tất cả những gì bạn cần là sử dụng
SharedPreference để lưu dữ liệu và đọc lại các dữ liệu này.
- Nếu bạn cần lưu dữ liệu thay đổi liên tục thì lưu dữ liệu ở bộ nhớ
trong là một giải pháp tốt cho ứng dụng của bạn. Việc lưu dữ liệu ở bộ
nhớ trong cũng tăng thời gian truy cập dữ liệu và bộ nhớ trong thì luôn
luôn có sẵn để sử dụng.
- Trường hợp bạn muốn chia sẻ dữ liệu của ứng dụng giữa các
người dùng thì lưu trữ ở bộ nhớ ngoài là lựa chọn tối ưu cho ứng dụng
của bạn. Ví dụ bạn thiết kế ứng dụng gho phép ghi chú lại các địa điểm
món ăn nổi tiếng, khu vui chơi nghỉ dưỡng thoáng mát, tiện nghi,…và
bạn muốn những người sử dụng ứng dùng này có thể chia sẽ các địa điểm
này với nhau hoặc lưu trữ chúng vào máy tính thì sử dụng bộ nhớ ngoài
để lưu trữ là lựa chọn tốt nhât vì có thể dễ dàng trao đổi thẻ nhớ giữa các
thiết bị. Ngoài ra, các dữ liệu lớn bạn cũng cần nhắc vì bộ nhớ trong còn
phục vụ cho hệ thống do đó việc chiếm dụng nhiều không gian bộ nhớ
trong là không tốt.

364
12.3. TẠO VÀ SỬ DỤNG CƠ SỞ DỮ LIỆU
Chúng ta đã cùng tìm hiểu qua cách lưu dữ liệu bằng
SharedPreferences và lưu tập tin lên bộ nhớưu điểm của chúng là đơn
giản nhưng chỉ phù hợp với việc lưu những dữ liệu đơn giản, rời rạc. Khi
cần lưu các dữ liệu phức tạp có tính cấu trúc và có mối quan hệ với nhau
thì những cách lưu trữ trên không phải là lựa chọn tối ưu vì bạn sẽ rất
khó quản lý. Để hỗ trợ người lập trình trong việc quản lý và lưu trữ các
dữ liệu có tính cấu trúc hệ điều hành Android cung cấp cho chúng ta một
giải pháp khác đó là sử dụng cơ sở dữ liệu SQLite. Ví dụ chúng cần xây
dựng một ứng dụng quản lý sinh viên trong trường, số lượng sinh viên
cần quản lý rất nhiều, mỗi sinh viên sẽ có những thông tin liên quan như:
tên sinh viên, năm sinh, giới tính, mã số sinh viên, khoa, ngành. Giữa các
sinh viên sẽ có các mối quan hệ với nhau như: học chung lớp, có những
sinh viên học khác lớp nhưng học chung một số môn, khác ngành và
khác lớp nhưng thi chung phòng,... Nếu bạn sử dụng các cách lưu dữ liệu
SharedPreferences hoặc lưu tập tin lên bộ nhớ thì trong trường hợp bạn
cần lấy dữ liệu những sinh viên học chung lớp, những sinh viên học
chung môn thì sẽ rất khó nhưng nếu sử dụng cơ sơ dữ liệu để quản lý thì
mọi chuyện sẽ trở nên rất dễ dàng.
Hệ điều hành Android sử dụng hệ thống cơ sở dữ liệu SQLite.
SQLite là cơ sở dữ liệu SQL mã nguồn mở, cho phép quản lý và lưu dữ
liệu trên thiết bị. SQLite hỗ trợ tất cả tính năng của một cơ sở dữ liệu, để
truy cập cơ sở dữ liệu SQLite bạn không cần phải thiết lập các loại kết
nối như JDBC, ODBC,…
Để hiểu rõ hơn về cơ sở dữ liệu, chúng ta sẽ cùng tìm hiểu các
phần sau:
- Tạo và xóa một cơ sở dữ liệu SQLite
- Tạo và xóa một bảng trong SQLite
- Thêm, sửa và xóa dữ liệu trong bảng
- Truy vấn dữ liệu trong bảng.
Những phần liệt kê ở trên là những phần cơ bản nhất khi bạn bắt
đầu làm quen với SQLite, còn rất nhiều chức năng khác nữa nhưng trong
khuôn khổ sách này không thể trình bày hết tất cả, các bạn có thể tìm
hiểu thêm về SQLite từ trang web của Google hỗ trợ các lập trình viên để
hiểu rõ hơn các chức năng cũng như những công cụ mà SQLite cung cấp
cho chúng ta. Để nhanh chóng hiểu và áp dụng cơ sở dữ liệu vào các ứng
dụng, chúng ta sẽ cùng thực hiện ví dụ sau:
Ví dụ 6: Thiết kế ứng dụng quản lý sinh viên
365
Ở ví dụ này, chúng ta sẽ cùng thiết kế một ứng dụng cơ bản để
quản lý sinh viên trong trường, một số thông tin cơ bản của sinh viên sẽ
được quản lý trong ứng dụng này:
- Tên sinh viên
- Ngày/ Tháng/ Năm sinh
- Giới tính
- Mã số sinh viên
- Lớp
- Điểm trung bình học tập.
Trong ứng dụng này, chúng ta cũng xây dựng một số chức năng cơ
bản để hỗ trợ việc quản lý:
- Thêm/ Chỉnh sửa/ Xóa sinh viên trong cơ sở dữ liệu
- Xem thông tin sinh viên dựa vào mã số sinh viên
- Thực hiện truy vấn trong cơ sở dữ liệu: Hiện danh sách sinh viên
có điểm trung bình trên 9.0.
Những chức năng ở trên là rất cơ bản và cần thiết phải có của một
ứng dụng quản lý, khi các bạn tiến hành thiết kế một ứng dụng để sử
dụng trong thức tế thì cần bổ sung thêm nhiều thông tin quản lý khác
như: môn học của sinh viên, số tín chỉ, thời khóa biểu,… Ngoài ra cũng
cần có thêm một dữ liệu tương ứng của Thầy, Cô để liên kết với môn học
của sinh viên. Ở ví dụ này, tôi thực hiện truy vấn dữ liệu để lọc ra những
sinh viên có điểm trung bình trên 9.0, số 9.0 là cố định để bạn dễ dàng
hiểu code, khi thiết kế ứng dụng của mình các bạn có thể sửa đổi chức
năng này và cho phép người dùng nhập số điểm trung bình mong muốn.
Còn rất nhiều chức năng khác cần phải có để hoàn thiện một ứng dụng
quản lý nhưng chúng đều dựa trên những điều ở bản bạn học được từ ví
dụ này.
Đầu tiên, chúng ta sẽ khởi tạo một project mới với các thông số
như hình dưới:

366
Hình 12.9. Khởi tạo Project cho ví dụ 6
Từ các thông tin cần quản lý đã liệt kê ở trên, chúng ta sẽ xây dựng
giao diện để nhập các thông tin sinh viên, ngoài ra cũng cần tạo các đối
tượng hỗ trợ người sử dụng truy vấn trong cơ sở dữ liệu. Dưới đây là
giao diện ứng dụng tôi đã thiết kế sẵn và sơ đồ thiết kế layout.

Hình 12.10. Thiết kế Layout cho ví dụ 6


Ví dụ này chúng ta sẽ sử dụng hệ cơ sở dữ liệu SQLite để quản lý
và lưu trữ dữ liệu. Đầu tiên, cần phải tạo một cơ sở dữ liệu để chứa các

367
bảng, chúng ta sẽ tạo cơ sở dữ liệu qlsv.db. Thông tin các đối tượng sẽ
được quản lý thông qua các bảng, mỗi bảng sẽ quản lý thông tin của một
đối tượng, đối tượng của chúng ta là sinh viên nên trong cơ sở dữ liệu
qlsv.db chúng ta sẽ tạo một bảng tên sinhvien, bảng này sẽ chứa các dữ
liệu về sinh viên với các mục sau:

Tên cột Kiểu dữ liệu Mô tả


tensv Text Tên sinh viên
ngaysinh Text Ngày tháng năm sinh
gioitinh Boolean Giới tính: Nam/ Nữ
mssv Text Mã số sinh viên
lop Text Lớp
diemtb Float Điểm trung học tập
Khi sử dụng cơ sở dữ liệu SQLite cần import thư viện sau vào
Project:
import android.database.sqlite.SQLiteDatabase;
Như đã trình bày ở trên chúng ta cần phải tạo một cơ sở dữ liệu,
đoạn code dưới đây sẽ tạo ra cơ sở dữ liệu qlsv.db chế độ Private.
Privatevoid TaoDatabase() {
qlsv = openOrCreateDatabase("qlsv.db",
MODE_PRIVATE, null);
}
Phương thức openOrCreateDatabase được dùng khi muốn tạo
mới hoặc mở một cơ sở dữ liệu, trường hợp cơ sở dữ liệu chưa tồn tại thì
sẽ được khởi tạo trước, sau đó sẽ được mở và trả về cho đối tượng qlsv.
Nếu khởi tạo thành công, cơ sở dữ liệu của bạn mặc định sẽ nằm ở bộ
nhớ trong, khi muốn lưu cơ sở dữ liệu ở bộ nhớ ngoài cần phải sửa phần
tham số tên cơ sở dữ liệu như sau:
“Đường dẫn bộ nhớ ngoài/qlsv.db”.
Trong đó, Đường dẫn bộ nhớ ngoài là phần đường dẫn chỉ đến bộ
nhớ ngoài trên thiết bị. Cách lấy đường dẫn bộ nhớ ngoài đã được thảo
luận ở phần lưu tập tin ở bộ nhớ ngoài, bạn có thể tìm hiểu lại để áp dụng
vào ví dụ này. Lưu ý, bạn cần cấp quyền đọc/ ghi lên bộ nhớ ngoài cho

368
ứng dụng trong tập tin androidMainfest.xml khi muốn lưu cơ sở dữ liệu
lên bộ nhớ ngoài.
Trong phương thức openOrCreateDatabase còn một tham số quan
trọng bạn cần lưu ý đó là tham sốMODE, tham số này bạn đã được gặp
nhiều ở những cách lưu dữ liệu chúng ta tìm hiểu phía trên, chúng cho
phép bạn quy định quyền truy cập của các ứng dụng khác lên cơ sở dữ
liệu này.
Ngoài cách tạo cơ sở dữ liệu bằng phương thức trên ta có thể sử
dụng một số phương thức khác cũng có chức năng tương tự như sau:
openDatabase(String path, CursorFactory factory, int flags,
DatabaseErrorHandler errorHandler)
Phương thức này chỉ mở cơ sở dữ liệu đã tồn tại trên bộ nhớ. Các
chế độ phổ biến thường dùng ở phương thức này như:
OPEN_READWRITE, OPEN_READONLY

openDatabase(String path, CursorFactory factory, int flags)


Phương thức này cũng tương tự như phương thức trên, nó chỉ mở
các cơ sở dữ liệu đã tồn tại nhưng nó không định nghĩa bất kỳ trình xử lý
lỗi nào để xử lý khi việc mở cơ sở dữ liệu phát sinh lỗi

openOrCreateDatabase(String path, CursorFactory factory)


Phương thức này không chỉ mở các cơ sở dữ liệu đã tồn tại, nếu cơ
sở dữ liệu chưa tồn tại thì nó sẽ tạo mới cơ sở dữ liệu và mở chúng.

openOrCreateDatabase(File file, CursorFactory factory)


Phương thức này cũng tương tự phương thức trên, có thể mở và tạo
cơ sở dữ liệu. Nhưng sử dụng đối tượng File thay cho một chuỗi.

Trong một số ứng dụng chúng ta cần phải xóa cơ sở dữ liệu vì vậy
ở đây chúng ta cũng sẽ tìm hiểu cách xóa một cơ sở dữ liệu (để đơn giản,
ở ứng dụng này chúng ta sẽ không sử dụng lệnh xóa cơ sở dữ liệu, mà
mặc định luôn tạo vào mở cơ sở dữ liệu mỗi khi ứng dụng được chạy):
Privatevoid XoaDatabase() {
String msg = "";

369
if (deleteDatabase("qlsv.db") == true) {
msg = "Xóa cơ sở dữ liệu thành công";
} else {
msg = "Có lỗi khi xóa cơ sở dữ liệu";
}
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT).show();
}
Phương thức deleteDatabase() cho phép chúng ta xóa cơ sở dữ liệu
và sẽ trả về một giá trị kiểu Boolean, nếu giá trị trả về là true thì việc xóa cơ
sở dữ liệu thành công, ngược lại thì đã có lỗi trong khi thực hiện xóa cơ sở
dữ liệu. Tham số truyền vào cho phương thức này chính là tên cơ sở dữ liệu
muốn xóa, như đã trình bày ở trên, nếu bạn lưu cơ sở dữ liệu ở bộ nhớ trong
thì chỉ cần điền tên cơ sở dữ liệu nhưng nếu lưu ở bộ nhớ ngoài thì cần phải
thêm đường dẫn chỉ đến bộ nhớ ngoài vào tên cơ sở dữ liệu.
Chúng ta cần tạo các bảng trong cơ sở dữ liệu để quản lý đối tượng
sinh viên, bảng này có các cột là các thông tin cần quản lý và từng hàng
trong bảng ứng với từng sinh viên. Phương thức execSQL() được dùng
để khởi tạo bảng, tham số truyền vào phương thức này là câu lệnh khởi
tạo trong SQL, chúng ta sẽ thay đổi một số thông tin để phù hợp với ứng
dụng của chúng ta gồm: tên bảng, tên cột và kiểu dữ liệu. Đoạn mã
dưới đây sẽ tạo bảng sinhvien trong cơ sở dữ liệu qlsv.db với các cột
tensv, ngaysinh, gioitinh, mssv, lop, diemtb.
Privatevoid TaoBangSinhVien(SQLiteDatabase csdl) {
//Kiem tra Bang sinhvien da ton tai chua
//Neu chua ton tai se tao bang moi
Cursor c = csdl.query("sinhvien", null, null, null, null,
null, null);
if (c == null){
String sql = "CREATE TABLE sinhvien(tensv
TEXT,ngaysinh TEXT, "+ "gioitinh Boolean, mssv TEXT," + "lop
TEXT,diemtb FLOAT)";
qlsv.execSQL(sql);
}

370
}
Lưu ý: Khi gọi phương thức TaoBangSinhVien chúng ta phải
truyền vào tham số là cơ sở dữ liệu đã tạo ở trên. Nếu bảng đã tồn tại
trong cơ sở dữ liệu mà bạn tiếp tục tạo bảng có cùng tên sẽ sinh ra lỗi. Do
đó, ở đây tôi sử dụng phương thức query() để thực hiện truy vấn nhằm
xác định bảng đã tồn tại trong cơ sở dữ liệu chưa, nếu chưa tồn tại sẽ
thực hiện tạo bảng mới. Phương thức query() sẽ được tìm hiểu chi tiết ở
phần truy vấn cơ sở dữ liệu.
Tiếp theo, chúng ta sẽ viết lệnh để thêm sinh viên vào bảng
sinhvien, thông tin sinh viên sẽ được nhập thông qua các EditText tương
ứng trên giao diện ứng dụng, phương thức thêm sinh viên này sẽ được
gọi trong sự kiện người dùng nhấn vào Button Thêm.
Privatevoid ThemSV() {
ContentValues values = new ContentValues();
values.put("tensv", edTen.getText().toString());
values.put("ngaysinh", edNgaySinh.getText().toString());
values.put("gioitinh", radNam.isChecked());
values.put("mssv", edMSSV.getText().toString());
values.put("lop", edLop.getText().toString());
values.put("diemtb",
Float.valueOf(edDTB.getText().toString()));
String msg = "";
if (qlsv.insert("sinhvien", null, values) != -1) {
msg = "Thêm thành công!";
} else {
msg = "Có lỗi xảy ra";
}
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT).show();
}
Đối tượng ContentValues được sử dụng để chứa dữ liệu về sinh
viên lấy từ các EditText, các thông tin này được đặt vào đối tượng
ContentValues thông qua phương thức put(String key, xxx value) với
371
xxx là kiểu dữ liệu muốn lưu. Sau khi đã đặt đầy đủ thông tin vào đối
tượng ContentValue chúng ta sẽ sử dụng phương thức insert() để thêm
dữ liệu vào bảng sinhvien. Phương thức insert()trả về giá trị là một số
kiểu long và giá trị này sẽ cho biết việc thêm sinh viên vào bảng có thành
công không, nếu giá trị trả về khác -1 thì là thêm thành công, trường hợp
giá trị trả về bằng -1 thì việc thêm sinh viên đã gặp lỗi.
Khác với việc thêm bảng vào cơ sở dữ liệu, khi thêm hai bảng cùng
tên vào cùng một cơ sở dữ liệu sẽ phát sinh lội, còn khi thêm hai sinh
viên có thông tin hoàn toàn giống nhau vào cùng một bảng thì sẽ không
có lỗi, và trong bảng sẽ tồn tại cả hai sinh viên này. Dưới đây là bảng dữ
liệu sinh viên chúng sẽ nhập vào để thực hiện các yêu cầu tiếp theo của
ví dụ này (các bạn có thể thay đổi tùy ý các thông tin này):

Tensv Ngaysinh gioitinh mssv lop Diemtb


Nguyen Hoang
01/01/1992 Nam 10101011 101012 8.5
Long
Hoang Nhat Bao 02/02/1992 Nam 10101012 101012 7.3
Pham Minh Hung 03/03/1992 Nam 10101013 101011 6.5
Nguyen Thien
04/04/1992 Nam 10101014 101011 9.2
Minh
Dinh Tuyet
05/05/1992 Nu 10101015 101012 9.0
Nhung
Nguyen Thi Van
06/06/1992 Nu 10101016 101011 8.7
Anh
Chúng ta sẽ dựa vào mã số sinh viên (mssv) để xác định sinh viên
trong bảng dữ liệu. Do đó, khi muốn cập nhật thông tin cho các sinh viên
đã lưu trong cơ sở dữ liệu, bạn phải biết chính xác mã số sinh viên cần
chỉnh sửa. Trong ví dụ chúng ta thực hiện ở đây, khi thay đổi thông tin
của sinh viên chúng ta sẽ nhập lại toàn bộ thông tin của sinh viên đó. Tuy
nhiên, khi lập trình ứng dụng thực tế các bạn không cần phải thực hiện
thay đổi toàn bộ thông tin, có thể lựa chọn những thông tin cần thay đổi,
các thông tin không chỉnh sửa sẽ vẫn giữ giá trị cũ.
Khi phải nhập lại toàn bộ thông tin của sinh viên thì việc sửa thông tin
này khá giống với thêm sinh viên, nhưng có sự khác biệt cơ bản như sau:
- Nếu nhấn nút Thêm, thì toàn bộ thông tin bạn nhập sẽ được sử
dụng để tạo thêm một sinh viên mới trong cơ sở dữ liệu, và thông tin của
372
sinh viên cũ (có cùng mssv) sẽ không bị thay đổi. Như vậy, trong cơ sở
dữ liệu sẽ tồn tại hai sinh viên có cùng mã số sinh viên.
- Nếu nhấn nút Sửa, thì ứng dụng sẽ tự động tìm sinh viên có mssv
như đã nhập ở trên và thay đổi các thông tin cũ của sinh viên này bằng
thông tin mới.
Code phần sửa thông tin sinh viên như sau:
Privatevoid SuaSV(String mssv) {
ContentValues values = new ContentValues();
values.put("tensv", edTen.getText().toString());
values.put("ngaysinh", edNgaySinh.getText().toString());
values.put("gioitinh", radNam.isChecked());
values.put("lop", edLop.getText().toString());
values.put("diemtb",
Float.valueOf(edDTB.getText().toString()));
String msg = "";
int i = qlsv
.update("sinhvien", values, "mssv=?", new
String[] { mssv });
if (i == 0) {
msg = "Không tìm thấy sinh viên có MSSV như
bạn nhập";
} else {
msg = "Đã sửa thành công!";
}
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT).show();
}
Chúng ta cũng sử dụng đối tượng ContentValues để chứa các
thông tin sinh viên (những thông tin cần cập nhật). Bạn lưu ý, ở đây
chúng ta không đặt mã số sinh viên vào đối tượng ContentValues vì phần
mã số sinh viên không thay đổi khi thực hiện cập nhật. Tương tự, khi xây
dựng các ứng dụng khác nếu thông tin nào bạn không muốn thực cập
nhật thì không cần đặt chúng vào đối tượng ContentValues.
373
Phương thức update() để cập nhật thông tin sinh viên có trong cơ
sở dữ liệu, trong phương thức này cần truyền vào các tham số sau:
- Table: tên bảng chứa sinh viên mà bạn muốn cập nhật thông tin.
- Values: nội dung cập nhật, phần này chúng ta sẽ đặt vào đối
tượng ContentValues đã tạo ở trên.
- whereClause: chứa tập các điều kiện lọc, các điều kiện lọc này
giúp tìm chính xác sinh viên cần cập nhật thông tin, ở đây chúng ta sử
dụng điều kiện lọc là mã số sinh viên.
- whereArgs: chứa giá trị của các điều kiện lọc, các giá trị này có
thứ tự tương ứng với thứ tự các điều kiện lọc của tham số phía trước.
Phương thức update() sẽ trả về một giá trị kiểu Integer, giá trị này
có ý nghĩa là số hàng trong bảng đã được cập nhật (số hàng tương ứng
với số sinh viên, ở ví dụ này mỗi lần chúng ta chỉ cập nhật cho một sinh
viên vì mã số sinh viên là duy nhất, một số trường hợp chúng ta sẽ cập
nhật cùng lúc nhiều sinh viên thì giá trị trả về sẽ là một số lớn hơn 1).
Nếu giá trị trả về là 0 thì tương ứng không có sinh viên có mã số sinh
viên như trên trong cơ sở dữ liệu. Trong tham số điều kiện lọc, khi bạn có
nhiều điều kiện hơn 1, chúng ta sẽ sử dụng từ nối “and” để liên kết các
điều kiện lọc:
Điều kiện lọc: “mssv=? and tensv=?”
Giá trị lọc: new String[]{mssv, “Pham Minh Hung”}
Ở ví dụ này mỗi sinh viên chỉ có một mã số sinh viên duy nhất do đó
việc thêm tên vào điều kiện lọc là không cần thiết, nhưng tôi cũng muốn
giới thiệu đến các bạn vì trong những ứng dụng thực tiễn trong một số
trường hợp bạn phải kết hợp nhiều bộ lọc để có kết quả chính xác.
Để xóa một sinh viên có trong cơ sở dữ liệu, chúng ta cũng dựa
vào mã số sinh viên. Dưới đây là code dùng để xóa sinh viên trong cơ
sở dữ liệu:
Privatevoid XoaSV(String mssv) {
int i = qlsv.delete("sinhvien", "mssv=?", new String[] {
mssv });
String msg = "";
if (i == 0) {
msg = "Không tìm thấy sinh viên có MSSV như
bạn nhập";

374
} else {
msg = "Đã xóa thành công!";
}
Toast.makeText(MainActivity.this, msg,
Toast.LENGTH_SHORT).show();
}
Sử dụng phương thức delete() để thực hiện việc xóa sinh viên trong
cơ sở dữ liệu, tương tự với phương thức update(), phương thức delete()
cũng yêu cầu chúng ta truyền vào các tham số:
- Table: bảng chứa sinh viên muốn xóa “sinhvien”.
- whereClause: chứa điều kiện lọc “mssv=?”, chúng ta sẽ lọc theo
mã số sinh viên
- whereArgs: đặt vào mã số của sinh viên muốn xóa. Khi kiểm tra
trong cột mssv, nếu thấy sinh viên nào có mã số sinh viên trùng với giá
trị truyền vào thì sẽ thực hiện việc xóa sinh viên đó.
Phương thức delete() trả về giá trị kiểu Integer tương ứng với số
hàng trong bảng (số hàng cũng chính là số sinh viên) đã bị xóa. Trong ví
dụ này, mã số sinh viên là duy nhất nên khi thực hiện thao tác xóa nếu
nhập đúng mã số sinh viên thì giá trị trả về luôn luôn là 1. Nhưng như tôi
đã trình bày ở trên, bạn có thể thêm hai sinh viên có thông số hoàn toàn
giống nhau trong cùng một bảng, khi đó nếu gọi phương thức delete() và
dùng mã số sinh viên làm điều kiện lọc để xóa thì giá trị trả về sẽ là 2
ứng với hai sinh viên có cùng mã số bị xóa.
Cuối cùng ta sẽ tìm hiểu về phần truy vấn cơ sở dữ liệu, đây là
phần phức tạp nhất khi tìm hiểu về cơ sở dữ liệu. Nhưng đây cũng là thế
mạnh của cơ sở dữ liệu so với các cách lưu trữ mà chúng ta đã tìm hiểu ở
phần trước. Chúng ta sẽ cùng thực hiện ví dụ đơn giản nhất đó là xem
thông tin một sinh viên dựa vào mã số sinh viên. Dưới đây là code để
xem thông tin sinh viên:
Privatevoid XemSV(String mssv) {
Cursor c = qlsv.query("sinhvien", new String[] { "tensv",
"mssv",
"diemtb" }, "mssv=?", new String[] {
mssv}, null, null, null);
if (!c. moveToFirst ()) {

375
Toast.makeText(MainActivity.this, "Không tìm
thấy sinh viên có mã số như trên",
Toast.LENGTH_SHORT).show();
return;
}
c.moveToFirst();
edTen.setText(c.getString(0));
edMSSV.setText(c.getString(1));
edDTB.setText(String.valueOf(c.getFloat(2)));
}
Chúng ta sử dụng phương thức query() để thực hiện việc truy xuất
dữ liệu, phương thức này sẽ trả về giá trị là con trỏ. Chúng ta sẽ dùng đối
tượng con trỏ để lấy dữ liệu mong muốn. Để thực hiện tuy vấn cần phải
truyền cho phương thức query() những tham số sau:

String table Tên bảng thực hiện truy vấn


String[] columns Danh sách các cột sẽ trả về, ví dụ bảng truy vấn có
năm cột nhưng bạn chỉ muốn lấy giá trị của ba cột
đầu, thì chỉ liệt kê ba cột đầu ở đây. Nếu đặt tham số
này là null thì sẽ trả về tất cả các cột trong bảng.
String selection Định nghĩa bộ lọc, để lọc những kết quả trả về theo
mong muốn. Định dạng nhập vào đây cũng giống với
SQL WHERE. Nếu đặt tham số này là null thì sẽ trả
về tất cả các hàng trong bảng.
String[] Tham số này dùng để đặt những giá trị của bộ lọc
selectionArgs bạn đã định nghĩa ở tham số trước
String groupBy Định nghĩa bộ lọc để nhóm các hàng lại với nhau,
định dạng nhập cũng giống với SQL GROUP BY.
Nếu đặt tham số này là null thì các hàng không được
nhóm.
String having Định nghĩa bộ lọc cho các nhóm hàng (những hàng
được nhóm lại nhờ tham số phía trước). Nếu đặt tham
số này là null thì không thực hiện lọc.
String orderBy Định nghĩa việc sắp xếp các hàng trong kết quả trả

376
về.

Ở phần xem thông tin sinh viên này chúng ta chỉ cần quan tâm đến
bốn tham số đầu:
String table: “sinhvien”
String[] columns: “new String[] { "tensv", "mssv","diemtb" }”
Ở đây trong kết quả trả về tôi chỉ muốn lấy các thông tin về Tên sinh
viên, Mã số sinh viên và Điểm trung bình do đó tôi chỉ liệt kê ba cột
tensv, mssv, diemtb. Bạn có thể thay thế bằng các cột khác trong bảng
sinhvien. Nếu muốn lấy toàn bộ các thông tin tương ứng với sinh viên
các bạn đặt tham số này là null.
String selection: “mssv=?” Vì mã số sinh viên là duy nhất nên
chúng ta sẽ sử dụng mssv để lọc kết quả.
String[] selectionArgs: new String[] { mssv} đây là mảng chứa giá
trị tương ứng với bộ lọc ở tham số trước, chúng ta sẽ so sánh mã số sinh
viên trong bảng dữ liệu với mã số sinh viên được người dùng nhập vào.
Các tham số còn lại chúng ta không sử dụng nên đặt giá trị null.
Sau khi có được giá trị trả về từ phương thức query(), chúng ta cần
kiểm tra có sinh viên nào phù hợp với điều kiện lọc không trước khi tiến
hành lấy thông tin của sinh viên đó. Sử dụng phương thức moveToFirst(),
phương thức này sẽ giúp chúng ta thực hiện cùng lúc hai việc:
- Kiểm tra có sinh viên nào phù hợp với điều kiện lọc trên không,
nếu không có thì đối tượng con trỏ(cursor) sẽ là trống(empty) khi đó
phương thức moveToFirst() cógiá trị trả về là false, dựa vào đây ta có
thế thông báo không tìm thấy sinh viên phù hợp.
- Nếu có sinh viên phù hợp với điều kiện lọc, đối tượng con trỏ sẽ khác
trống, và phương thức moveToFirst() sẽ đặt con trỏ về dòng đầu tiên.
Trường hợp có sinh viên phù hợp với kết quả lọc chúng ta sử dụng
phương thức getXXX(int ColumnIndex) để lấy thông tin sinh viên đó.
- XXX: là kiểu giá trị ứng với dữ liệu trong bảng.
- int ComunIndex là vị trí cột muốn lấy dữ liệu, vị trí các cột này
tương ứng với vị trí trong tham số String[] columns mà chung ta đã
nhập, ở đây tương ứng như sau: tensv - 0, mssv - 1 ,diemtb – 2. Nếu
bạn nhập null ở tham số String[] columns thì vị trí các cột sẽ tương ứng
với khi chúng ta tạo bảng.
Cuối cùng là gán các dữ liệu đã lấy được vào các EditText tương ứng.

377
Yêu cầu còn lại của ứng dụng là thiết kế chức năng liệt kê danh
sách sinh viên có điểm trung bình trên 9.0 (như đã nói ở trên, 9.0 là số do
tôi chọn, các bạn có thể phát triển thêm chức năng cho người dùng nhập
mức điểm mong muốn). Tương tự với chức năng xem thông tin sinh viên,
chúng ta cũng sẽ sử dụng phương thức query() để truy vấn với bộ lọc sử
dụng là điểm trung bình >=9.0
Private voidDanhSachSVGioi() {
Cursor c = qlsv.query("sinhvien", new String[] { "tensv",
"mssv","diemtb" }, "diemtb>=?", new String[] { "9.0" }, null, null,
"diemtb asc");
c.moveToFirst();
String msg = "";
while (!c.isAfterLast()) {
msg += "Tên SV: " + c.getString(0) + "\nMSSV: "
+ c.getString(1)+ "\nĐTB: " + c.getFloat(2) + "\n==========\n";
c.moveToNext();
}
tvKQ.setText(msg);
}
Code trên gần giống với code xem sinh viên, chúng ta chỉ phân tích
những điểm khác quan trọng sau:
- Tham số String orderBy trong phương thức query(). Ở phần
xem thông tin sinh viên có kết quả trả về chỉ có thể là 1 hoặc 0 nên
chúng ta không quan tâm đến tham số này nhưng ở chức năng này nếu
có nhiều sinh viên có điểm trung bình từ 9.0 trở lên, thì chúng ta sẽ cần
nghĩ đến việc sắp xếp các sinh viên này như thế nào trước khi hiển thị
chúng. Tham số String orderBy sẽ giúp chúng ta thực hiện công việc
trên. Có hai cách sắp xếp là tăng dần (ASC) và giảm dần (DESC). Nếu
muốn sắp xếp kết quả trả về theo điểm số giảm dần, các bạn điểm cao
hơn sẽ xếp trước và thấp hơn sẽ xếp sau thì tham số String orderBy sẽ
là "diemtb asc", nếu muốn xếp ngược lại thì thay bằng "diemtb desc".
Nếu muốn sắp xếp theo các cột khác thì chúng ta chỉ cần thay “diemtb”
bằng cột tương ứng.

378
- Tham số String selection và String[] selectionArgs: điều kiện
lọc này đối với cột điểm trung bình "diemtb>=?" và giá trị tương ứng
new String[] { "9.0" }
- Chúng ta sử dụng vòng lặp để lấy toàn bộ thông tin sinh viên từ
con trỏ, phương thức isAfterLast() sẽ là điều kiện lặp. Trường hợp
không có sinh viên thỏa điều kiện lọc thì đối tượng con trỏ sẽ là trống và
phương thức isAfterLast() sẽ trả về giá trị true (khi đó !c. isAfterLast()
sẽ là false). Nếu có sinh viên thỏa điều kiện lọc thì khi đọc hết các sinh
viên trong con trỏ phương thức isAfterLast() cũng sẽ trả về giá trị true.
Dưới đây là kết quả tìm kiếm những sinh viên có điểm trung bình
từ 9.0 trở lên

Hình 12.11. Kết quả hiển thị sinh viên có điểm trung bình từ 9.0 trở lên

379
380
Chương 13
TIN NHẮN VÀ EMAIL

Khi một ứng dụng Android cơ bản của bản được bật lên và đang
chạy, thì điều thú vị tiếp theo là bạn có thể thêm cho nó khả năng thông
tin với thế giới bên ngoài. Bạn có thể muốn ứng dụng của bạn gửi một tin
nhắn SMS đến một số điện thoại khác khi một sự kiện xảy ra (chẳng hạn
như khi bạn đến gần một địa điểm địa lý cụ thể nào đấy) hoặc bạn có thể
muốn truy cập một dịch vụ Web để lấy các thông tin giống như tiền tệ,
thời tiết,… Trong chương này, bạn sẽ tìm hiểu làm thế nào để gửi và
nhận những tin nhắn SMS được lập trình tự động tự ứng dụng Android
của bạn.

13.1. TIN NHẮN SMS


Android xây dựng sẵn cho bạn ứng dụng SMS có thể gửi và nhận
dữ liệu SMS. Tuy nhiên, trong một vài trường hợp bạn có thể tích hợp
khả năng SMS vào ứng dụng Android của riêng bạn. Ví dụ, bạn có thể
viết một ứng dụng tự động gửi một tin nhắn SMS theo khoảng chu kỳ
thời gian. Một ứng dụng đơn giản bạn có thể dễ hình dung là bạn cần
theo dõi vị trí của con bạn. Và cứ đúng 30 phút ứng dụng từ điện thoại
của con bạn sẽ gửi một tin nhắn SMS đến số phone của bạn cho biết vị trí
địa lý của chúng. Bây giờ thì bạn có thể biết được thực sự con của bạn có
đến thư viện sau giờ học hay không? Dĩ nhiên, bạn sẽ phải trả một phí
nhất định cho việc gửi tin nhắn SMS này. Điều này mô tả việc bạn có thể
lập trình tự động để gửi và nhận tin nhắn SMS từ ứng dụng Android của
bạn. Một tin vui cho các nhà phát triển Android là bạn không cần một
thiết bị thực để kiểm tra việc nhắn tin SMS. Thiết bị ảo cung cấp miễn
phí khả năng đó.
13.1.1. Việc gửi tin nhắn SMS theo việc lập trình tự động
Đầu tiên bạn sẽ tìm hiểu làm thế nào để lập trình gửi tin nhắn SMS
từ ứng dụng của bạn. Sử dụng cách tiếp cận này, ứng dụng của bạn có thể
tự động gửi một tin nhắn SMS đến một số điện thoại nào đó mà ko cần
sự can thiệp của người dùng.
Ví dụ 1: Ta có một ứng dụng được viết gồm một nút nhấn Send
SMS chạy trên thiết bị giả lập 5556:Z1. Khi nhấn vào nút Send SMS nó
sẽ gửi một tin nhắn có nội dung là “Hello my friend!” sang thiết bị giả
lập 5554:z2.
381
Như yêu cầu ta chỉ cần viết ứng dụng gửi tin nhắn tự động cài đặt
trên thiết bị 5556:Z1, còn thiết bị 5556:z2 nhận tin nhắn bằng trình nhận
tin nhắn mặc định.

Hình 13.1. Giao diện nhận và gửi tin nhắn trên hai thiết bị ảo
Ta thực hiện lần lượt các bước sau:
Bước 1: Thiết kế giao diện cho ứng dụng Send SMS. Chỉnh sửa
file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

382
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="29dp"
android:text="@string/title_tv"
android:textAppearance="?android:attr/textAppearanceMedium"/>

<Button
android:id="@+id/send_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_centerHorizontal="true"
android:layout_marginTop="32dp"
android:text="@string/sms_send"/>

</RelativeLayout>
Bước 2: Định nghĩa các hằng số trong file: res\value\String.xml
như sau:
<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<stringname="app_name">SendSMS</string>
<stringname="action_settings">Settings</string>
<stringname="sms_send">Send SMS</string>

383
<stringname="title_tv">This is an application about SMS</string>

</resources>
Bước 3: Khai báo cho phép ứng dụng sử dụng việc gửi tin nhắn SMS
trong file AndroidManifest.xml. Chú ý dòng in đậm là dòng ta thêm vào:
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="nguyenvanhiep.fivemen"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>

//Cho phép ứng dụng gửi tin nhắn SMS


<uses-
permissionandroid:name="android.permission.SEND_SMS"><
/uses-permission>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="nguyenvanhiep.fivemen.MainActivity"
android:label="@string/app_name">
<intent-filter>

384
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>
Bước 4: Viết chương trình xử lý sự kiện cho ứng dụng. Chỉnh sửa
file scr\nguyenvanhiep.fivemen\MainActivity.java
package nguyenvanhiep.fivemen;
import android.os.Bundle;
import android.app.Activity;
//khai báo thư viện gửi tin nhắn SMS
import android.telephony.gsm.SmsManager;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

@SuppressWarnings("deprecation")
Publicclass MainActivity extends Activity {
//Khai báo nút nhấn Send SMS
Button btn_sendSMS;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Tìm nút nhấn btn_sendSMS
btn_sendSMS=(Button) findViewById(R.id.send_btn);

385
//Thiết lập phương thức xử lý sự kiện Click
btn_sendSMS.setOnClickListener(new
View.OnClickListener() {

@Override
publicvoid onClick(View v) {
// TODO Auto-generated method stub
//Gửi tin nhắn SMS đến máy ảo 5554 với nội dung “Hello my
friend!”
//hàm sendSMS(String, String) ta sẽ viết bên dưới
sendSMS("5554","Hello my friend!");

//Hàm gửi tin nhắn sendSMS


Private void sendSMS(String phoneNumber,
String message) {
// TODO Auto-generated method stub

SmsManager sms=SmsManager.getDefault();
sms.sendTextMessage(phoneNumber,null,message,
null, null);

}
});
}

@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.

386
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}
}
Bước 5: Ta phải khởi động hai thiết bị ảo 5554 và 5556. Ta chạy
chương trình trên thiết bị 5556 và nhấn vào nút Send SMS thì một tin
nhắn SMS có nội dung “Hello my friend!” gửi qua thiết bị 5554 như hình
13.1 bên trên.
Thực tế khi chạy ứng dụng trên một thiết bị thật, và ta muốn gửi
tin nhắn có nội dung như trên đến một thiết bị thật thì ta sẽ sửa lại số
điện thoại của thiết bị ta cần gửi đến. Ví dụ khi nhấn nút Send SMS nó
gửi tin nhắn đến số điện thoại 0909990000 tại Việt Nam ta sửa lại dòng
lệnh như sau:
sendSMS("+84909990000","Hello my friend!");
Trong đó +84 là mã quốc gia của Việt Nam.
Giải thích một chút về chương trình trên:
Android sử dụng chính sách cho phép bất cứ khi nào các sự cho
phép (permission) là cần thiết. Và sự cho phép này phải được khai báo
trong file AndroidManifest.xml. Điều này chắc chắn rằng khi ứng dụng
được cài đặt, người dùng biết chính xác sự truy cập các permission nào
được yêu cầu. Bởi vì ứng dụng dụng gửi tin nhắn SMS sẽ tốn phí nên
người dùng được thông báo cụ thể về điều này và họ quyết định có cài
hoặc không cài ứng dụng nữa.
Để gửi một tin nhắn được lập trình tự động, bạn sử dụng lớp
SmsManager. Không giống các lớp khác, bạn không phải trực tiếp khởi
tạo lớp này, thay vào đó bạn gọi phương thức tĩnh getDefaul() để có đối
tượng SmsManager.
privatevoidsendSMS(StringphoneNumber,Stringmessage)
{
SmsManagersms=SmsManager.getDefault();
sms.sendTextMessage(phoneNumber,null,message,
null,null);
}
Bạn chú ý năm đối số sau của phương thức sendTextMessage()

387
- destinestionAddress – Số điện thoại của thiết bị nhận
- scAddress– Địa chỉ trung tâm dịch vụ; sử dụng null cho SMSC
mặc định
- text – Nội dung của tin nhắn SMS
- sendIntent –Intent chờ đó để gọi ra khi tin nhắn được gửi (sẽ thảo
luận nhiều hơn ở phần tiếp theo).
- deliveryIntent –Intent chờ đó để gọi ra khi tin nhắn được chuyển
đến thiết bị nhận (sẽ thảo luận nhiều hơn ở phần tiếp theo).
Nhận phản hồi sau khi gửi tin nhắn
Trong phần trước, chúng ta đã tìm hiểu cách để lập trình gửi tin
nhắn tự động sử dụng lớp SmsManager, nhưng làm thế nào chúng ta chắc
chắn được là tin nhắn chúng ta đã được gửi đến nơi? Để làm điều này,
bạn có thể tạo hai đối tượng PendingIntentcho hai đối số cuối của
phương thức sendTextMessage. Đoạn code sau đây cho bạn thấy làm thế
nào để biết được trạng thái của tin nhắn SMS đang được gửi:
//---gửi một tin nhắn SMS đến một thiết bị khác
PrivatevoidsendSMS(StringphoneNumber,Stringmessage)
{
String SENT = “SMS_SENT”;
String DELIVERED = “SMS_DELIVERED”;
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
NewIntent(SENT), 0);
PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
NewIntent(DELIVERED), 0);
//---khi SMS đã được gửi
RegisterReceiver(newBroadcastReceiver(){
@Override
public voidonReceive(Context arg0, Intentarg1) {
switch(getResultCode())
{
CaseActivity.RESULT_OK:

388
Toast.makeText(getBaseContext(), “SMS sent”,
Toast.LENGTH_SHORT).show();
break;

CaseSmsManager.RESULT_ERROR_GENERIC_FAILURE:
Toast.makeText(getBaseContext(), “Generic failure”,
Toast.LENGTH_SHORT).show();
break;
CaseSmsManager.RESULT_ERROR_NO_SERVICE:
Toast.makeText(getBaseContext(), “No service”,
Toast.LENGTH_SHORT).show();
break;
CaseSmsManager.RESULT_ERROR_NULL_PDU:
Toast.makeText(getBaseContext(), “Null PDU”,
Toast.LENGTH_SHORT).show();
break;
CaseSmsManager.RESULT_ERROR_RADIO_OFF:
Toast.makeText(getBaseContext(), “Radio off”,
Toast.LENGTH_SHORT).show();
break;
}
}
}, newIntentFilter(SENT));
//---Khi SMS đã được gửi đến nơi
registerReceiver(newBroadcastReceiver(){
@Override
public voidonReceive(Context arg0, Intent arg1) {
switch(getResultCode())
{

389
CaseActivity.RESULT_OK:
Toast.makeText(getBaseContext(), “SMS delivered”,
Toast.LENGTH_SHORT).show();
break;
CaseActivity.RESULT_CANCELED:
Toast.makeText(getBaseContext(), “SMS not delivered”,
Toast.LENGTH_SHORT).show();
break;
}
}
}, newIntentFilter(DELIVERED));

SmsManager sms = SmsManager.getDefault();


sms.sendTextMessage(phoneNumber, null, message, sentPI,
deliveredPI);
}
Ở đây bạn tạo hai đối tượng PendingIntent. Sau đó, bạn đăng ký
cho hai BroadcastReceiver. Hai BroadcastReceiverchờ được gọi cho các
intent trùng khớp với “SMS_SENT” và “SMS_DELIVERED”(Nó sẽ
được bắn phát bởi OS (hệ điều hành) khi tin nhắn này được gửi (sent) và
được chuyển đến nơi (delivered). Với mỗi BroadcastReceiver bạn tạo
phương thức onReceive() và nhận kết quả hiện hành.
Hai đối tượng PendingIntent được đưa vào hai đối số cuối của
phương thức sendTextMessage():
sms.sendTextMessage(phoneNumber, null, message, sentPI,
deliveredPI);
Kết quả ta thấy, khi tin nhắn đã được gửi đúng, gửi đến nơi, hoặc bị
lỗi thì sẽ có các thông báo hiển thị lên màn hình.
Gửi tin nhắn SMS sử dụng Intent
Việc sử dụng lớp SmsMaganer bạn có thể gửicác tin nhắn SMS từ
ứng dụng của bạn mà không cần gọi ứng dụng Messaging (trình nhắn tin)
đã được xây dựng sẵn. Tuy nhiên, đôi lúc để đơn giản bạn có thể gọi ứng

390
dụng Messaging được xây dựng sẵn và để nó làm tất cả công việc gửi tin
nhắn này.
Để kích hoạt trình gửi tin nhắn được tích hợp từ ứng dụng của bạn,
bạn có thể sử dụng Intent kết hợp với loại MIME “vnd.android-dir/mms-
sms” như code bên dưới. Chú ý ở đâu ta xem lại ví dụ gửi tin nhắn SMS
ở ví dụ 1. Trong bài bên dưới, ta sử dụng lại giao diện gửi tin nhắn ở hình
13.1. Khi nhấn vào nút Send SMS nó sẽ gọi trình Messaging mặc định
tích hợp ra để gửi tin nhắn.
/**Được gọi khi Activity đầu tiên được gọi.*/
@Override
PublicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//tìm nhấn nhấn
btnSendSMS=(Button)findViewById(R.id.btnSendSMS);
//Thiết lập phương thức xử lý sự kiện OnClick
btnSendSMS.setOnClickListener(new
View.OnClickListener()
{ PublicvoidonClick(Viewv)
{
//sendSMS(“5556”, “Hello my friends!”);
Intent i = new

Intent(android.content.Intent.ACTION_VIEW);
i.putExtra(“address”, “5556; 5558; 5560”);
i.putExtra(“sms_body”, “Hello my friends!”);
i.setType(“vnd.android-dir/mms-sms”);
startActivity(i);
}
});
}

391
Chương trình ở trên sẽ gọi ứng dụng tin nhắn tích hợp. Chú ý là
bạn có thể gửi tin nhắn SMS của bạn đến nhiều thiết bị nhận bằng dấu;
để ngăn cách các số vơi nhau theo phương thức .putExtra()
Khi chạy chương trình ta thấy kết quả khi ta nhấn vào nút Send
SMS nó sẽ gọi trình gửi tin nhắn tích hợp trong máy để gửi đến các số
máy ta đã thiết lập với nội dung đã được lập soạn trước đó.
13.1.2. Việc nhận tin nhắn SMS
Bên cạnh việc gửi tin nhắn SMS từ các ứng dụng Android của bạn,
bạn cũng có thể nhận các tin nhắn SMS từ ứng dụng của bạn bằng việc
sử dụng đối tượng BroadcastReceiver. Điều này thì hữu ích khi bạn
muốn ứng dụng của bạn thực thi một hành động nào đó khi một tin nhắn
SMS được nhận.
Có nhiều cách để lập trình nhận một tin nhắn gửi đến. Ta sẽ khảo
sát các cách thông dụng.
Đầu tiên ta xét cách nhận tin nhắn bằng cách đăng ký
BroadcastReceiver trong MainActivity, nó sẽ lắng nghe mọi thứ trong
Intent-filter, khi đó nếu ứng dụng mở lên thì nó mới lắng nghe và có thể
xử lý tin nhắn đến, còn nếu tắt ứng dụng thì nó sẽ không còn lắng nghe
nữa và nó không thể xử lý các tin nhắn gửi đến.
Xét lại ví dụ Send SMS đã thực hiện, ta viết thêm một ứng dụng
ReceiveSMS nữa. Khi đó, ứng dụng Send SMS sẽ chạy trên một thiết bị (có
thể là máy ảo) và gửi tin nhắn đến một thiết bị khác (có thể là một máy ảo
khác). Ứng dụng ReceiveSMS sẽ được chạy trên thiết bị nhận tin nhắn.
Ứng dụng nhận tin nhấn SMS có giao diện khá đơn giản như sau:

Hình 13.2. Giao diện ứng dụng SendSMS và ReceiveSMS


392
Yêu cầu chương trình ReceiveSMS khi có tin nhắn SMS đến nó sẽ
hiển thị số điện thoại và nội dung của tin SMS lên TextView của Activity
đang chạy và đồng thời thông báo có tin nhắn đến lên màn hình.
Các bước thực hiện chương trình ReceiveSMS bằng cách đăng ký
lắng nghe ngay trong MainActivity
Bước 1: Thiết kế giao diện chương trình theo yêu cầu. Chỉnh sửa
file res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>

<TextView
android:id="@+id/noidung_tv"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignLeft="@+id/textView2"
android:layout_below="@+id/textView2"
android:layout_marginTop="25dp"
393
android:background="#16FCFF"
android:text="@string/noidung_txt" />

</RelativeLayout>

Bước 2: Đăng ký các hằng số trong file res\values\string.xml


<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<stringname="app_name">ReceiveSMS</string>
<stringname="action_settings">Settings</string>
<stringname="hello_world">Chờ nhận tin nhắn SMS</string>
<stringname="noidung_txt"></string>

</resources>

Bước 3: Cho phép ứng dụng nhận tin nhắn. Vào file
AndroidManifest.xml chỉnh lại permission. Chú ý dòng in đậm
<uses-
permissionandroid:name="android.permission.RECEIVE_SMS">

<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="nguyenvanhiep.fivemen.receivesms"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>
394
<uses-
permissionandroid:name="android.permission.RECEIVE_SMS
">
</uses-permission>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="nguyenvanhiep.fivemen.receivesms.MainActivity"
android:label="@string/app_name">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

Bước 4: Viết chương trình xử lý các sự kiện. Chú ý các dòng chú
thích để hiểu rõ chương trình. Chỉnh file
scr\nguyenvanhiep.fivemen.recievesms\MainActivity.java như sau:
package nguyenvanhiep.fivemen.receivesms;
//Khai báo các thư viện sử dụng
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

395
import android.widget.TextView;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telephony.SmsMessage;
import android.widget.Toast;

publicclass MainActivity extends Activity {

//Tạo một đối tượng BroadcastReceiver


BroadcastReceiver receiver=null;

@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//tạo bộ lọc lắng nghe khi tin nhắn gửi đến


IntentFilter filter_sms=new IntentFilter
("android.provider.Telephony.SMS_RECEIVED");

//tạo bộ lắng nghe khi tin nhắn gửi đến


receiver=new BroadcastReceiver(){

//Hàm này sẽ được thực thi khi có tin nhắn đến


Publicvoid onReceive(Context context, Intent intent){
//Tìm TextView hiển thị nội dung tin nhắn gỏi tới

396
TextView
nd=(TextView)findViewById(R.id.noidung_tv);
//chuỗi có nội dung "pdus" để nhận gói tin nhắn
String sms_extra="pdus";
//khai báo đóng goi bundle để nhận gói dữ liệu
Bundle bundle=intent.getExtras();
//đóng gói bundle trả về tập các tin nhắn gửi đến cùng lúc
Object []smsArr=(Object[]) bundle.get(sms_extra);

String sms="";
//Dung vong lap de doc tung tin nhan
for(int i=0;i<smsArr.length;i++)
{
//chuyen doi ve tin nhan creatFromPdu
SmsMessage smsMsg=SmsMessage.createFromPdu((byte[])
smsArr[i]);
//Lấy nội dung tin nhắn
String body=smsMsg.getMessageBody();
//lấy địa chỉ (số điện thoại) của thiết bị gửi tin nhắn
String address=smsMsg.getDisplayOriginatingAddress();

//Tạo chuỗi gồm nhưng thông tin tổng hợp số điện thoại và nội
dung tin nhắn
sms=sms+"Ban co mot tin nhan moi tu:\n"+ address+ "\nNoi dung
tin nhan la:"+ body;

}
//hiển thị thông tin tổng hợp lên TextView thông qua chuỗi sms
nd.setText(sms);
//Thông báo có tin nhắn mới đến lên màn hình thông qua Toast

397
Toast.makeText(getApplicationContext(), "Ban co tin nhanmoi",
Toast.LENGTH_LONG).show();
}

};
//Đăng ký bộ lắng nghe vào hệ thống
registerReceiver(receiver,filter_sms);
}

//huy bo dang ky lang nghe khi tat ung dung


protectedvoid onDestroy(){
super.onDestroy();
unregisterReceiver(receiver);

}
@Override
Publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}

}
Chạy mô phỏng chương trình. Chú ý là chương trình sendSMS phải
thiết lập số điện thoại của thiết bị nhận là thiết bị cài ứng dụng
receiveSMS. Trong mô phỏng ở đây, ứng dụng sendSMS chạy trên thiết
bị 5556:z2 ta thiết lập gửi tin nhắn đến thiết bị 5554. Tất nhiên, địa chỉ
của các máy ảo của bạn có thể khác các thiết bị trong sách tôi đang trình
bày, vấn đề ở đây tôi muốn nhắc các bạn không được nhầm lẫn thiết bị
gửi và nhận sms mà thôi. Giao diện chạy chương trình resceiveSMS khi
nhận được tin nhắn gửi đến như sau:
398
Hình 13.3. Giao diện chương trình receiveSMS khi nhận được tin nhắn

Tiếp theo ta xét cách nhận tin nhắn bằng cách đăng ký
BroadcastReceiver trong AndroidManifest.xml, nó sẽ lắng nghe mọi
thứ trong Intent-filter để xử lý khi có tin nhắn tới. Một đặc tính quan
trong khi đăng ký lắng nghe trong Manifest là nó sẽ lắng nghe mọi thứ
trong intent-filter ngay cả khi ứng dụng đã đóng. Điều này có một sự
khác biệt với cách đầu tiên ta xử lý nhận tin nhắn bằng cách đăng ký
trong file .Java. Với cách đầu tiên mọi lắng nghe sẽ kết thúc khi ứng
dụng đóng, còn với cách đăng ký trong AndroidManifest thì nó có thể xử
lý khi tin nhắn đến ngay cả khi ứng dụng đã đóng miễn là ứng dụng đã
được cài đặt trên thiết bị.
Xét ví dụ sau để hiểu rõ hơn hoạt động của việc nhận tin nhắn bằng
cách đăng ký trong AndroidManifest.
Bước 1: Tạo ứng dụng Android như sau:

399
Hình 13.4. Giao diện tạo ứng dụng ReceiveSMS_2
Ở đây ta có thể kết hợp với úng dụng SMS ta đã tạo trước đó hoặc
sử dụng trình nhắn tin mặc định để nhắn tin qua máy ảo chạy chương
trình ReceiveSMS_2. Giao diện chương trình ReceiveSMS_2 khá đơn
giản như sau:

400
Hình 13.5. Giao diện chương trình ReceiveSMS_2
Bước 2: Tạo giao diện cho ReceiveSMS_2, chỉnh sửa file
res\layout\activity_main.xml như sau:
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/r
es/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world"/>

</RelativeLayout>

401
Bước 3: Tạo thêm lớp mới, từ thư mục scr của ứng dụng Click phải
chọn New  Class và đặt tên cho lớp mới là SMSReceiver.java

Hình 13.6.Tạo class SMSReceiver.java

Bước 4: Thêm code cho file SMSReceiver.java như sau:


package nguyenvanhiep.fivemen.receivesms_2;
//Khai báo thư viện cần dùng
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;

public class SMSReceiver extends BroadcastReceiver{

@Override
publicvoid onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
402
//nhận tin nhắn SMS thông qua đóng gói bundle
Bundle bundle=intent.getExtras();
SmsMessage[] msgs=null;
//khai báo chuỗi str để chứa thông tin tổng hợp về tin nhắn
String str="";
//Nếu có tin nhắn đến
if (bundle!=null)
{
//Lấy tin nhắn đã nhận
Object[] pdus=(Object[]) bundle.get("pdus");
msgs = new SmsMessage[pdus.length];
//đọc tất cả các tin nhắn đến cùng lúc
for (int i=0;i<msgs.length;i++)
{

msgs[i]=SmsMessage.createFromPdu((byte[])pdus[i]);
str += "SMS from "+msgs[i].getDisplayOriginatingAddress();
str += ":";
str += msgs[i].getDisplayMessageBody().toString();
str += "\n";

}
//hiển thị tin nhắn SMS đến
Toast.makeText(context, str, Toast.LENGTH_LONG).show();
}
}
}

403
Bước 5: Đăng ký lắng nghe trong file AndroidManifest.xml. Lưu ý
phần in đậm.
<?xmlversion="1.0"encoding="utf-8"?>
<manifestxmlns:android="http://schemas.android.com/apk/res/and
roid"
package="nguyenvanhiep.fivemen.receivesms_2"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19"/>
<uses-
permissionandroid:name="android.permission.RECEIVE_SMS">
</uses-permission>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="nguyenvanhiep.fivemen.receivesms_2.MainActivity
"
android:label="@string/app_name">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN"/>

<categoryandroid:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

404
<receiverandroid:name="nguyenvanhiep.fivemen.receivesms_2.SM
SReceiver">
<intent-filter>
<actionandroid:name=
"android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>

</application>

</manifest>

Bước 6: Chạy mô phỏng ứng dụng. Khi mở ứng dụng


ReceiveSMS_2 lên, nếu có tin nhắn gửi đến nó sẽ hiển thị lên thông báo
về số điện thoại và nội dung tin nhắn đến như sau:

Hình 13.7. Giao diện khi nhận tin nhắn nếu ứng dụng ReceiveSMS_2
đang mở
Nếu tắt ứng dụng ReceiveSMS_2 nhưng do ta đã đăng ký trong
AndroidManifest nên khi tin nhắn đến nó vẫn xử lý và kết quả như sau:

405
Hình 13.8. Giao diện khi nhận tin nhắn nếu ứng dụng ReceiveSMS_2
đóng
Ứng dụng trên đã hoạt động như thế nào?
Để lắng nghe những tin nhắn SMS đang đến, bạn có thể tạo một
lớp BroadcastReceiver. Lớp BroadcastReceiver cho phép ứng dụng của
bạn nhận những intent được gửi đến bởi các ứng dụng khác sử dụng
phương thức sendBroadcast(). Về cơ bản nó cho phép ứng dụng của
bạn xử lý các sự kiện phát sinh bởi các ứng khác. Khi một intent được
nhận, phương thức onReceive() được gọi vì vậy bạn cần viết hàm xử lý
điều này.
Khi một tin nhắn SMS được nhận, phương thức onReceive() được
gọi ra. Tin nhắn SMS được chứa trong đối tượng Intent (intent: một
thông số thứ hai trong phương thức onReceive()) thông qua đối tượng
Bundle. Các tin nhắn được lưu trữ trong một mảng đối tượng Object
trong định dạng PDU. Để tách ra mỗi tin nhắn, bạn sử dụng phương thức
tĩnh createFromPdu() từ lớp SmsMessage. Tin nhắn SMS sau đó được
hiển thị sử dụng lớpToast. Số điện thoại của người gửi được lấy thông
qua phương thức getOriginatingAddress(). Vì vậy, nếu bạn cần gửi một
tin nhắn tự động ngược lại người gửi bạn có thể sử dụng thông tin này.
Một đặc tính thú vị của BroadcastReceiver là bạn có thể tiếp tục
lắng nghe tin nhắn đang đến ngay cả khi ứng dụng không đang được
chạy miễn là ứng dụng đã được cài đặt trên thiết bị của bạn và khi đó bất
kỳ tin nhắn nào đến đều được nhận bởi ứng dụng của bạn.

406
13.2. GỬI EMAIL
Giống như trình nhắn tin SMS, Android cũng hỗ trợ thư điện tử (e-
mail). Ứng dụng Gmail/Email trên Android cho phép bạn cấu hình tài
khoản e-mail sử dụng POP3 hoặc IMAP. Bên cạnh việc gửi và nhận
email sử dụng ứng dụng Gmail/Email, bạn cũng có thể lập trình gửi các
email một cách tự động theo các ứng dụng Android của riêng bạn. Theo
dõi ví dụ sau để hiểu rõ hơn:
Thiết kế giao diện chương trình như sau:

Hình 13.9. Giao diện ứng dụng gửi Email


<LinearLayoutxmlns:android="http://schemas.android.com/apk/res
/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
407
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/hello_world"/>

<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="74dp"
android:text="@string/send_btn"/>

</LinearLayout>

- Định nghĩa các hằng số trong file res\values\String.xml


<?xmlversion="1.0"encoding="utf-8"?>
<resources>

<string name="app_name">SendEmail</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Đây là ứng dụng về gửi
Email</string>
<string name="send_btn">Send Email</string>

</resources>

408
- Viết code trong file MainActivity.java
package nguyenvanhiep.fivemen.sendemail;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;

import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {


Button btnSendEmail;

@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

btnSendEmail=(Button)findViewById(R.id.button1);
btnSendEmail.setOnClickListener(new
View.OnClickListener() {

@Override
publicvoid onClick(View v) {

String[]
to={"thewind030282@gmail.com","sunlight@yahoo.com"};
String[] cc={"rainy@gmail.com"};
409
sendEmail(to, cc, "Xin chao", "Ban co khoe khong?");

Privatevoid sendEmail(String[] emailAddresses, String[]


carbonCopies, String subject,String message) {
// TODO Auto-generated method stub
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setData(Uri.parse("mailto:"));
String[] to = emailAddresses;
String[] cc=carbonCopies;
emailIntent.putExtra(Intent.EXTRA_EMAIL, to);
emailIntent.putExtra(Intent.EXTRA_CC, cc);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, message);
emailIntent.setType("message/rfc822");
startActivity(Intent.createChooser(emailIntent, "Email"));

}
});
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is
present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
- Debug chương trình trên thiết bị thật (Sony Xperia Z1) ta có kết
quả như sau:
410
Hình 13.10. Giao diện chương trình khi debug trên thiết bị thật
Giải thích chương trình:
Trong ví dụ này, bạn gọi một ứng dụng Emai được xây dựng sẵn để
gửi một email của bạn. Để làm điều này, bạn sử dụng đối tượng Intent và
thiết lập cho nó với các thông số khác nhau sử dụng các phương thức
setData(), putExtra(), và setType()
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setData(Uri.parse("mailto:"));
String[] to = emailAddresses;
String[] cc=carbonCopies;
emailIntent.putExtra(Intent.EXTRA_EMAIL, to);
emailIntent.putExtra(Intent.EXTRA_CC, cc);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, message);
emailIntent.setType("message/rfc822");
startActivity(Intent.createChooser(emailIntent, "Email"));
411
TÀI LIỆU THAM KHẢO

[1] Begin Android Application Development, Wei-Meng Lee, Wiley


Publishing, Inc, 2011
[2] Trang học trực tuyến http://tutorialspoint.com/
[3] Trang học trực tuyến http://duythanhcse.wordpress.com/

412
GIÁO TRÌNH NHÀ XUẤT BẢN
LẬP TRÌNH ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH
Khu phố 6, Phường Linh Trung, Quận Thủ Đức, TP Hồ Chí Minh
ANDROID CƠ BẢN Số 3, Công trường Quốc tế, Quận 3, TP Hồ Chí Minh
ĐT: 38239171 - 38225227 - 38239172
ThS. NGUYỄN VĂN HIỆP Fax: 38239172
KS. ĐINH QUANG HIỆP E-mail: vnuhp@vnuhcm.edu.vn

PHÒNG PHÁT HÀNH


Số 3, Công trường Quốc tế, Quận 3, TP Hồ Chí Minh
ĐT: 38239170 - 0982920509 - 0913943466
Fax: 38239172 - Website: www.nxbdhqghcm.edu.vn

Nhà xuất bản ĐHQG-HCM và tác Chịu trách nhiệm xuất bản:
©
giả/đối tác liên kết giữ bản quyền NGUYỄN HOÀNG DŨNG
Copyright © by VNU-HCM
Publishing House and author/co-
partnership Chịu trách nhiệm nội dung:
All rights reserved NGUYỄN HOÀNG DŨNG

Tổ chức bản thảo và chịu trách nhiệm về tác quyền


TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TPHCM

Xuất bản năm 2015 Biên tập:


HOÀNG KHẮC THỦY

Sửa bản in:


MINH TUẤN

Trình bày bìa:


TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TPHCM

Số lượng 300 cuốn,


Khổ: 16x24 cm,
GIÁO TRÌNH
ĐKKHXB số: 2802-2014/CXBIPH/ LẬP TRÌNH ANDROID CƠ BẢN
11-178/ĐHQGTPHCM,
NXB ĐHQG-HCM

Quyết định XB số: 13/QĐ


của NXB ĐHQG-HCM
cấp ngày 30-1-2015.
In tại: Cty TNHH
In và Bao bì Hưng Phú
Địa chỉ: 162A/1 KP1A, P. An Phú, ISBN: 978-604-73-3048-5
TX. Thuận An, Tỉnh Bình Dương
Nộp lưu chiểu: Quý I năm 2015
ISBN: 978-604-73-3048-5

9 786047 330485

You might also like