Tạo thành phần khung hiển thị tuỳ chỉnh

Thử cách Compose
Jetpack Compose là bộ công cụ giao diện người dùng được đề xuất cho Android. Tìm hiểu cách sử dụng bố cục trong ứng dụng Compose.

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 ViewViewGroup Nền tảng này bao gồm nhiều lớp con ViewViewGroup đượ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:

  1. 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.
  2. 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ện on trong Activity hoặc ListActivity mà bạn cơ chế ghi đè cho các vòng đời và các hook khác về chức năng.
  3. 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 định onMeasure() 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. widthMeasureSpecheightMeasureSpec 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 trong View.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ầu onMeasure() 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.
onFinishInflate() Đượ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 onMeasure(int, int) Đượ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.
onLayout(boolean, int, int, int, int) Đượ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.
onSizeChanged(int, int, int, int) Được gọi khi kích thước của thành phần hiển thị này thay đổi.
Vẽ onDraw(Canvas) Được gọi khi khung hiển thị phải kết xuất nội dung.
Xử lý sự kiện onKeyDown(int, KeyEvent) Được gọi khi một sự kiện nhấn phím xảy ra.
onKeyUp(int, KeyEvent) Được gọi khi một sự kiện nhả phím xảy ra.
onTrackballEvent(MotionEvent) Được gọi khi một sự kiện chuyển động bi xoay xảy ra.
onTouchEvent(MotionEvent) Đượ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 onFocusChanged(boolean, int, Rect) Được gọi khi thành phần hiển thị nhận hoặc mất tâm điểm.
onWindowFocusChanged(boolean) Đượ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 onAttachedToWindow() Được gọi khi thành phần hiển thị được đính kèm vào cửa sổ.
onDetachedFromWindow() Được gọi khi thành phần hiển thị được tách khỏi cửa sổ.
onWindowVisibilityChanged(int) Đượ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: SpinnerAutoCompleteTextView 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ột Layout, vì vậy, hãy tạo một lớp mở rộng Layout. Trong trường hợp dùng hộp kết hợp, bạn có thể sử dụng LinearLayout 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()onMeasure() (không bắt buộc). Điều này thường không cần thiết khi mở rộng Layout, 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()onMeasure() cùng với hầu hết các phương thức khác Các phương thức on 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:

  1. Đị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 trong NoteEditor nhưng sẽ ở chế độ công khai để mọi người có thể truy cập dưới dạng NoteEditor.LinedEditText từ bên ngoài lớp NoteEditor.

    Ngoài ra, LinedEditTextstatic, 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ẽ đến NoteEditor. Đâ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ộng EditText, đâ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ột EditText thông thường chế độ xem.

  2. 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.

  3. 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ức super.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.

  4. 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 trong res/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ớp NoteEditor, 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.