Tạo dịch vụ hỗ trợ tiếp cận của riêng bạn

Dịch vụ hỗ trợ tiếp cận là một ứng dụng giúp cải thiện giao diện người dùng để hỗ trợ người dùng bị khuyết tật hoặc người dùng tạm thời không thể tương tác đầy đủ với một thiết bị. Ví dụ: những người dùng đang lái xe, chăm sóc trẻ nhỏ hoặc tham dự một bữa tiệc có tiếng ồn rất lớn có thể cần đến phản hồi bổ sung hoặc thay thế trên giao diện.

Android cung cấp các dịch vụ hỗ trợ tiếp cận tiêu chuẩn, bao gồm cả TalkBack, đồng thời nhà phát triển có thể tạo và phân phối các dịch vụ của riêng họ. Tài liệu này giải thích các khái niệm cơ bản liên quan đến việc tạo một dịch vụ hỗ trợ tiếp cận.

Bạn có thể kết hợp dịch vụ hỗ trợ tiếp cận với một ứng dụng thông thường hoặc tạo dưới dạng một dự án Android độc lập. Các bước tạo dịch vụ tương tự nhau trong cả hai trường hợp.

Tạo dịch vụ hỗ trợ tiếp cận của bạn

Trong dự án của bạn, hãy tạo một lớp mở rộng AccessibilityService:

Kotlin

package com.example.android.apis.accessibility

import android.accessibilityservice.AccessibilityService
import android.view.accessibility.AccessibilityEvent

class MyAccessibilityService : AccessibilityService() {
...
    override fun onInterrupt() {}

    override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
...
}

Java

package com.example.android.apis.accessibility;

import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;

public class MyAccessibilityService extends AccessibilityService {
...
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
    }

    @Override
    public void onInterrupt() {
    }

...
}

Nếu tạo một dự án mới cho Service này và không có ý định liên kết với một ứng dụng, thì bạn có thể xoá lớp khởi đầu của Activity khỏi nguồn của mình.

Quyền và nội dung khai báo trong tệp kê khai

Các ứng dụng cung cấp dịch vụ hỗ trợ tiếp cận phải bao gồm các nội dung khai báo cụ thể trong tệp kê khai ứng dụng để được hệ thống Android coi là một dịch vụ hỗ trợ tiếp cận. Phần này giải thích các chế độ cài đặt bắt buộc và không bắt buộc cho các dịch vụ hỗ trợ tiếp cận.

Khai báo dịch vụ hỗ trợ tiếp cận

Để ứng dụng của bạn được coi là một dịch vụ hỗ trợ tiếp cận, hãy thêm phần tử service (thay vì phần tử activity) trong phần tử application vào tệp kê khai. Ngoài ra, trong phần tử service, hãy thêm một bộ lọc ý định cho dịch vụ hỗ trợ tiếp cận. Tệp kê khai cũng phải bảo vệ dịch vụ này bằng cách thêm quyền BIND_ACCESSIBILITY_SERVICE để đảm bảo chỉ hệ thống mới có thể liên kết với dịch vụ đó. Ví dụ:

  <application>
    <service android:name=".MyAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
        android:label="@string/accessibility_service_label">
      <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
      </intent-filter>
    </service>
  </application>

Cấu hình dịch vụ hỗ trợ tiếp cận

Dịch vụ hỗ trợ tiếp cận phải cung cấp một cấu hình chỉ định các loại sự kiện hỗ trợ tiếp cận mà dịch vụ này xử lý và thông tin bổ sung về dịch vụ đó. Cấu hình của một dịch vụ hỗ trợ tiếp cận nằm trong lớp AccessibilityServiceInfo. Dịch vụ của bạn có thể tạo và đặt cấu hình bằng một thực thể của lớp này và setServiceInfo() trong thời gian chạy. Tuy nhiên, một số lựa chọn cấu hình sẽ không xuất hiện khi dùng phương pháp này.

Bạn có thể đưa một phần tử <meta-data> vào tệp kê khai với nội dung tham chiếu đến một tệp cấu hình. Tệp này cho phép bạn đặt đầy đủ các tuỳ chọn cho dịch vụ hỗ trợ tiếp cận của mình, như trong ví dụ sau:

<service android:name=".MyAccessibilityService">
  ...
  <meta-data
    android:name="android.accessibilityservice"
    android:resource="@xml/accessibility_service_config" />
</service>

Phần tử <meta-data> này là một tệp XML mà bạn tạo trong thư mục tài nguyên của ứng dụng: <project_dir>/res/xml/accessibility_service_config.xml>. Đoạn mã sau đây là một ví dụ về nội dung của tệp cấu hình dịch vụ:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:packageNames="com.example.android.apis"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
    android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>

Để biết thêm thông tin về các thuộc tính XML có thể dùng trong tệp cấu hình của dịch vụ hỗ trợ tiếp cận, hãy xem tài liệu tham khảo sau:

Để biết thêm thông tin về các chế độ cài đặt cấu hình có thể được đặt tự động trong thời gian chạy, hãy xem tài liệu tham khảo về AccessibilityServiceInfo.

Định cấu hình dịch vụ hỗ trợ tiếp cận

Hãy cân nhắc những điều sau khi đặt các biến cấu hình cho dịch vụ hỗ trợ tiếp cận để cho hệ thống biết cách thức và thời điểm chạy:

  • Bạn muốn dịch vụ phản hồi với loại sự kiện nào?
  • Dịch vụ có cần hoạt động với mọi ứng dụng hay chỉ với những tên gói cụ thể?
  • Dịch vụ sử dụng các loại phản hồi nào?

Bạn có 2 lựa chọn để đặt các biến này. Lựa chọn tương thích ngược dùng để đặt các biến trong mã bằng cách sử dụng setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo). Để làm vậy, hãy ghi đè phương thức onServiceConnected() và định cấu hình dịch vụ của bạn ở đó, như trong ví dụ sau:

Kotlin

