
Được chỉnh sửa ngày 18/03/2019.
Chào mừng các bạn đã đến với bài học Android thứ 7, bài học về cách tạo giao diện người dùng trên Android. Đây là bài học trong chuỗi bài viết về lập trình ứng dụng Android bằng Java của Yellow Code Books.
Bài hôm nay chúng ta sẽ nói về Giao Diện Người Dùng (Graphical User Interface – GUI), một thành phần quan trọng trong ứng dụng của các bạn. Nhắc lại một chút rằng, ở bài 5 chúng ta có nói qua các Thành Phần Của Ứng Dụng (Application Component), nhưng có bạn nào thắc mắc rằng giao diện sẽ được xây dựng như thế nào dựa trên các Thành Phần này không? Nếu bạn để ý kỹ bài học, thì mình có nói rằng trong 4 Thành Phần đó (Activities, Services, Content Providers và Broadcast Receiver) thì chỉ có duy nhất Activities là hiển thị giao diện cho người dùng. Vậy bài này các bạn sẽ được biết cách thức xây dựng giao diện cho một Activity. Activity là gì thì chúng ta sẽ tìm hiểu sau, điều các bạn cần quan tâm bây giờ là bạn cần hiểu Activity chính là cái màn hình mà bạn thực thi ứng dụng lên từ bài học số 4, màn hình in ra mỗi một dòng chữ “Hello World”. Và giao diện của nó gắn liền với một class có tên MainActivity.java.
Trước khi chính thức đi vào thực hành xây dựng giao diện, chúng ta nói qua một số khái niệm sau, chúng là những viên gạch cơ bản đầu tiên để xây dựng nên một “công trình” giao diện hoàn chỉnh, chúng hoành tráng hay nghèo nàn, kiên cố hay xiêu vẹo là ở những viên gạch cơ bản này đấy.
Khái Niệm Widget
Bạn ít khi nghe nói đến Widget trong giao tiếp thông thường, nhất là khi trao đổi tiếng Việt giữa các lập trình viên với nhau. Nhưng trong ngôn ngữ lập trình, Widget có khái niệm rõ ràng và được sử dụng trong các tài liệu tiếng Anh đấy nhé.
Vậy thì, Widget là một thành phần của Giao Diện Người Dùng, Widget giúp hiển thị các thông tin được sắp xếp, và có thể chỉnh sửa được tới người dùng. Mỗi Widget sẽ mang một kiểu thông tin nhất định, tập hợp chúng lại với nhau chúng ta tạo nên một Giao Diện Người Dùng. @@
Đọc các dòng trên có thể khiến bạn bị lùng bùng lỗ tai, nhưng thực ra Widget rất dễ hiểu, nó chính là cái Textbox, cái Label, cái Button,… trên ứng dụng của bạn mà thôi. Để dễ hiểu hơn bạn hãy nhìn vào hình ảnh rất đẹp của một ứng dụng sau. ^^

Bạn có thể thấy:
– Một số text, như Thêm Ghi Chú, Chủ Đề
– Một số chỗ để người dùng nhập text của họ vào, như Tiêu Đề, Miêu Tả, Ghi Chú
– Một số check box, như Ăn Uống, Tham Quan, Mua Sắm
– Một số nút, như nút +, nút x
– Một số nơi để hiển thị hình ảnh
– …
Tất cả những thành phần được liệt kê ở trên đều là các Widget cả đấy, chúng được sắp đặt ở các vị trí cụ thể trên màn hình, mỗi Widget đảm nhận các vai trò khác nhau đến với sự tương tác với người dùng. Bạn thấy có dễ hiểu hơn không nào. Chúng ta sẽ làm quen với từng Widget ở bài học sau nhé, còn bây giờ hãy qua khái niệm kế tiếp.
Khái Niệm View
Bạn đã hiểu Widget là gì rồi, giờ ta sẽ tìm hiểu về khái niệm View. View thực chất là nguồn gốc để tạo nên các Widget được nói đến ở trên.
View được hiểu như là các cấu tạo giống nhau, hay nói cách khác nó là các khuôn mẫu cơ bản để làm nền tảng tạo nên Widget. Nó tương tự như khái niệm về lập trình trong OOP ấy, trong đó View là class còn các Widget là các object được tạo ra từ class (View) đó. Do đó trong khi ở View chỉ có các thuộc tính chung nhất, thì Widget là các thể hiện khác nhau của View. Mỗi Widget chứa đựng các thuộc tính đặc thù của nó mà các Widget khác không có. Chẳng hạn Button sẽ có các thuộc tính khác với Label, Checkbox sẽ khác Radio Button,...
Khái Niệm ViewGroup
ViewGroup cũng gần như là View, nhưng đặc biệt hơn một chút, đó là, ViewGroup được tạo ra để chứa đựng các View hay các ViewGroup khác. Nếu như View là nguồn gốc để tạo ra các Widget, thì ViewGroup lại là nguồn gốc để tạo ra các Layout. Các layout mà chúng ta sẽ sớm làm quen, như ConstraintLayout, LinearLayout, RelativeLayout, FrameLayout, GridLayout,… Mỗi layout như một Thùng Chứa (Container) để chứa đựng các View hay các ViewGroup khác bên trong mình, các thành phần bên trong này được sắp xếp theo một quy tắc nhất định tùy theo từng loại ViewGroup.
Kết Hợp View Và ViewGroup Để Tạo Nên Giao Diện
Với khái niệm View và ViewGroup ở trên, các bạn có thể thấy rằng mối quan hệ của hai thành phần này giúp tạo thành một giao diện người dùng có bản chất là một cây phân cấp. Cây phân cấp này bao gồm các View và ViewGroup, chúng được sắp xếp hay lồng vào nhau theo những quy tắc nhất định. Như hình minh họa dưới đây.

