Android cung cấp một mô hình được sắp xếp thành phần tinh vi và mạnh mẽ để xây dựng giao diện người dùng, dựa trên
các lớp bố cục cơ bản
View
và
ViewGroup
Nền tảng này bao gồm
nhiều lớp con View
và ViewGroup
được tạo sẵn, được gọi là tiện ích và
mà bạn có thể dùng để tạo giao diện người dùng.
Một phần danh sách các tiện ích widget có sẵn bao gồmButton
,
TextView
,
EditText
,
ListView
,
CheckBox
,
RadioButton
,
Gallery
Spinner
,
cũng như AutoCompleteTextView
với mục đích đặc biệt hơn,
ImageSwitcher
, và
TextSwitcher
.
Trong số các bố cục có sẵn có
LinearLayout
,
FrameLayout
,
RelativeLayout
,
và các nguồn khác. Để biết thêm ví dụ, hãy xem
Bố cục phổ biến.
Nếu không có tiện ích hoặc bố cục tạo sẵn nào đáp ứng được nhu cầu của mình, bạn có thể tạo tiện ích hoặc bố cục tạo sẵn của riêng mình
Lớp con View
. Nếu bạn chỉ cần thực hiện những điều chỉnh nhỏ đối với tiện ích hiện có hoặc
, bạn có thể tạo lớp con cho tiện ích hoặc bố cục và ghi đè phương thức của tiện ích hoặc bố cục đó.
Việc tạo lớp con View
riêng giúp bạn kiểm soát chính xác giao diện và
chức năng của một thành phần màn hình. Để cho bạn biết về thành phần điều khiển mà bạn sẽ có với các thành phần hiển thị tuỳ chỉnh, dưới đây là
một số ví dụ về những việc bạn có thể làm với chúng:
-
Bạn có thể tạo một loại
View
được kết xuất hoàn toàn tuỳ chỉnh, ví dụ: "volume" kiểm soát" núm vặn, được kết xuất bằng đồ hoạ 2D, giống với bộ điều khiển điện tử analog. -
Bạn có thể kết hợp một nhóm thành phần
View
thành một thành phần mới, có thể là để tạo một thứ gì đó như hộp kết hợp (kết hợp danh sách bật lên và trường văn bản nhập tự do), tuỳ chọn điều khiển bộ chọn hai ngăn (ngăn bên trái và bên phải có danh sách ở mỗi ngăn để bạn có thể chỉ định lại mục nào nằm trong danh sách nào), v.v. -
Bạn có thể ghi đè cách kết xuất thành phần
EditText
trên màn hình. Chiến lược phát hành đĩa đơn Ứng dụng mẫu NotePad sử dụng điều này để tạo hiệu ứng tốt nhằm tạo một trang sổ tay có dòng kẻ. - Bạn có thể ghi lại các sự kiện khác (như các lần nhấn phím) và xử lý các sự kiện đó theo cách tuỳ chỉnh, chẳng hạn như đối với trò chơi.
Các phần sau đây giải thích cách tạo khung hiển thị tuỳ chỉnh và cách sử dụng các khung hiển thị đó trong ứng dụng của bạn. Cho
thông tin tham khảo chi tiết, hãy xem
Lớp View
.
Phương pháp cơ bản
Sau đây là thông tin tổng quan cấp cao về những điều bạn cần biết để tạo View
của riêng mình
thành phần:
-
Mở rộng một lớp hoặc lớp con
View
hiện có bằng lớp của riêng bạn. -
Ghi đè một số phương thức từ lớp cha. Các phương thức của lớp cấp cao cần ghi đè bắt đầu bằng
on
—ví dụ:onDraw()
,onMeasure()
, vàonKeyDown()
. Điều này tương tự như các sự kiệnon
trongActivity
hoặcListActivity
mà bạn cơ chế ghi đè cho các vòng đời và các hook khác về chức năng. - Sử dụng lớp mở rộng mới của bạn. Sau khi hoàn tất, bạn có thể sử dụng lớp tiện ích mới thay cho dựa trên chế độ xem nào.
Thành phần được tuỳ chỉnh toàn bộ
Bạn có thể tạo các thành phần đồ hoạ hoàn toàn tuỳ chỉnh xuất hiện theo cách bạn muốn muốn. Có thể bạn cần một đồng hồ VU dạng đồ hoạ trông giống như một đồng hồ đo kim loại cũ hoặc chế độ xem văn bản hát theo khi một quả bóng nảy lên di chuyển theo lời khi bạn hát theo máy karaoke. Bạn có thể muốn điều mà các thành phần tích hợp sẵn không làm được, cho dù bạn kết hợp chúng theo cách nào.
May mắn là bạn có thể tạo các thành phần có giao diện và hoạt động theo cách bạn muốn, chỉ có giới hạn theo trí tưởng tượng, kích thước màn hình và khả năng xử lý có sẵn, hãy lưu ý rằng ứng dụng của bạn có thể phải chạy trên thiết bị nào đó có công suất thấp hơn đáng kể so với máy tính để bàn của bạn máy trạm.
Để tạo một thành phần được tuỳ chỉnh hoàn toàn, hãy cân nhắc những việc sau:
-
Chế độ xem chung nhất mà bạn có thể mở rộng là
View
, vì vậy, bạn thường bắt đầu bằng cách mở rộng để tạo siêu thành phần mới. - Bạn có thể cung cấp một hàm khởi tạo có thể lấy các thuộc tính và tham số từ XML, đồng thời bạn có thể sử dụng các thuộc tính và tham số đó của riêng bạn, chẳng hạn như màu sắc và phạm vi của đồng hồ VU hoặc chiều rộng và độ giảm chấn của kim.
- Bạn nên tạo trình nghe sự kiện, trình truy cập thuộc tính và đối tượng sửa đổi của riêng mình cũng như hành vi tinh vi hơn trong lớp thành phần.
-
Bạn gần như chắc chắn muốn ghi đè
onMeasure()
và có thể cũng cần phải ghi đèonDraw()
nếu bạn muốn thành phần này hiển thị nội dung nào đó. Mặc dù cả hai đều có hành vi mặc định,onDraw()
mặc định không làm gì cả và giá trị mặc địnhonMeasure()
luôn đặt kích thước 100x100 mà có thể bạn không muốn. -
Nếu cần, bạn cũng có thể ghi đè các phương thức
on
khác.
Mở rộng onDraw() và onMeasure()
Phương thức onDraw()
cung cấp một
Canvas
mà bạn có thể
triển khai mọi thứ bạn muốn: đồ hoạ 2D, các thành phần tiêu chuẩn hoặc tuỳ chỉnh khác, văn bản được tạo kiểu, hoặc
bất kỳ nội dung nào khác mà bạn có thể nghĩ ra.
onMeasure()
có liên quan nhiều hơn một chút. onMeasure()
là một phần quan trọng
hợp đồng hiển thị giữa thành phần và vùng chứa của thành phần đó. onMeasure()
phải là
được ghi đè để báo cáo hiệu quả và chính xác số liệu đo lường của các phần chứa trong đó. Đây là
thực hiện phức tạp hơn một chút bởi các yêu cầu giới hạn từ cấp độ gốc — được chuyển vào
onMeasure()
và theo yêu cầu gọi phương thức
Phương thức setMeasuredDimension()
với chiều rộng và chiều cao đo được sau khi
được tính. Nếu bạn không gọi phương thức này từ một phương thức onMeasure()
bị ghi đè, thì
sẽ dẫn đến ngoại lệ tại thời điểm đo.
Ở cấp độ cao, việc triển khai onMeasure()
sẽ có dạng như sau:
-
Phương thức
onMeasure()
bị ghi đè được gọi với chiều rộng và chiều cao được coi là yêu cầu đối với các hạn chế về chiều rộng và chiều cao mà bạn tạo ra.widthMeasureSpec
vàheightMeasureSpec
tham số đều là mã số nguyên đại diện cho các phương diện. Tài liệu tham khảo đầy đủ về loại các hạn chế mà các thông số kỹ thuật này có thể yêu cầu có thể được tìm thấy trong tài liệu tham khảo trongView.onMeasure(int, int)
Tài liệu tham khảo này cũng giải thích toàn bộ thao tác đo lường. -
Phương thức
onMeasure()
của thành phần tính toán chiều rộng và chiều cao đo lường, bắt buộc để hiển thị thành phần. Ứng dụng phải cố gắng đáp ứng các thông số kỹ thuật đã được thông qua mặc dù số lần hiển thị có thể vượt quá giới hạn. Trong trường hợp này, cha mẹ có thể chọn những việc cần làm, bao gồm cắt đoạn, cuộn, loại bỏ một trường hợp ngoại lệ hoặc yêu cầuonMeasure()
thử lại, có thể với các thông số đo lường khác nhau. -
Khi chiều rộng và chiều cao được tính toán, hãy gọi phương thức
setMeasuredDimension(int width, int height)
với giá trị đã tính thiết bị đo lường. Không thực hiện được điều này sẽ dẫn đến một ngoại lệ.
Dưới đây là bản tóm tắt các phương thức chuẩn khác mà khung này gọi trên khung hiển thị:
Danh mục | Phương pháp | Mô tả |
---|---|---|
Tạo | Hàm khởi tạo | Có một dạng hàm khởi tạo được gọi khi khung hiển thị được tạo bằng mã và một biểu mẫu được gọi khi khung hiển thị được tăng cường từ tệp bố cục. Dạng thức thứ hai phân tích cú pháp và áp dụng các thuộc tính được xác định trong tệp bố cục. |
|
Được gọi sau khi một khung hiển thị và tất cả các khung hiển thị con đều được tăng cường từ XML. | |
Bố cục |
|
Được gọi để xác định các yêu cầu về kích thước cho thành phần hiển thị này và tất cả các thành phần con. |
|
Được gọi khi khung hiển thị này phải gán một kích thước và vị trí cho tất cả các khung hiển thị con. | |
|
Được gọi khi kích thước của thành phần hiển thị này thay đổi. | |
Vẽ |
|
Được gọi khi khung hiển thị phải kết xuất nội dung. |
Xử lý sự kiện |
|
Được gọi khi một sự kiện nhấn phím xảy ra. |
|
Được gọi khi một sự kiện nhả phím xảy ra. | |
|
Được gọi khi một sự kiện chuyển động bi xoay xảy ra. | |
|
Được gọi khi một sự kiện chuyển động trên màn hình cảm ứng xảy ra. | |
Tập trung |
|
Được gọi khi thành phần hiển thị nhận hoặc mất tâm điểm. |
|
Được gọi khi cửa sổ chứa thành phần hiển thị nhận hoặc mất tâm điểm. | |
Đính kèm |
|
Được gọi khi thành phần hiển thị được đính kèm vào cửa sổ. |
|
Được gọi khi thành phần hiển thị được tách khỏi cửa sổ. | |
|
Được gọi khi chế độ hiển thị của cửa sổ chứa khung hiển thị thay đổi. |
Chế độ điều khiển phức hợp
Nếu bạn không muốn tạo một thành phần được tuỳ chỉnh hoàn toàn mà thay vào đó muốn đặt
kết hợp một thành phần có thể sử dụng lại bao gồm một nhóm các thành phần điều khiển hiện có, sau đó tạo một thành phần kết hợp
thành phần (hoặc điều khiển kết hợp) có thể là tốt nhất. Tóm lại, công cụ này tập hợp một số thông tin
các nút điều khiển hoặc chế độ xem nguyên tử thành một nhóm mục hợp lý có thể được coi là một sự vật duy nhất.
Ví dụ: một hộp kết hợp có thể là sự kết hợp của một trường EditText
dòng đơn
và một nút bên cạnh có danh sách bật lên đính kèm. Nếu người dùng nhấn vào nút và chọn mục nào đó từ
danh sách, nó sẽ điền vào trường EditText
. Tuy nhiên, người dùng cũng có thể nhập nội dung nào đó
trực tiếp vào EditText
nếu họ muốn.
Trong Android, bạn có thể sử dụng hai chế độ xem khác để làm việc này: Spinner
và
AutoCompleteTextView
Dù vậy, khái niệm về hộp kết hợp này là một ví dụ điển hình.
Để tạo thành phần phức hợp, hãy làm như sau:
-
Tương tự như với
Activity
, hãy sử dụng phương pháp khai báo (dựa trên XML) để tạo các thành phần bên trong hoặc lồng chúng theo phương thức lập trình từ mã của bạn. Chiến lược phát hành đĩa đơn điểm xuất phát thông thường là mộtLayout
, vì vậy, hãy tạo một lớp mở rộngLayout
. Trong trường hợp dùng hộp kết hợp, bạn có thể sử dụngLinearLayout
với hướng ngang. Bạn có thể lồng các bố cục khác vào bên trong để thành phần phức hợp có thể phức tạp và có cấu trúc một cách tuỳ ý. -
Trong hàm khởi tạo cho lớp mới, hãy lấy bất kỳ tham số nào mà lớp cấp cao dự kiến rồi truyền
qua hàm khởi tạo lớp cấp cao trước tiên. Sau đó, bạn có thể thiết lập các chế độ xem khác để sử dụng
trong thành phần mới. Đây là nơi bạn tạo trường
EditText
và danh sách bật lên. Bạn có thể đưa các thuộc tính và tham số của riêng mình vào tệp XML mà hàm khởi tạo có thể kéo và sử dụng. -
Nếu muốn, hãy tạo trình nghe cho những sự kiện mà các khung hiển thị được chứa của bạn có thể tạo ra. Ví dụ:
phương thức trình nghe cho trình nghe lượt nhấp vào mục trong danh sách để cập nhật nội dung của
EditText
nếu người dùng lựa chọn danh sách. -
Bạn có thể tạo các thuộc tính của riêng mình bằng trình truy cập và đối tượng sửa đổi (không bắt buộc). Ví dụ: hãy để
Giá trị
EditText
được đặt ban đầu trong thành phần và truy vấn nội dung của thành phần đó khi cần thiết. -
Ghi đè
onDraw()
vàonMeasure()
(không bắt buộc). Điều này thường không cần thiết khi mở rộngLayout
, vì bố cục có hành vi mặc định có thể hoạt động tốt. -
Ghi đè các phương thức
on
khác (chẳng hạn nhưonKeyDown()
) (không bắt buộc) để chọn một số phương thức các giá trị mặc định từ danh sách bật lên của hộp kết hợp khi nhấn vào một phím nhất định.
Việc sử dụng Layout
làm cơ sở cho chế độ điều khiển tuỳ chỉnh có một số lợi thế,
bao gồm:
- Bạn có thể chỉ định bố cục bằng cách sử dụng các tệp XML khai báo, giống như với màn hình hoạt động, hoặc bạn có thể tạo các thành phần hiển thị theo phương thức lập trình rồi lồng chúng vào bố cục trong mã.
-
Phương thức
onDraw()
vàonMeasure()
cùng với hầu hết các phương thức khác Các phương thứcon
có hành vi phù hợp nên bạn không cần phải ghi đè lên các phương thức đó. - Bạn có thể nhanh chóng xây dựng các thành phần hiển thị phức hợp tuỳ ý và sử dụng lại như thể chúng là thành phần đơn lẻ.
Sửa đổi loại chế độ xem hiện tại
Nếu có một thành phần tương tự như thành phần bạn muốn, bạn có thể mở rộng thành phần đó rồi ghi đè
hành vi mà bạn muốn thay đổi. Bạn có thể làm mọi việc mình làm bằng một
thành phần, nhưng bằng cách bắt đầu với một lớp chuyên biệt hơn trong hệ phân cấp View
, bạn có thể
nhận được một số hành vi thực hiện miễn phí.
Ví dụ:
Sổ tay
ứng dụng mẫu thể hiện nhiều khía cạnh của việc sử dụng nền tảng Android. Trong số đó là việc mở rộng
Chế độ xem EditText
để tạo sổ tay có dòng chữ. Đây không phải là một ví dụ hoàn hảo và các API cho
làm điều này có thể thay đổi, nhưng nó thể hiện các nguyên tắc.
Nếu bạn chưa thực hiện thao tác này, hãy nhập mẫu NotePad vào Android Studio hoặc xem
bằng liên kết được cung cấp. Cụ thể, hãy xem định nghĩa của LinedEditText
trong
NoteEditor.java
.
Dưới đây là một số điểm cần lưu ý trong tệp này:
-
Định nghĩa
Lớp này được xác định bằng dòng sau:
public static class LinedEditText extends EditText
LinedEditText
được khai báo là một lớp bên trong trongNoteEditor
nhưng sẽ ở chế độ công khai để mọi người có thể truy cập dưới dạngNoteEditor.LinedEditText
từ bên ngoài lớpNoteEditor
.Ngoài ra,
LinedEditText
làstatic
, nghĩa là nó không tạo phương pháp gọi là "phương pháp tổng hợp" cho phép nó truy cập vào dữ liệu từ lớp mẹ. Điều này có nghĩa là hoạt động như một lớp riêng biệt thay vì một lớp liên quan chặt chẽ đếnNoteEditor
. Đây là cách gọn gàng hơn để tạo các lớp bên trong nếu các lớp đó không cần quyền truy cập vào trạng thái từ lớp bên ngoài. Thư viện này giúp lớp được tạo nhỏ gọn và dễ dàng sử dụng từ khác.LinedEditText
mở rộngEditText
, đây là thành phần hiển thị có thể tuỳ chỉnh trong trường hợp này. Khi bạn hoàn tất, lớp mới có thể thay thế cho mộtEditText
thông thường chế độ xem. -
Khởi tạo lớp
Như thường lệ, lớp cha sẽ được gọi trước tiên. Đây không phải là một hàm khởi tạo mặc định, mà là một có tham số.
EditText
được tạo bằng các tham số này khi được tăng cường từ tệp bố cục XML. Do đó, hàm khởi tạo cần lấy và truyền chúng vào cả hàm khởi tạo lớp cấp cao. -
Phương thức bị ghi đè
Ví dụ này chỉ ghi đè phương thức
onDraw()
, nhưng bạn có thể cần phải ghi đè khác khi bạn tạo thành phần tuỳ chỉnh của riêng mình.Đối với mẫu này, việc ghi đè phương thức
onDraw()
cho phép bạn vẽ các đường màu xanh dương trên khung hiển thịEditText
. Canvas được chuyển vào phần bị ghi đèonDraw()
. Phương thứcsuper.onDraw()
được gọi trước phương thức kết thúc. Phương thức của lớp cấp cao phải được gọi. Trong trường hợp này, hãy gọi nó ở cuối sau bạn vẽ các đường bạn muốn đưa vào. -
Thành phần tuỳ chỉnh
Bây giờ, bạn đã có thành phần tuỳ chỉnh, nhưng làm cách nào để sử dụng? Trong ví dụ về NotePad, được sử dụng trực tiếp từ bố cục khai báo, vì vậy hãy xem xét
note_editor.xml
trongres/layout
thư mục:<view xmlns:android="http://schemas.android.com/apk/res/android" class="com.example.android.notepad.NoteEditor$LinedEditText" android:id="@+id/note" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" android:padding="5dp" android:scrollbars="vertical" android:fadingEdge="vertical" android:gravity="top" android:textSize="22sp" android:capitalize="sentences" />
Thành phần tuỳ chỉnh được tạo dưới dạng một khung hiển thị chung trong XML và lớp được chỉ định bằng cách sử dụng gói đầy đủ. Lớp bên trong mà bạn xác định được tham chiếu bằng cách sử dụng ký hiệu
NoteEditor$LinedEditText
, là cách tiêu chuẩn để tham chiếu đến bên trong trong ngôn ngữ lập trình Java.Nếu thành phần khung hiển thị tuỳ chỉnh không được xác định là một lớp bên trong, bạn có thể khai báo khung hiển thị đó thành phần có tên phần tử XML và loại trừ thuộc tính
class
. Cho ví dụ:<com.example.android.notepad.LinedEditText id="@+id/note" ... />
Xin lưu ý rằng lớp
LinedEditText
hiện là một tệp lớp riêng biệt. Khi lớp được lồng trong lớpNoteEditor
, kỹ thuật này không hoạt động.Các thuộc tính và thông số khác trong định nghĩa là những thuộc tính và thông số được chuyển vào thuộc tính tuỳ chỉnh hàm khởi tạo thành phần rồi truyền đến hàm khởi tạo
EditText
, vì vậy chúng cũng chính là tham số mà bạn dùng cho khung hiển thịEditText
. Bạn có thể thêm của riêng bạn.
Việc tạo thành phần tuỳ chỉnh chỉ phức tạp theo mức bạn cần.
Một thành phần tinh vi hơn có thể ghi đè nhiều phương thức on
hơn nữa và giới thiệu
các phương thức trợ giúp riêng, tuỳ chỉnh đáng kể các thuộc tính và hành vi của phương thức đó. Giới hạn duy nhất là
trí tưởng tượng và những việc bạn cần thành phần này làm.