override fun onServiceConnected() {
    info.apply {
        // Set the type of events that this service wants to listen to. Others
        // aren't passed to this service.
        eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED

        // If you only want this service to work with specific apps, set their
        // package names here. Otherwise, when the service is activated, it
        // listens to events from all apps.
        packageNames = arrayOf("com.example.android.myFirstApp", "com.example.android.mySecondApp")

        // Set the type of feedback your service provides.
        feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN

        // Default services are invoked only if no package-specific services are
        // present for the type of AccessibilityEvent generated. This service is
        // app-specific, so the flag isn't necessary. For a general-purpose
        // service, consider setting the DEFAULT flag.

        // flags = AccessibilityServiceInfo.DEFAULT;

        notificationTimeout = 100
    }

    this.serviceInfo = info

}

Java

@Override
public void onServiceConnected() {
    // Set the type of events that this service wants to listen to. Others
    // aren't passed to this service.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
            AccessibilityEvent.TYPE_VIEW_FOCUSED;

    // If you only want this service to work with specific apps, set their
    // package names here. Otherwise, when the service is activated, it listens
    // to events from all apps.
    info.packageNames = new String[]
            {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};

    // Set the type of feedback your service provides.
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;

    // Default services are invoked only if no package-specific services are
    // present for the type of AccessibilityEvent generated. This service is
    // app-specific, so the flag isn't necessary. For a general-purpose service,
    // consider setting the DEFAULT flag.

    // info.flags = AccessibilityServiceInfo.DEFAULT;

    info.notificationTimeout = 100;

    this.setServiceInfo(info);

}

Lựa chọn thứ hai là định cấu hình dịch vụ bằng một tệp XML. Một số lựa chọn cấu hình như canRetrieveWindowContent chỉ có sẵn nếu bạn định cấu hình dịch vụ của mình bằng XML. Các lựa chọn cấu hình trong ví dụ trước sẽ có dạng như sau khi được xác định bằng XML:

<accessibility-service
     android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
     android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:notificationTimeout="100"
     android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
     android:canRetrieveWindowContent="true"
/>

Nếu bạn sử dụng XML, hãy tham chiếu tệp này trong tệp kê khai bằng cách thêm thẻ <meta-data> vào phần khai báo dịch vụ trỏ đến tệp XML. Nếu bạn lưu trữ tệp XML trong res/xml/serviceconfig.xml, thẻ mới sẽ có dạng như sau:

<service android:name=".MyAccessibilityService">
     <intent-filter>
         <action android:name="android.accessibilityservice.AccessibilityService" />
     </intent-filter>
     <meta-data android:name="android.accessibilityservice"
     android:resource="@xml/serviceconfig" />
</service>

Các phương thức của dịch vụ hỗ trợ tiếp cận

Dịch vụ hỗ trợ tiếp cận phải mở rộng lớp AccessibilityService và ghi đè các phương thức sau của lớp đó. Các phương thức này trình bày theo thứ tự được hệ thống Android gọi: từ khi dịch vụ bắt đầu (onServiceConnected()), trong lúc dịch vụ chạy (onAccessibilityEvent(), onInterrupt()) tới khi dịch vụ ngừng hoạt động (onUnbind()).

  • onServiceConnected(): (không bắt buộc) hệ thống gọi phương thức này khi đây là phương thức kết nối với dịch vụ hỗ trợ tiếp cận của bạn. Hãy sử dụng phương thức này để thực hiện các bước thiết lập một lần cho dịch vụ của bạn, bao gồm cả việc kết nối với các dịch vụ hệ thống phản hồi của người dùng, chẳng hạn như trình quản lý âm thanh hoặc trình rung trên thiết bị. Nếu bạn muốn đặt cấu hình cho dịch vụ trong thời gian chạy hoặc thực hiện việc điều chỉnh một lần, đây là vị trí thuận tiện để gọi setServiceInfo().

  • onAccessibilityEvent(): (bắt buộc) hệ thống gọi lại phương thức này khi phát hiện thấy một AccessibilityEvent khớp với các tham số lọc sự kiện do dịch vụ hỗ trợ tiếp cận của bạn chỉ định, chẳng hạn như khi người dùng nhấn vào một nút hoặc lấy làm tâm điểm trên một chế độ kiểm soát giao diện người dùng trong ứng dụng mà dịch vụ hỗ trợ tiếp cận của bạn đang cung cấp ý kiến phản hồi. Khi gọi phương thức này, hệ thống sẽ truyền AccessibilityEvent được liên kết mà sau đó dịch vụ có thể diễn giải và sử dụng để cung cấp ý kiến phản hồi cho người dùng. Phương thức này có thể được gọi nhiều lần trong vòng đời của dịch vụ.

  • onInterrupt(): (bắt buộc) hệ thống gọi phương thức này khi muốn làm gián đoạn ý kiến phản hồi mà dịch vụ của bạn đang cung cấp, thường là để phản hồi một hành động của người dùng, chẳng hạn như di chuyển tâm điểm sang một chế độ điều khiển khác. Phương thức này có thể được gọi nhiều lần trong vòng đời của dịch vụ.

  • onUnbind(): (không bắt buộc) hệ thống gọi phương thức này khi sắp tắt dịch vụ hỗ trợ tiếp cận. Hãy sử dụng phương thức này để thực hiện các quy trình tắt một lần, bao gồm cả việc huỷ phân bổ các dịch vụ cho hệ thống phản hồi của người dùng, chẳng hạn như trình quản lý âm thanh hoặc trình rung trên thiết bị.

Các phương thức gọi lại này cung cấp cấu trúc cơ bản cho dịch vụ hỗ trợ tiếp cận. Bạn có thể quyết định cách xử lý dữ liệu do hệ thống Android cung cấp dưới dạng các đối tượng AccessibilityEvent và đưa ra ý kiến phản hồi cho người dùng. Để biết thêm thông tin về cách nhận thông tin từ một sự kiện hỗ trợ tiếp cận, hãy xem phần Lấy thông tin chi tiết về sự kiện.

Đăng ký các sự kiện hỗ trợ tiếp cận

