Professional Documents
Culture Documents
GIÁO TRÌNH
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)
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
4
MỤC LỤC
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
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
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.
12
iOS là hệ điều hành trên các thiết bị di động của Apple.
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
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.
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.5. Sự chênh lệch về dung lượng ứng dụng giữa Dalvik và ART
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
37
Hình 3.6. Phần mềm jdk-u21 (java) sau khi tải về
38
Chọn next
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
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
43
Tìm đến thư mục lưu file ADT đã tải về.
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.
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.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
<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;
}
}
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.
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 n - 1
Activity 2
Activity 1
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.
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
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>[];
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,
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:
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:
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
95
Hình 4.20. Biên dịch ứng dụng
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
98
Hình 4.24. Chọn tùy chọn thiết bị debug
Ở 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:
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:
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.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:
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:
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
nó
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
nó
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
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.
<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>
<string name="app_name">GUITextView1</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
</resources>
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.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:
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"/>
<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 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)
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.
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;
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:
151
Gõ vào vùng AutoCompleteTextView ký tự “H” ta có giao diện
như sau:
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).
<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>
@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){
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
<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;
162
//Tạo một chương trình con xử lý sự kiện cho ImageButton
addLisenerOnButton();
}
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:
<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>
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;
@Override
168
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//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();
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) {
}
});
}
//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:
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
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
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>
<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;
@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);
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();
}
});
}
@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)
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.
<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>
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;
RadioGroup rdg;
RadioButton rdbtchon;
Button btchon;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
addListennerOnBieuquyet();
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;
}
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.
<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.
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ử
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.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;
@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;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
204
lstDanhSach = (ListView)
findViewById(R.id.lstSinhVien);
@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("");
}
}
});
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;
215
this.Lop = Lop;
}
216
}
}
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;
@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;
@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;
}
});
}
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
<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;
225
private Context context;
// Mảng chứa ảnh sẽ hiển thị trên gallery
Privateint[] 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 {
@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:
231
Màn hình ứng dụng:
@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.
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:
<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;
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:
<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;
@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
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
<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>
<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;
@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:
258
Hình 9.3. Kết quả khi nhấn nút giảm font
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;
@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);
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:
<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>
<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;
@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:
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
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);
}
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
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:
<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>
<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>
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);
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;
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.
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>
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>
295
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tieptuc=(Button) findViewById(R.id.button1) ;
chieucao=(EditText)findViewById(R.id.editText2);
cannang=(EditText)findViewById(R.id.editText1);
@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());
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);
}
});
@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;
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;
299
tket.append("Chỉ số BMI của bạn là:
").append(BMI).append("\n").append(kq);
@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.
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.
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:
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 đồ”
<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;
@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;
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.
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ữ đó.
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:
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 {
@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.
349
private Button btnHienThi, btnReadInternal, btnWriteInternal;
private EditText edTenBaiHat, edLoiBaihat;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@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:
@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.
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:
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
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):
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:
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.
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"/>
<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!");
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));
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:
<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>
<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;
@Override
Protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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);
}
}
@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
@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>
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:
<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>
<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;
@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?");
}
});
}
@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
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
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
9 786047 330485