Tổng quan về sự kiện đầu vào

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 thao tác chạm và nhập trong Compose.

Trên Android, có nhiều cách để chặn các sự kiện từ tương tác của người dùng với ứng dụng. Để xem xét các sự kiện trong phạm vi giao diện người dùng, nên chụp các sự kiện từ đối tượng chế độ hiển thị cụ thể mà người dùng tương tác. Lớp chế độ hiển thị sẽ cung cấp phương tiện để thực hiện điều này.

Trong số các lớp chế độ hiển thị được dùng để thiết kế bố cục, bạn sẽ thấy một số phương thức gọi lại công khai hữu ích cho các sự kiện giao diện người dùng. Các phương thức này được khung Android gọi lại khi có hành động tương ứng xảy ra trên đối tượng đó. Chẳng hạn, khi chạm vào một chế độ hiển thị (chẳng hạn như Nút), phương thức onTouchEvent() sẽ được gọi trên đối tượng đó. Tuy nhiên, để ngăn chặn điều này, phải mở rộng lớp và ghi đè phương thức. Tuy nhiên, việc mở rộng mọi đối tượng chế độ hiển thị để điều khiển những sự kiện như vậy sẽ không thực tế. Vì vậy, lớp chế độ hiển thị cũng chứa một tập hợp các giao diện lồng nhau cùng với các lệnh gọi lại rất dễ xác định. Các giao diện này, có tên là trình nghe sự kiện, giúp thu hút người dùng tương tác với giao diện người dùng.

Mặc dù trình nghe sự kiện thường được dùng để theo dõi tương tác của người dùng, sẽ có những lúc cần mở rộng lớp chế độ hiển thị để tạo thành phần tùy chỉnh. Có trường hợp cần mở rộng lớp Button để thêm hiệu ứng đẹp hơn. Trong trường hợp này, có thể xác định các hành vi sự kiện mặc định cho lớp bằng cách sử dụng trình xử lý sự kiện của lớp.

Trình nghe sự kiện

Trình nghe sự kiện là một giao diện trong lớp View chứa duy nhất một phương thức gọi lại. Phương thức này sẽ được khung Android bật lên khi chế độ hiển thị đã được trình nghe đăng ký được kích hoạt khi người dùng tương tác với mục trong giao diện người dùng.

Trong giao diện của trình nghe sự kiện có các phương thức gọi lại sau đây:

onClick()
Từ
View.OnClickListener Lệnh này được gọi khi người dùng nhấn vào mục (ở chế độ cảm ứng), hoặc đặt tiêu điểm vào mục bằng các phím điều hướng hoặc bi xoay rồi nhấn phím "enter" hoặc bi xoay.
onLongClick()
Từ
View.OnLongClickListener Lệnh này được gọi khi người dùng chạm và giữ mục (ở chế độ cảm ứng), hoặc đặt tiêu điểm vào mục bằng các phím điều hướng hoặc bi xoay rồi nhấn và giữ phím "Enter" hoặc bi xoay (trong một giây).
onFocusChange()
Từ
View.OnFocusChangeListener Lệnh này được gọi khi người dùng di chuyển lên hoặc ra khỏi mục bằng các phím điều hướng hoặc bi xoay.
onKey()
Từ
View.OnKeyListener Lệnh này được gọi khi người dùng đặt tiêu điểm vào mục và nhấn hoặc nhả phím cứng trên thiết bị.
onTouch()
Từ
View.OnTouchListener Lệnh này được gọi khi người dùng thực hiện một thao tác chạm, bao gồm nhấn, nhả hay bất kỳ thao tác chuyển động nào trên màn hình (trong phạm vi của mục đó).
onCreateContextMenu()
Từ
View.OnCreateContextMenuListener Lệnh này được gọi khi một Trình đơn ngữ cảnh đang được tạo (do một lượt "nhấp và giữ" liên tục). Xem giới thiệu về trình đơn ngữ cảnh trong hướng dẫn dành cho lập trình viên Trình đơn.

Các phương thức này là những thành tố duy nhất trong giao diện tương ứng. Để xác định một phương thức và điều khiển các sự kiện, hãy triển khai giao diện lồng nhau trong phần Hoạt động hoặc định nghĩa giao diện đó là một lớp ẩn danh. Sau đó, hãy chuyển một phiên bản triển khai sang phương thức View.set...Listener() tương ứng. (Ví dụ: gọi setOnClickListener() và chuyển phần triển khai OnClickListener cho nó.)

Ví dụ sau đây minh họa cách đăng ký trình nghe theo lượt nhấp cho một Nút.

Kotlin

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

Java

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(corkyListener);
    ...
}

Việc triển khai trình nghe theo lượt nhấp như một phần trong Hoạt động . Điều này giúp tránh tải thêm lớp và phân bổ đối tượng. Ví dụ:

Kotlin

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

Java

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}