Một trong những chức năng quan trọng nhất của tham số cấu hình dịch vụ hỗ trợ tiếp cận là cho phép bạn chỉ định những loại sự kiện hỗ trợ tiếp cận mà dịch vụ của bạn có thể xử lý. Khi chỉ định thông tin này, các dịch vụ hỗ trợ tiếp cận có thể hoạt động cùng nhau và giúp bạn có được sự linh hoạt để chỉ xử lý những loại sự kiện cụ thể của những ứng dụng cụ thể. Quá trình lọc sự kiện có thể bao gồm các tiêu chí sau:

  • Tên gói chỉ định tên gói của các ứng dụng có sự kiện hỗ trợ tiếp cận mà bạn muốn dịch vụ của mình xử lý. Nếu tham số này bị bỏ qua, dịch vụ hỗ trợ tiếp cận của bạn sẽ được coi là có sẵn cho các sự kiện hỗ trợ tiếp cận dịch vụ của mọi ứng dụng. Bạn có thể đặt tham số này trong các tệp cấu hình dịch vụ hỗ trợ tiếp cận với thuộc tính android:packageNames dưới dạng danh sách được phân tách bằng dấu phẩy hoặc sử dụng thành phần AccessibilityServiceInfo.packageNames.

  • Loại sự kiện Chỉ định loại sự kiện hỗ trợ tiếp cận mà bạn muốn dịch vụ của mình xử lý. Bạn có thể đặt tham số này trong các tệp cấu hình dịch vụ hỗ trợ tiếp cận với thuộc tính android:accessibilityEventTypes dưới dạng một danh sách được phân tách bằng ký tự | (ví dụ: accessibilityEventTypes="typeViewClicked|typeViewFocused"). Bạn cũng có thể đặt tham số này bằng thành phần AccessibilityServiceInfo.eventTypes.

Khi thiết lập dịch vụ hỗ trợ tiếp cận, hãy cân nhắc kỹ những sự kiện mà dịch vụ của bạn có thể xử lý và chỉ đăng ký các sự kiện đó. Vì người dùng có thể kích hoạt nhiều dịch vụ hỗ trợ tiếp cận cùng lúc, nên dịch vụ của bạn không được sử dụng các sự kiện mà dịch vụ đó không xử lý được. Hãy nhớ rằng các dịch vụ khác có thể xử lý các sự kiện đó để cải thiện trải nghiệm người dùng.

Âm lượng của tính năng hỗ trợ tiếp cận

Các thiết bị chạy Android 8.0 (API cấp 26) trở lên có chứa danh mục âm lượng STREAM_ACCESSIBILITY cho phép bạn kiểm soát âm lượng đầu ra âm thanh của dịch vụ hỗ trợ tiếp cận một cách độc lập với các âm thanh khác trên thiết bị.

Các dịch vụ hỗ trợ tiếp cận có thể sử dụng loại luồng này bằng cách đặt tuỳ chọn FLAG_ENABLE_ACCESSIBILITY_VOLUME. Sau đó, bạn có thể thay đổi âm lượng hỗ trợ tiếp cận của thiết bị bằng cách gọi phương thức adjustStreamVolume() trên thực thể AudioManager của thiết bị.

Đoạn mã sau đây minh hoạ cách một dịch vụ hỗ trợ tiếp cận có thể sử dụng danh mục âm lượng STREAM_ACCESSIBILITY:

Kotlin

import android.media.AudioManager.*

class MyAccessibilityService : AccessibilityService() {

    private val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager

    override fun onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) {
        if (accessibilityEvent.source.text == "Increase volume") {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY, ADJUST_RAISE, 0)
        }
    }
}

Java

import static android.media.AudioManager.*;

public class MyAccessibilityService extends AccessibilityService {
    private AudioManager audioManager =
            (AudioManager) getSystemService(AUDIO_SERVICE);

    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
        AccessibilityNodeInfo interactedNodeInfo =
                accessibilityEvent.getSource();
        if (interactedNodeInfo.getText().equals("Increase volume")) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY,
                ADJUST_RAISE, 0);
        }
    }
}

Để biết thêm thông tin, hãy xem video hội thảo Có gì mới về tính năng Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu lúc 6:35.

Phím tắt hỗ trợ tiếp cận

Trên các thiết bị chạy Android 8.0 (API cấp 26) trở lên, người dùng có thể bật và tắt dịch vụ hỗ trợ tiếp cận ưu tiên của họ trên mọi màn hình bằng cách nhấn và giữ cả hai phím âm lượng cùng lúc. Mặc dù phím tắt này sẽ bật và tắt TalkBack theo mặc định, nhưng người dùng có thể định cấu hình nút này để bật và tắt bất kỳ dịch vụ nào được cài đặt trên thiết bị của họ.

Để người dùng có thể sử dụng một dịch vụ hỗ trợ tiếp cận cụ thể thông qua phím tắt hỗ trợ tiếp cận, dịch vụ đó cần yêu cầu tính năng này trong thời gian chạy.

Để biết thêm thông tin, hãy xem video hội thảo Có gì mới về tính năng Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu lúc 13:25.

Nút hỗ trợ tiếp cận

Trên các thiết bị sử dụng khu vực điều hướng được kết xuất bằng phần mềm và chạy Android 8.0 (API cấp 26) trở lên, phía bên phải của thanh điều hướng có một nút hỗ trợ tiếp cận. Khi người dùng nhấn vào nút này, họ có thể gọi một trong nhiều tính năng và dịch vụ hỗ trợ tiếp cận đã bật, tuỳ thuộc vào nội dung đang hiển thị trên màn hình.

Để cho phép người dùng gọi một dịch vụ hỗ trợ tiếp cận nhất định bằng nút hỗ trợ tiếp cận, dịch vụ đó cần thêm cờ FLAG_REQUEST_ACCESSIBILITY_BUTTON vào thuộc tính android:accessibilityFlags của đối tượng AccessibilityServiceInfo. Sau đó, dịch vụ này có thể đăng ký các lệnh gọi lại bằng registerAccessibilityButtonCallback().

Đoạn mã sau đây minh hoạ cách bạn có thể định cấu hình một dịch vụ hỗ trợ tiếp cận để phản hồi khi người dùng nhấn nút hỗ trợ tiếp cận:

Kotlin

private var mAccessibilityButtonController: AccessibilityButtonController? = null
private var accessibilityButtonCallback:
        AccessibilityButtonController.AccessibilityButtonCallback? = null
private var mIsAccessibilityButtonAvailable: Boolean = false