Chỉnh Sửa Giao Diện Cho TourNote
Woohooo!!! Lý thuyết ở trên nặng đầu quá, nếu bạn chưa hiểu gì cả thì cũng không sao. Giờ chúng ta nên làm quen với thực hành tạo giao diện. Có một điều chúng ta phải thống nhất với nhau trước khi đi vào chi tiết thực hành, đó là việc xây dựng giao diện cho ứng dụng Android sẽ làm bằng cách kết hợp cả hai phương pháp, là “kéo thả”, và code. Kéo thả có nghĩa là bạn có được công cụ cần thiết để chỉ việc kéo các widget có sẵn vào màn hình thiết kế, việc kéo thả này sẽ làm phát sinh code XML. Còn code có nghĩa là bạn phải biết cách nhìn vào các dòng chữ được sắp xếp theo chuẩn XML sau các thao tác kéo thả của bạn. Việc kéo thả giao diện thì rất trực quan rồi, lát nữa đến bài thực hành bạn sẽ thấy nó dễ đến mức nào. Còn việc nhìn vào code XML là vì nhiều khi công cụ kéo thả không giúp bạn có thể đạt được mong muốn cuối cùng của giao diện, hoặc có bất kỳ lỗi nào đối với công cụ này mà bạn hoàn toàn không có khả năng sửa chữa nó bằng các phương pháp kéo thả, ngoại trừ nhìn vào code. Ngoài ra, khi nhìn vào code, các bạn còn có thể thấy sự kết hợp các View và ViewGroup như cây phân cấp ở trên đã trình bày, giúp bạn có sự hình dung rõ ràng nhất về việc giao diện của bạn tạo ra nó được xây dựng dựa trên cơ sở gì.
Đầu tiên, để đảm bảo chắc chắn code của bạn sẽ thực thi lên được (vì bài trước bạn đã code một dòng làm cho ứng dụng bị crash để trải nghiệm), thì bạn nên xóa các dòng đã code từ bài trước đó đi nhé, bảo đảm class MainActivity.java “sạch” giống như sau.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Giờ bạn hãy tìm đến file activity_main.xml và click đúp để mở file này lên, đây chính là giao diện của class ActivityMain.java. Nhắc lại là nếu không biết mở class này ở đâu, bạn hãy tìm trong cửa sổ Project ở bên trái màn hình. File activity_main.xml sẽ xuất hiện ở cửa sổ Project như một trong hai hình sau, tùy vào cách thức hiển thị là Android hay Project.
Cửa sổ của file activity_main.xml được mở lên sẽ rất khác với lúc bạn mở ActivityMain.java hôm trước. Vì mình cũng đã có nói đến ở Bài 3, rằng cửa sổ editor này rất đặc biệt, nó sẽ hiển thị nội dung theo đúng loại file đã mở, trong trường hợp này bạn đang mở file giao diện XML nên Android Studio sẽ hiển thị theo kiểu thiết kế giao diện như sau.
Giao diện ở cửa sổ này có thể khác với giao diện ở máy bạn một chút tùy vào version của Android Studio. Các bạn có thể chú ý ở phía dưới cửa sổ này có 2 tab: Design và Text (như hình dưới).
Mặc định cửa sổ này hiển thị chế độ Design, tức là các bạn có thể kéo-thả giao diện, hệ thống sẽ sinh ra code XML một cách tự động cho bạn. Để hiểu hơn về cửa sổ Design này, và cách kéo thả sao cho ra một giao diện Android, thì bạn có thể xem trước loạt bài riêng về ContraintLayout này của mình nhé.
Và như mình nói, chúng ta phải học cách làm quen việc kéo thả giao diện, và cả cách code XML cho giao diện Android nữa. Nên bạn hãy đừng ngần ngại, hãy thử nhấn vài tab Text xem sao.
Một lát nữa đây chúng ta sẽ thực hành với việc kéo thả giao diện. Nhưng trước khi thực hành chuyện đó, chúng ta nên xem qua một chút cấu tạo của XML ở bước này, để khi kết thúc việc kéo thả, chúng ta sẽ cùng nhau so sánh lại một lần nữa sự thay đổi ở XML nhé.
Giải Thích Một Chút Giao Diện XML
Bạn hãy chú ý dòng đầu tiên.
<?xml version="1.0" encoding="utf-8"?>
Bạn nên biết rằng đây không phải khái niệm của Android, nên bạn không cần quan tâm cũng được. Đây là định nghĩa của XML mà thôi, nó thông báo rằng cấu trúc bạn đang dùng là XML version 1.0, và text được mã hóa theo định dạng utf-8.
Đoạn kế tiếp.
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
...
</android.support.constraint.ConstraintLayout>
Đoạn này cho thấy rằng giao diện đang được khai báo với thẻ gốc là ConstraintLayout. Lưu ý là nếu bạn nào chưa rành về XML được tổ chức như thế nào, thì có thể tìm hiểu thêm trên mạng. Rất nhiều bài viết mô tả rất chi tiết về cấu trúc XML đấy nhé, đơn cử như bài viết này đây.
Quay lại đoạn XML trên đây. Như bài học có nói, ConstraintLayout là một ViewGroup, do đó bên trong nó có thể chứa các View hoặc ViewGroup khác. Như trường hợp trên, TextView chính là View, thành phần này nằm bên trong ViewGroup ConstraintLayout. Ngoài ra, sở dĩ bạn thấy ConstraintLayout được viết hơi bị dài dòng, android.support.constraint.ConstraintLayout là vì hệ thống nêu đích danh nơi chứa ConstraintLayout này thôi, bạn đừng quan tâm cũng đừng chỉnh sửa gì hết nhé.
Các thuộc tính bên trong thẻ ConstraintLayout hay các thẻ khác xuất hiện trong giao diện hôm nay sẽ được mình trình bày riêng ở bài học khác. Hôm nay bạn hiểu sơ như thế này thôi.
Thực Hành Chỉnh Sửa Giao Diện Cho TourNote
Bây giờ chúng ta sẽ chính thức chỉnh sửa giao diện cho TourNote nhé. Vẫn ở màn hình của main_activity.xml, bạn hãy chọn lại tab Design.
Sau đó đảm bảo chữ “Hello World!” đang được chọn ở màn hình trực quan thiết kế trong cửa sổ này. Bạn có thể tham khảo hình sau.
Chữ “Hello World!” mà bạn đang chọn đó là một TextView. TextView là gì ư? Đây là một widget giúp chúng ta hiển thị các text đến với người dùng. Text mà TextView này đang hiển thị chính là “Hello World!” đó. Giờ chúng ta sẽ thay đổi text này. Từng Widget cụ thể cũng sẽ được mình nói ở các bài học riêng lẻ luôn nhé. Với TextView đang được chọn, nhìn vào bên phải cửa sổ editor bạn thấy có một cửa sổ nhỏ có tên Attributes. Cửa sổ Attributes sẽ giúp bạn thay đổi các thuộc tính của các View hay ViewGroup mà bạn đang chọn.
Nhìn xuống phía dưới Attributes, bạn sẽ thấy một field có tên là text. Trong field text này có đang hiển thị sẵn text “Hello World”. Hãy thay thế text này thành text “Bạn chưa có ghi chú nào cho mình, hãy tạo mới ghi chú từ hôm nay”. Như hình sau.
Khi bạn sửa chữa nội dung cho TextView ở cửa sổ Attributes, bạn thấy trực quan màn của ứng dụng cũng thay đổi theo ở bên phải Attributes này, thật thuận tiện đúng không nào.
Tuy nhiên, cách hiển thị text ở màn hình ứng dụng chưa được đẹp lắm. Bạn mong muốn chúng được canh giữa màn hình cơ. Bạn hãy làm tiếp bước dưới đây.
Cuộn xuống đến cuối Attributes, bạn sẽ thấy dòng chữ View all attributes.
Nhấn vào dòng chữ này hệ thống sẽ dẫn bạn đến một danh sách đầy đủ hơn các thuộc tính của TextView. Bạn thấy đấy, một cái TextView thôi mà có rất nhiều thuộc tính cho nó đúng không. Bạn có thể nhìn sơ qua danh sách các thuộc tính này, chúng cũng rất dễ hiểu thôi.
Hãy cuộn tìm trong các thuộc tính này một thuộc tính quan trọng, đó là gravity. Hãy nhấn vào hình tam giác bên trái chữ gravity này để bung ra nhiều tùy chọn nhỏ hơn nữa. Khi này bạn hãy check chọn vào thành phần center bên trong gravity này nhé. Bạn có thể xem hình dưới đây để hiểu rõ hơn.
Nếu bạn chưa biết gravity là gì thì có thể xem tiếp các bài học kế. Nhưng như bạn thấy đấy, sau khi check chọn vào center, bạn thấy ngay text ở TextView đã được canh chỉnh vào giữa rồi đúng không nào.
Giờ thì bạn hãy nhấn lại tab Text của màn hình activity_main.xml để xem sự thay đổi về code XML nhé. Bạn có thấy dòng code nào xuất hiện thêm không. Để mình chỉ ra cho bạn xem bằng dòng lệnh được tô sáng như sau. Bài sau chúng ta sẽ tìm hiểu một chút về các dòng code XML này.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Bạn chưa có ghi chú nào cho mình, hãy tạo mới ghi chú từ hôm nay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Run Ứng Dụng
Lúc này bạn hãy thử run ứng dụng TourNote để xem thành quả nhé.
Download Source Code Mẫu
Bạn có thể download source code mẫu ở đây.
Bạn vừa cùng mình thử thiết kế giao diện ở mức cơ bản cho ứng dụng TourNote, chúng ta sẽ đi sâu hơn về thiết kế giao diện ở các bài tiếp theo.
Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao bên dưới mỗi bài nếu thấy thích.
– Comment bên dưới mỗi bài nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
Bài Kế Tiếp
Bạn sẽ được học cách quản lý các resource của ứng dụng, từ đó bạn biết cách làm sao có thể xây dựng một ứng dụng chạy được trên tất cả các kích thước màn hình khác nhau, bao gồm cả việc hỗ trợ màn hình xoay ngang/dọc, và còn hỗ trợ nhiều ngôn ngữ nữa.
Reblogged this on gioilaptrinh.
Good Lesson!!!
ad ơi sao của mình báo lỗi cannot resolve symbol @dimen…? Làm ơn xem giúp. Cảm ơn ad
Bạn chat vào facebook https://www.facebook.com/yellowcodebooks/ và gởi kèm hình ảnh cho mình nhé, mình chưa hình dung ra là bạn đang bị lỗi gì cả 😉
Cảm ơn Ad đã phản hồi. Mình đã google và tìm ra lỗi rồi.
Mình chia sẻ để ae học viên mới như mình khắc phục ạ:
Do Adroid studio mới nên nó mặc định layout constrain trong khi sample của ad là relative. Layout relative có sẵn file dimens.xml nhưng constrain ko có nên bị lỗi ko tìm đc file dimens. Mình đã khắc phục bằng cách tạo file dimens.xml trong res/value (chọn new/Value resources file) rồi nội dung file đó thì thêm
3dp
3dp
vậy là chạy đc.
3dp
3dp
Cảm ơn bạn đã tìm ra lỗi và giúp post cho các bạn khác cùng biết. Mình rất thích các phản hồi tích cực như thế này. Tương lai mình sẽ điều chỉnh lại các bài học để theo kịp các cập nhật của Android hơn. Vì nền tảng này thay đồi nhanh quá. Mong các bạn thông cảm nhé.