Lưu ý lệnh gọi lại onClick() trong ví dụ trên không có giá trị trả về, nhưng một số phương thức trình xử lý sự kiện khác phải trả về một giá trị boolean. Lý do phụ thuộc vào sự kiện. Dưới đây là lý giải cho điều này:

  • onLongClick() – Lệnh này sẽ trả về một giá trị boolean cho biết liệu sự kiện đã được sử dụng hay chưa và sự kiện không nên được thực hiện thêm nữa. Nghĩa là, trả về giá trị true (đúng) để cho biết bạn đã xử lý sự kiện và sự kiện sẽ dừng tại đây; trả về giá trị false (sai) nếu bạn chưa xử lý sự kiện và/hoặc sự kiện sẽ tiếp tục với bất kỳ trình nghe lượt nhấp nào khác.
  • onKey() – Lệnh này sẽ trả về một giá trị boolean cho biết liệu sự kiện đã được sử dụng hay chưa và sự kiện không nên được thực hiện thêm nữa. Nghĩa là, trả về giá trị true (đúng) để cho biết bạn đã xử lý sự kiện và sự kiện sẽ dừng tại đây; trả về giá trị false (sai) nếu bạn chưa xử lý sự kiện và/hoặc sự kiện sẽ tiếp tục với bất kỳ trình nghe phím nào khác.
  • onTouch() – Lệnh này sẽ trả về một giá trị boolean cho biết trình nghe có sử dụng sự kiện này hay không. Sự kiện này có thể có nhiều hành động nối tiếp nhau. Vì vậy, nếu trả về giá trị false (sai) khi nhận được sự kiện hoạt động không hành động nghĩa là bạn chưa sử dụng sự kiện và cũng không quan tâm đến các hành động tiếp theo sau sự kiện này. Do đó, bạn sẽ không được gọi thêm bất kỳ hành động nào khác trong phạm vi sự kiện, chẳng hạn như một thao tác ngón tay bằng cử chỉ hoặc sự kiện hành động cuối.

Hãy nhớ các sự kiện chính của phần cứng luôn được chuyển tới Chế độ xem hiện đang được lấy tiêu điểm. Chúng xuất phát từ đỉnh của hệ phân cấp Chế độ xem, rồi di chuyển xuống cho tới khi đạt điểm đến thích hợp. Nếu Chế độ xem (hoặc một Chế độ xem con) hiện có tiêu điểm, bạn có thể thấy sự kiện này di chuyển qua phương thức dispatchKeyEvent(). Ngoài việc ghi lại các sự kiện quan trọng thông qua Chế độ xem, bạn cũng có thể tiếp nhận tất cả các sự kiện bên trong Hoạt động thông qua onKeyDown()onKeyUp().

Ngoài ra, khi xem xét việc nhập văn bản cho ứng dụng, hãy nhớ nhiều thiết bị chỉ có phương thức nhập phần mềm. Những phương thức đó không bắt buộc phải dựa trên phím bấm; một số có thể sử dụng tính năng nhập bằng giọng nói, chữ viết tay, v.v. Ngay cả khi phương thức nhập hiển thị một giao diện giống bàn phím, phương thức đó thường cũng sẽkhông kích hoạt nhóm sự kiện onKeyDown(). Không được tạo một giao diện người dùng yêu cầu chế độ nhấn phím cụ thể trừ khi bạn chỉ muốn giới hạn ứng dụng ở các thiết bị có bàn phím cứng. Cụ thể, đừng dựa vào các phương thức này để xác thực đầu vào khi người dùng nhấn phím trả về; thay vào đó, hãy sử dụng các thao tác như IME_ACTION_DONE để báo cho phương thức nhập biết ứng dụng dự kiến sẽ phản ứng thế nào, nhờ đó, ứng dụng có thể thay đổi giao diện người dùng theo cách phù hợp. Tránh các giả định về cách thức hoạt động của phương thức nhập phần mềm và chỉ cần tin tưởng phương thức đó sẽ cung cấp cho ứng dụng văn bản đã được định dạng.

Lưu ý: Trước tiên, Android sẽ gọi các trình xử lý sự kiện, tiếp đến là các trình xử lý mặc định thích hợp từ việc xác định lớp thứ hai. Theo đó, việc trả về giá trị true (đúng) từ các trình nghe sự kiện này sẽ ngừng truyền sự kiện đến các trình nghe khác và cũng sẽ chặn lệnh gọi lại đến trình xử lý sự kiện mặc định trong Chế độ xem. Vì vậy, phải chắc chắn rằng bạn muốn chấm dứt sự kiện khi trả về giá trị true (đúng).

Trình xử lý sự kiện

Nếu đang tạo một thành phần tùy chỉnh từ Chế độ xem, bạn có thể xác định một số phương thức gọi lại được dùng làm trình xử lý sự kiện mặc định. Trong tài liệu về Thành phần chế độ xem tùy chỉnh, có một số lệnh gọi lại phổ biến dùng để xử lý sự kiện, bao gồm:

Ngoài ra còn có một số phương thức khác bạn nên biết. Những phương thức này không thuộc lớp Chế độ hiển thị, nhưng có thể tác động trực tiếp đến cách xử lý các sự kiện. Vì vậy, khi quản lý các sự kiện phức tạp hơn bên trong một bố cục, hãy cân nhắc các phương thức sau:

Chế độ cảm ứng

Khi người dùng đang thao tác trên một giao diện người dùng bằng các phím định hướng hoặc bi xoay, cần chú ý các mục thao tác (như các nút) để thấy mục nào sẽ tiếp nhận đầu vào. Tuy nhiên, nếu thiết bị có khả năng cảm ứng và người dùng bắt đầu tương tác với giao diện bằng cách chạm thì không cần đánh dấu các mục hoặc tập trung vào một Chế độ hiển thị cụ thể nữa. Do đó, có một chế độ dành cho tương tác có tên là "chế độ cảm ứng".

Đối với thiết bị có khả năng cảm ứng, khi người dùng chạm vào màn hình, thiết bị sẽ chuyển sang chế độ cảm ứng. Từ thời điểm này trở đi, chỉ các Chế độ hiển thị có isFocusableInTouchMode() là true mới có thể đặt tiêu điểm, chẳng hạn như các tiện ích chỉnh sửa văn bản. Các Chế độ hiển thị khác có thể chạm, chẳng hạn như các nút, sẽ không đặt tiêu điểm khi được chạm; khi được nhấn vào, chúng sẽ kích hoạt trình nghe theo nhấp chuột.

Bất cứ khi nào người dùng nhấn vào phím định hướng hoặc cuộn bằng bi xoay, thiết bị sẽ thoát khỏi chế độ cảm ứng và tìm một chế độ hiển thị để lấy tiêu điểm. Lúc này, người dùng có thể tiếp tục tương tác với giao diện người dùng mà không cần chạm vào màn hình.

Trạng thái của chế độ cảm ứng được duy trì trên toàn bộ hệ thống (tất cả các cửa sổ và hoạt động). Để truy vấn trạng thái hiện tại, có thể gọi isInTouchMode() để xem thiết bị hiện có đang ở chế độ cảm ứng hay không.

Xử lý tiêu điểm

Khung này sẽ xử lý chuyển động tiêu điểm thường xuyên để phản hồi khi người dùng nhập liệu. Công việc này bao gồm thay đổi tiêu điểm khi Chế độ hiển bị xóa hoặc bị ẩn, hoặc khi có Chế độ hiển thị mới. Chế độ hiển thị thể hiện trạng thái sẵn sàng lấy tiêu điểm thông qua phương thức isFocusable(). Để cho phép Chế độ hiển thị có thể lấy tiêu điểm, gọi setFocusable(). Ở chế độ cảm ứng, bạn có thể truy vấn xem Chế độ hiển thị có cho phép lấy tiêu điểm hay không bằng isFocusableInTouchMode(). Thay đổi trạng thái này bằng setFocusableInTouchMode().

Trên các thiết bị chạy phiên bản Android 9 (API cấp 28) trở lên, các hoạt động sẽ không chỉ định tiêu điểm ban đầu. Thay vào đó, nếu muốn, phải yêu cầu rõ tiêu điểm ban đầu.

Chuyển động tiêu điểm dựa trên thuật toán tìm yếu tố lân cận gần nhất theo một hướng nhất định. Trong một số ít trường hợp, thuật toán mặc định có thể không phù hợp với hành vi mong muốn của lập trình viên. Trong các trường hợp này, bạn có thể cung cấp phần ghi đè rõ ràng bằng các thuộc tính XML sau trong tệp bố cục:nextFocusDown, nextFocusLeft, nextFocusRightnextFocusUp. Thêm một trong các thuộc tính này vào Chế độ xem từ tiêu điểm để lại. Xác định giá trị của thuộc tính là id của Chế độ xem cho tiêu điểm cần cung cấp. Ví dụ:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

Thông thường, trong bố cục dọc này, việc di chuyển lên từ Nút đầu tiên sẽ không hiển thị ở bất cứ đâu hoặc cũng không di chuyển xuống từ Nút thứ hai. Hiện tại, Nút trên cùng đã xác định phần tử dưới cùng là nextFocusUp (và ngược lại), tiêu điểm điều hướng sẽ xoay vòng từ trên xuống dưới và từ dưới lên trên.

Nếu muốn khai báo một Chế độ xem có thể lấy tiêu điểm trong giao diện người dùng (thường thì không thể) thì hãy thêm thuộc tính XML android:focusable vào Chế độ xem trong bản khai báo bố cục. Đặt giá trị true. Bạn cũng có thể khai báo Chế độ xem có thể lấy tiêu điểm khi ở Chế độ cảm ứng với android:focusableInTouchMode.

Để yêu cầu một Chế độ xem cụ thể tập trung, hãy gọi requestFocus().

Để theo dõi các sự kiện tập trung (được thông báo khi Chế độ xem nhận được hoặc mất tiêu điểm), hãy sử dụng onFocusChange(), như đã thảo luận trong phần Trình xử lý sự kiện.