override fun onServiceConnected() {
    mAccessibilityButtonController = accessibilityButtonController
    mIsAccessibilityButtonAvailable =
            mAccessibilityButtonController?.isAccessibilityButtonAvailable ?: false

    if (!mIsAccessibilityButtonAvailable) return

    serviceInfo = serviceInfo.apply {
        flags = flags or AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON
    }

    accessibilityButtonCallback =
        object : AccessibilityButtonController.AccessibilityButtonCallback() {
            override fun onClicked(controller: AccessibilityButtonController) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!")

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            override fun onAvailabilityChanged(
                    controller: AccessibilityButtonController,
                    available: Boolean
            ) {
                if (controller == mAccessibilityButtonController) {
                    mIsAccessibilityButtonAvailable = available
                }
            }
    }

    accessibilityButtonCallback?.also {
        mAccessibilityButtonController?.registerAccessibilityButtonCallback(it, null)
    }
}

Java

private AccessibilityButtonController accessibilityButtonController;
private AccessibilityButtonController
        .AccessibilityButtonCallback accessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;

@Override
protected void onServiceConnected() {
    accessibilityButtonController = getAccessibilityButtonController();
    mIsAccessibilityButtonAvailable =
            accessibilityButtonController.isAccessibilityButtonAvailable();

    if (!mIsAccessibilityButtonAvailable) {
        return;
    }

    AccessibilityServiceInfo serviceInfo = getServiceInfo();
    serviceInfo.flags
            |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
    setServiceInfo(serviceInfo);

    accessibilityButtonCallback =
        new AccessibilityButtonController.AccessibilityButtonCallback() {
            @Override
            public void onClicked(AccessibilityButtonController controller) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!");

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            @Override
            public void onAvailabilityChanged(
              AccessibilityButtonController controller, boolean available) {
                if (controller.equals(accessibilityButtonController)) {
                    mIsAccessibilityButtonAvailable = available;
                }
            }
        };

    if (accessibilityButtonCallback != null) {
        accessibilityButtonController.registerAccessibilityButtonCallback(
                accessibilityButtonCallback, null);
    }
}

Để biết thêm thông tin, hãy xem video hội thảo Có gì mới về tính năng Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu lúc 16:28.

Cử chỉ vân tay

Dịch vụ hỗ trợ tiếp cận trên các thiết bị chạy Android 8.0 (API cấp 26) trở lên có thể phản hồi các thao tác vuốt theo hướng (lên, xuống, trái và phải) dọc theo cảm biến vân tay của thiết bị. Để định cấu hình cho một dịch vụ nhận lệnh gọi lại về các lượt tương tác này, hãy hoàn thành trình tự các bước sau:

  1. Khai báo quyền USE_BIOMETRIC và chức năng CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES.
  2. Đặt cờ FLAG_REQUEST_FINGERPRINT_GESTURES trong thuộc tính android:accessibilityFlags.
  3. Đăng ký các lệnh gọi lại bằng cách sử dụng registerFingerprintGestureCallback().

Xin lưu ý rằng một số thiết bị không có cảm biến vân tay. Để xác định xem thiết bị có hỗ trợ cảm biến hay không, hãy sử dụng phương thức isHardwareDetected(). Ngay cả trên thiết bị có cảm biến vân tay, dịch vụ của bạn cũng không thể sử dụng cảm biến khi cảm biến này đang được dùng cho các mục đích xác thực. Để xác định thời điểm có thể dùng cảm biến, hãy gọi phương thức isGestureDetectionAvailable() và triển khai lệnh gọi lại onGestureDetectionAvailabilityChanged().

Đoạn mã sau đây cho thấy một ví dụ về cách sử dụng cử chỉ vân tay để di chuyển xung quanh bảng trò chơi ảo:

// AndroidManifest.xml
<manifest ... >
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    ...
    <application>
        <service android:name="com.example.MyFingerprintGestureService" ... >
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/myfingerprintgestureservice" />
        </service>
    </application>
</manifest>
// myfingerprintgestureservice.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:accessibilityFlags=" ... |flagRequestFingerprintGestures"
    android:canRequestFingerprintGestures="true"
    ... />

Kotlin

// MyFingerprintGestureService.kt
import android.accessibilityservice.FingerprintGestureController.*

class MyFingerprintGestureService : AccessibilityService() {

    private var gestureController: FingerprintGestureController? = null
    private var fingerprintGestureCallback:
            FingerprintGestureController.FingerprintGestureCallback? = null
    private var mIsGestureDetectionAvailable: Boolean = false

    override fun onCreate() {
        gestureController = fingerprintGestureController
        mIsGestureDetectionAvailable = gestureController?.isGestureDetectionAvailable ?: false
    }

    override fun onServiceConnected() {
        if (mFingerprintGestureCallback != null || !mIsGestureDetectionAvailable) return

        fingerprintGestureCallback =
                object : FingerprintGestureController.FingerprintGestureCallback() {
                    override fun onGestureDetected(gesture: Int) {
                        when (gesture) {
                            FINGERPRINT_GESTURE_SWIPE_DOWN -> moveGameCursorDown()
                            FINGERPRINT_GESTURE_SWIPE_LEFT -> moveGameCursorLeft()
                            FINGERPRINT_GESTURE_SWIPE_RIGHT -> moveGameCursorRight()
                            FINGERPRINT_GESTURE_SWIPE_UP -> moveGameCursorUp()
                            else -> Log.e(MY_APP_TAG, "Error: Unknown gesture type detected!")
                        }
                    }

                    override fun onGestureDetectionAvailabilityChanged(available: Boolean) {
                        mIsGestureDetectionAvailable = available
                    }
                }

        fingerprintGestureCallback?.also {
            gestureController?.registerFingerprintGestureCallback(it, null)
        }
    }
}

Java

// MyFingerprintGestureService.java
import static android.accessibilityservice.FingerprintGestureController.*;

public class MyFingerprintGestureService extends AccessibilityService {
    private FingerprintGestureController gestureController;
    private FingerprintGestureController
            .FingerprintGestureCallback fingerprintGestureCallback;
    private boolean mIsGestureDetectionAvailable;

    @Override
    public void onCreate() {
        gestureController = getFingerprintGestureController();
        mIsGestureDetectionAvailable =
                gestureController.isGestureDetectionAvailable();
    }

    @Override
    protected void onServiceConnected() {
        if (fingerprintGestureCallback != null
                || !mIsGestureDetectionAvailable) {
            return;
        }

        fingerprintGestureCallback =
               new FingerprintGestureController.FingerprintGestureCallback() {
            @Override
            public void onGestureDetected(int gesture) {
                switch (gesture) {
                    case FINGERPRINT_GESTURE_SWIPE_DOWN:
                        moveGameCursorDown();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_LEFT:
                        moveGameCursorLeft();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_RIGHT:
                        moveGameCursorRight();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_UP:
                        moveGameCursorUp();
                        break;
                    default:
                        Log.e(MY_APP_TAG,
                                  "Error: Unknown gesture type detected!");
                        break;
                }
            }

            @Override
            public void onGestureDetectionAvailabilityChanged(boolean available) {
                mIsGestureDetectionAvailable = available;
            }
        };

        if (fingerprintGestureCallback != null) {
            gestureController.registerFingerprintGestureCallback(
                    fingerprintGestureCallback, null);
        }
    }
}

Để biết thêm thông tin, hãy xem video hội thảo Có gì mới về tính năng Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu lúc 9:03.

Chuyển văn bản sang lời nói đa ngôn ngữ

Kể từ Android 8.0 (API cấp 26), dịch vụ chuyển văn bản sang lời nói (TTS) của Android có thể xác định và nói các cụm từ bằng nhiều ngôn ngữ trong một khối văn bản. Để bật tính năng tự động chuyển đổi ngôn ngữ này trong một dịch vụ hỗ trợ tiếp cận, hãy gói tất cả các chuỗi trong các đối tượng LocaleSpan lại, như minh hoạ trong đoạn mã sau đây:

Kotlin

val localeWrappedTextView = findViewById<TextView>(R.id.my_french_greeting_text).apply {
    text = wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE)
}

private fun wrapTextInLocaleSpan(originalText: CharSequence, loc: Locale): SpannableStringBuilder {
    return SpannableStringBuilder(originalText).apply {
        setSpan(LocaleSpan(loc), 0, originalText.length - 1, 0)
    }
}

Java

TextView localeWrappedTextView = findViewById(R.id.my_french_greeting_text);
localeWrappedTextView.setText(wrapTextInLocaleSpan("Bonjour!", Locale.FRANCE));

private SpannableStringBuilder wrapTextInLocaleSpan(
        CharSequence originalText, Locale loc) {
    SpannableStringBuilder myLocaleBuilder =
            new SpannableStringBuilder(originalText);
    myLocaleBuilder.setSpan(new LocaleSpan(loc), 0,
            originalText.length() - 1, 0);
    return myLocaleBuilder;
}

Để biết thêm thông tin, hãy xem video hội thảo What's new in Android accessibility (Có gì mới về tính năng Hỗ trợ tiếp cận của Android) tại Google I/O 2017, bắt đầu lúc 10:59.

Hành động thay cho người dùng

Kể từ năm 2011, các dịch vụ hỗ trợ tiếp cận có thể hành động thay cho người dùng, bao gồm cả việc thay đổi tâm điểm nhập và chọn (kích hoạt) các phần tử trên giao diện người dùng. Trong năm 2012, phạm vi của các hành động đã được mở rộng để bao gồm thao tác cuộn danh sách cũng như tương tác với các trường văn bản. Các dịch vụ hỗ trợ tiếp cận cũng có thể thực hiện các thao tác chung, như chuyển đến Màn hình chính, nhấn vào nút Quay lại, mở màn hình thông báo và danh sách ứng dụng gần đây. Kể từ năm 2012, Android bao gồm dịch vụ lấy tâm điểm hỗ trợ tiếp cận. Dịch vụ này giúp tất cả các phần tử hiển thị có thể chọn được bằng một dịch vụ hỗ trợ tiếp cận.

Các chức năng này cho phép nhà phát triển các dịch vụ hỗ trợ tiếp cận tạo chế độ thao tác thay thế (chẳng hạn như thao tác bằng cử chỉ) và giúp người dùng bị khuyết tật cải thiện khả năng điều khiển thiết bị chạy Android.

Lắng nghe cử chỉ

Các dịch vụ hỗ trợ tiếp cận có thể lắng nghe những cử chỉ cụ thể và phản hồi bằng cách hành động thay cho người dùng. Tính năng này đòi hỏi bạn kích hoạt yêu cầu dịch vụ hỗ trợ tiếp cận của tính năng Khám phá bằng cách chạm. Dịch vụ của bạn có thể yêu cầu kích hoạt bằng cách đặt thành phần flags trong thực thể AccessibilityServiceInfo của dịch vụ thành FLAG_REQUEST_TOUCH_EXPLORATION_MODE, như minh hoạ trong ví dụ sau.

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onCreate() {
        serviceInfo.flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE
    }
    ...
}

Java

public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onCreate() {
        getServiceInfo().flags = AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE;
    }
    ...
}

Sau khi dịch vụ của bạn yêu cầu kích hoạt tính năng Khám phá bằng cách chạm, người dùng phải cho phép bật tính năng này, nếu tính năng này chưa hoạt động. Khi tính năng này hoạt động, dịch vụ của bạn sẽ nhận được thông báo về các cử chỉ hỗ trợ tiếp cận thông qua phương thức gọi lại onGesture() của dịch vụ và có thể phản hồi bằng cách hành động thay cho người dùng.

Cử chỉ liên tục

Các thiết bị chạy Android 8.0 (API cấp 26) bao gồm dịch vụ hỗ trợ cho các cử chỉ liên tục hoặc các cử chỉ có lập trình chứa nhiều đối tượng Path.

Khi chỉ định một trình tự các nét, bạn có thể chỉ định rằng các nét đó thuộc cùng một cử chỉ có lập trình bằng cách sử dụng đối số cuối cùng willContinue trong hàm khởi tạo GestureDescription.StrokeDescription, như minh hoạ trong đoạn mã sau:

Kotlin

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private fun doRightThenDownDrag() {
    val dragRightPath = Path().apply {
        moveTo(200f, 200f)
        lineTo(400f, 200f)
    }
    val dragRightDuration = 500L // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    val dragDownPath = Path().apply {
        moveTo(400f, 200f)
        lineTo(400f, 400f)
    }
    val dragDownDuration = 500L
    val rightThenDownDrag = GestureDescription.StrokeDescription(
            dragRightPath,
            0L,
            dragRightDuration,
            true
    ).apply {
        continueStroke(dragDownPath, dragRightDuration, dragDownDuration, false)
    }
}

Java

// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.
private void doRightThenDownDrag() {
    Path dragRightPath = new Path();
    dragRightPath.moveTo(200, 200);
    dragRightPath.lineTo(400, 200);
    long dragRightDuration = 500L; // 0.5 second

    // The starting point of the second path must match
    // the ending point of the first path.
    Path dragDownPath = new Path();
    dragDownPath.moveTo(400, 200);
    dragDownPath.lineTo(400, 400);
    long dragDownDuration = 500L;
    GestureDescription.StrokeDescription rightThenDownDrag =
            new GestureDescription.StrokeDescription(dragRightPath, 0L,
            dragRightDuration, true);
    rightThenDownDrag.continueStroke(dragDownPath, dragRightDuration,
            dragDownDuration, false);
}

Để biết thêm thông tin, hãy xem video hội thảo What's new in Android accessibility (Có gì mới về tính năng Hỗ trợ tiếp cận của Android) tại Google I/O 2017, bắt đầu lúc 15:47.

Sử dụng thao tác hỗ trợ tiếp cận

Các dịch vụ hỗ trợ tiếp cận có thể hành động thay cho người dùng để đơn giản hoá các lượt tương tác với ứng dụng và giúp người dùng làm việc hiệu quả hơn. Chúng tôi đã thêm khả năng thực hiện các hành động cho các dịch vụ hỗ trợ tiếp cận trong năm 2011 và mở rộng đáng kể trong năm 2012.

Để thay người dùng thực hiện hành động, dịch vụ hỗ trợ tiếp cận phải đăng ký để nhận các sự kiện của các ứng dụng và yêu cầu quyền xem nội dung của ứng dụng đó bằng cách đặt android:canRetrieveWindowContent về true trong tệp cấu hình dịch vụ. Khi dịch vụ của bạn nhận được sự kiện, dịch vụ này có thể truy xuất đối tượng AccessibilityNodeInfo của sự kiện bằng cách sử dụng getSource(). Với đối tượng AccessibilityNodeInfo, dịch vụ của bạn có thể khám phá hệ phân cấp khung hiển thị để xác định hành động cần thực hiện và sau đó thực hiện hành động thay người dùng bằng performAction().

Kotlin

class MyAccessibilityService : AccessibilityService() {

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        // Get the source node of the event.
        event.source?.apply {

            // Use the event and node information to determine what action to
            // take.

            // Act on behalf of the user.
            performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)

            // Recycle the nodeInfo object.
            recycle()
        }
    }
    ...
}

Java

public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // Get the source node of the event.
        AccessibilityNodeInfo nodeInfo = event.getSource();

        // Use the event and node information to determine what action to take.

        // Act on behalf of the user.
        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);

        // Recycle the nodeInfo object.
        nodeInfo.recycle();
    }
    ...
}

Phương thức performAction() cho phép dịch vụ của bạn thực hiện hành động trong một ứng dụng. Nếu dịch vụ của bạn cần thực hiện một thao tác chung, chẳng hạn như chuyển đến Màn hình chính, nhấn vào nút Quay lại hoặc mở màn hình thông báo/danh sách ứng dụng gần đây, thì hãy sử dụng phương thức performGlobalAction().

Sử dụng các loại tâm điểm

Năm 2012, Android ra mắt một loại tâm điểm trong giao diện người dùng có tên là accessibility focus (tâm điểm hỗ trợ tiếp cận). Các dịch vụ hỗ trợ tiếp cận có thể sử dụng loại tâm điểm này để chọn bất kỳ phần tử hiển thị nào trên giao diện người dùng và thao tác với phần tử đó. Loại tâm điểm này khác với Tâm điểm nhập giúp xác định phần tử nào trong giao diện người dùng trên màn hình sẽ nhận dữ liệu đầu vào khi người dùng nhập ký tự, nhấn phím Enter trên bàn phím hoặc nhấn nút giữa của D-pad.

Một phần tử trong giao diện người dùng có thể có tâm điểm nhập trong khi một phần tử khác lại có tâm điểm hỗ trợ tiếp cận. Mục đích của tính năng tâm điểm hỗ trợ tiếp cận là cung cấp cho các dịch vụ hỗ trợ tiếp cận phương thức tương tác với phần tử hiển thị trên một màn hình, bất kể phần tử đó có thể lấy tâm điểm đầu vào từ góc nhìn hệ thống hay không. Để đảm bảo dịch vụ hỗ trợ tiếp cận tương tác chính xác với các phần tử đầu vào của ứng dụng, hãy làm theo nguyên tắc về kiểm thử khả năng hỗ trợ tiếp cận của ứng dụng để kiểm thử dịch vụ trong khi dùng một ứng dụng thông thường.

Dịch vụ hỗ trợ tiếp cận có thể xác định phần tử nào trong giao diện người dùng có tâm điểm nhập hoặc tâm điểm hỗ trợ tiếp cận sử dụng phương thức AccessibilityNodeInfo.findFocus(). Bạn cũng có thể tìm kiếm các phần tử có thể được chọn bằng tâm điểm nhập nhờ phương thức focusSearch(). Cuối cùng, dịch vụ hỗ trợ tiếp cận của bạn có thể đặt tâm điểm hỗ trợ tiếp cận bằng phương thức performAction(AccessibilityNodeInfo.ACTION_SET_ACCESSIBILITY_FOCUS).

Thu thập thông tin

Các dịch vụ hỗ trợ tiếp cận có các phương thức chuẩn để thu thập và trình bày các đơn vị chính của thông tin do người dùng cung cấp, chẳng hạn như thông tin chi tiết về sự kiện, văn bản và số.

Lấy thông tin chi tiết về thay đổi cửa sổ

Android 9 (API cấp 28) trở lên cho phép các ứng dụng theo dõi bản cập nhật của cửa sổ khi một ứng dụng vẽ lại nhiều cửa sổ cùng lúc. Khi sự kiện TYPE_WINDOWS_CHANGED xảy ra, hãy sử dụng API getWindowChanges() để xác định cách thay đổi của cửa sổ. Trong một lần cập nhật nhiều cửa sổ, mỗi cửa sổ tạo ra một nhóm sự kiện riêng. Phương thức getSource() trả về khung hiển thị gốc của cửa sổ liên kết với mỗi sự kiện.

Nếu một ứng dụng xác định tiêu đề của ngăn hỗ trợ tiếp cận cho các đối tượng View, thì dịch vụ của bạn có thể nhận ra khi giao diện người dùng của ứng dụng được cập nhật. Khi sự kiện TYPE_WINDOW_STATE_CHANGED xảy ra, hãy sử dụng các loại do getContentChangeTypes() trả về để xác định cách thay đổi của cửa sổ. Ví dụ: khung có thể phát hiện thời điểm một ngăn có tiêu đề mới hoặc thời điểm một ngăn biến mất.

Lấy thông tin chi tiết về sự kiện

Android cung cấp thông tin cho các dịch vụ hỗ trợ tiếp cận về hoạt động tương tác trên giao diện người dùng thông qua các đối tượng AccessibilityEvent. Trong các phiên bản Android trước, thông tin có sẵn trong một sự kiện hỗ trợ tiếp cận, dù cung cấp các thông tin chi tiết quan trọng về chế độ điều khiển giao diện người dùng do người dùng chọn, nhưng lại cung cấp thông tin ngữ cảnh ở mức hạn chế. Trong nhiều trường hợp, thông tin ngữ cảnh bị thiếu này có thể đóng vai trò rất quan trọng trong việc giúp bạn hiểu được ý nghĩa của chế độ điều khiển đã chọn.

Ví dụ về một giao diện mà ngữ cảnh có ý nghĩa quan trọng là lịch hoặc trình lập kế hoạch ngày. Nếu người dùng chọn khung giờ 4:00 chiều trong danh sách ngày từ thứ Hai đến thứ Sáu và dịch vụ hỗ trợ tiếp cận thông báo "4:00 chiều" nhưng không thông báo tên ngày trong tuần, ngày trong tháng hoặc tên tháng, thì kết quả phản hồi sẽ gây khó hiểu. Trong trường hợp này, ngữ cảnh điều khiển giao diện người dùng là yếu tố quan trọng đối với người dùng đang muốn lên lịch một cuộc họp.

Kể từ năm 2011, Android mở rộng đáng kể lượng thông tin mà một dịch vụ hỗ trợ tiếp cận có thể thu được về một hoạt động tương tác trên giao diện người dùng bằng cách tập hợp các sự kiện hỗ trợ tiếp cận dựa trên hệ phân cấp khung hiển thị. Hệ phân cấp khung hiển thị là một tập hợp gồm các thành phần giao diện người dùng có chứa thành phần (mẹ) và các thành phần giao diện người dùng có thể nằm trong thành phần đó (con). Bằng cách này, Android có thể cung cấp nhiều thông tin chi tiết hơn về các sự kiện hỗ trợ tiếp cận, cho phép các dịch vụ hỗ trợ tiếp cận cung cấp ý kiến phản hồi hữu ích hơn cho người dùng.

Dịch vụ hỗ trợ tiếp cận sẽ lấy thông tin về một sự kiện liên quan đến giao diện người dùng thông qua AccessibilityEvent mà hệ thống chuyển sang phương thức gọi lại onAccessibilityEvent() của dịch vụ đó. Đối tượng này cung cấp các thông tin chi tiết về sự kiện, bao gồm loại đối tượng đang được thực hiện, văn bản mô tả và các thông tin chi tiết khác.

  • AccessibilityEvent.getRecordCount()getRecord(int): các phương thức này cho phép bạn truy xuất tập hợp các đối tượng AccessibilityRecord góp phần vào AccessibilityEvent được hệ thống chuyển cho bạn. Cấp chi tiết này cung cấp thêm ngữ cảnh cho sự kiện kích hoạt dịch vụ hỗ trợ tiếp cận của bạn.

  • AccessibilityRecord.getSource(): phương thức này trả về đối tượng AccessibilityNodeInfo. Đối tượng này cho phép bạn yêu cầu hệ phân cấp bố cục khung hiển thị (mẹ và con) của thành phần tạo ra sự kiện hỗ trợ tiếp cận. Tính năng này cho phép dịch vụ hỗ trợ tiếp cận điều tra ngữ cảnh đầy đủ của một sự kiện, bao gồm nội dung và trạng thái của mọi khung hiển thị đính kèm hoặc khung hiển thị con.

Nền tảng Android cho phép AccessibilityService truy vấn hệ phân cấp khung hiển thị, thu thập thông tin về thành phần trên giao diện người dùng tạo ra sự kiện cùng thành phần mẹ và con của sự kiện đó. Để làm điều này, hãy đặt dòng sau trong cấu hình XML của bạn:

android:canRetrieveWindowContent="true"

Sau khi hoàn tất, hãy lấy đối tượng AccessibilityNodeInfo bằng getSource(). Lệnh gọi này chỉ trả về một đối tượng nếu cửa sổ nơi sự kiện bắt đầu vẫn là cửa sổ hoạt động. Nếu không, giá trị này sẽ trả về giá trị rỗng, vì vậy, hãy hành động phù hợp.

Trong ví dụ sau, đoạn mã sẽ thực hiện những việc sau đây khi nhận được một sự kiện:

  1. Ngay lập tức lấy thành phần chính của khung hiển thị mẹ nơi sự kiện này bắt đầu.
  2. Trong khung hiển thị đó, hãy tìm nhãn và hộp đánh dấu làm khung hiển thị con.
  3. Nếu tìm thấy, hãy tạo một chuỗi để báo cáo cho người dùng, cho biết nhãn và liệu nhãn có được đánh dấu chọn hay không.

Nếu tại bất kỳ thời điểm nào, một giá trị rỗng được trả về trong khi truyền tải hệ phân cấp khung hiển thị, thì phương thức này sẽ tự bỏ cuộc.

Kotlin

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

override fun onAccessibilityEvent(event: AccessibilityEvent) {

    val source: AccessibilityNodeInfo = event.source ?: return

    // Grab the parent of the view that fires the event.
    val rowNode: AccessibilityNodeInfo = getListItemNodeInfo(source) ?: return

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    val taskLabel: CharSequence = rowNode.getChild(0)?.text ?: run {
        rowNode.recycle()
        return
    }

    val isComplete: Boolean = rowNode.getChild(1)?.isChecked ?: run {
        rowNode.recycle()
        return
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.childCount < 2 || !rowNode.getChild(1).isCheckable) {
        rowNode.recycle()
        return
    }

    val completeStr: String = if (isComplete) {
        getString(R.string.checked)
    } else {
        getString(R.string.not_checked)
    }
    val reportStr = "$taskLabel$completeStr"
    speakToUser(reportStr)
}

Java

// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    AccessibilityNodeInfo source = event.getSource();
    if (source == null) {
        return;
    }

    // Grab the parent of the view that fires the event.
    AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
    if (rowNode == null) {
        return;
    }

    // Using this parent, get references to both child nodes, the label, and the
    // checkbox.
    AccessibilityNodeInfo labelNode = rowNode.getChild(0);
    if (labelNode == null) {
        rowNode.recycle();
        return;
    }

    AccessibilityNodeInfo completeNode = rowNode.getChild(1);
    if (completeNode == null) {
        rowNode.recycle();
        return;
    }

    // Determine what the task is and whether it's complete based on the text
    // inside the label, and the state of the checkbox.
    if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {
        rowNode.recycle();
        return;
    }

    CharSequence taskLabel = labelNode.getText();
    final boolean isComplete = completeNode.isChecked();
    String completeStr = null;

    if (isComplete) {
        completeStr = getString(R.string.checked);
    } else {
        completeStr = getString(R.string.not_checked);
    }
    String reportStr = taskLabel + completeStr;
    speakToUser(reportStr);
}

Bây giờ, bạn đã có dịch vụ hỗ trợ tiếp cận hoạt động hoàn chỉnh. Hãy thử định cấu hình cách dịch vụ đó tương tác với người dùng bằng cách thêm công cụ chuyển văn bản sang lời nói của Android hoặc sử dụng Vibrator để đưa ra phản hồi xúc giác.

Xử lý văn bản

Các thiết bị chạy Android 8.0 (API cấp độ 26) trở lên có một số tính năng xử lý văn bản giúp các dịch vụ hỗ trợ tiếp cận dễ dàng xác định và vận hành trên các đơn vị văn bản cụ thể xuất hiện trên màn hình.

Chú thích

Android 9 (API cấp 28) giới thiệu một số tính năng cho phép bạn truy cập vào phần chú thích trong giao diện người dùng của một ứng dụng. Hãy sử dụng getTooltipText() để đọc văn bản của phần chú thích, đồng thời sử dụng ACTION_SHOW_TOOLTIPACTION_HIDE_TOOLTIP để hướng dẫn các thực thể của View hiển thị hoặc ẩn phần chú thích của chúng.

Văn bản gợi ý

Kể từ năm 2017, Android sẽ bao gồm một số phương thức để tương tác với văn bản gợi ý của đối tượng dựa trên văn bản:

  • Phương thức isShowingHintText()setShowingHintText() cho biết và đặt lần lượt, liệu nội dung văn bản hiện tại của nút có đại diện cho văn bản gợi ý của nút hay không.
  • getHintText() cung cấp quyền truy cập vào văn bản gợi ý. Ngay cả khi một đối tượng hiện không hiển thị văn bản gợi ý, thì việc gọi đến getHintText() vẫn thành công.

Vị trí của các ký tự văn bản trên màn hình

Trên các thiết bị chạy Android 8.0 (API cấp 26) trở lên, các dịch vụ hỗ trợ tiếp cận có thể xác định toạ độ màn hình cho hộp ràng buộc của từng ký tự hiển thị trong một tiện ích TextView. Các dịch vụ tìm những toạ độ này bằng cách gọi refreshWithExtraData(), chuyển vào EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY làm đối số đầu tiên và một đối tượng Bundle làm đối số thứ hai. Khi phương thức này thực thi, hệ thống sẽ điền đối số Bundle bằng một dải các đối tượng Rect có thể đóng gói. Mỗi đối tượng Rect đại diện cho hộp giới hạn của một ký tự cụ thể.

Giá trị trong dải một chiều được chuẩn hoá

Một số đối tượng AccessibilityNodeInfo sử dụng thực thể của AccessibilityNodeInfo.RangeInfo để cho biết rằng phần tử trên giao diện người dùng có thể lấy một dải giá trị. Khi tạo dải giá trị sử dụng RangeInfo.obtain() hoặc khi truy xuất các giá trị cực trị của dải giá trị sử dụng getMin()getMax(), hãy lưu ý các thiết bị chạy Android 8.0 (API cấp 26) trở lên đại diện cho các dải giá trị theo cách chuẩn hoá:

  • Đối với các dải không có giá trị tối thiểu, Float.NEGATIVE_INFINITY đại diện cho giá trị tối thiểu.
  • Đối với các dải không có giá trị tối đa, Float.POSITIVE_INFINITY đại diện cho giá trị tối đa.

Phản hồi các sự kiện hỗ trợ tiếp cận

Hiện dịch vụ của bạn đã được thiết lập để chạy và theo dõi các sự kiện, hãy viết một số mã để ứng dụng biết việc cần làm khi AccessibilityEvent đến. Bắt đầu bằng cách ghi đè phương thức onAccessibilityEvent(AccessibilityEvent). Trong phương thức đó, sử dụng getEventType() để xác định loại sự kiện và getContentDescription() để trích xuất bất kỳ văn bản nhãn nào liên kết với khung hiển thị đã kích hoạt sự kiện:

Kotlin

override fun onAccessibilityEvent(event: AccessibilityEvent) {
    var eventText: String = when (event.eventType) {
        AccessibilityEvent.TYPE_VIEW_CLICKED -> "Clicked: "
        AccessibilityEvent.TYPE_VIEW_FOCUSED -> "Focused: "
        else -> ""
    }

    eventText += event.contentDescription

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText)
    ...
}

Java

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    final int eventType = event.getEventType();
    String eventText = null;
    switch(eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "Clicked: ";
            break;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            eventText = "Focused: ";
            break;
    }

    eventText = eventText + event.getContentDescription();

    // Do something nifty with this text, like speak the composed string back to
    // the user.
    speakToUser(eventText);
    ...
}

Tài nguyên khác

Để tìm hiểu thêm, hãy xem các tài nguyên sau:

Hướng dẫn

Lớp học lập trình