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

Stay organized with collections Save and categorize content based on your preferences.

Dịch vụ hỗ trợ tiếp cận là một ứng dụng cung cấp các tính năng nâng cao cho 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 tính năng của 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 lớn có thể cần ý kiến phản hồi thêm hoặc khác về 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 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 về việc xây dựng một dịch vụ hỗ trợ tiếp cận.

Lưu ý: Ứng dụng của bạn chỉ nên sử dụng các dịch vụ hỗ trợ tiếp cận ở cấp nền tảng để giúp người dùng khuyết tật tương tác với ứng dụng đó.

Chúng tôi đã giới thiệu khả năng xây dựng và triển khai các dịch vụ hỗ trợ tiếp cận với Android 1.6 (API cấp độ 4) và đã cải tiến đáng kể bằng Android 4.0 (API cấp độ 14). Thư viện hỗ trợ Android cũng được cập nhật với bản phát hành Android 4.0 để hỗ trợ các tính năng hỗ trợ tiếp cận nâng cao này cho Android 1.6. Các nhà phát triển hướng đến các dịch vụ hỗ trợ tiếp cận tương thích rộng rãi nên sử dụng Thư viện hỗ trợ và phát triển các tính năng hỗ trợ tiếp cận nâng cao hơn được giới thiệu trong Android 4.0.

Tạo dịch vụ hỗ trợ tiếp cận của bạ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. 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 dịch vụ này và không có kế hoạch liên kết một ứng dụng với dịch vụ, bạn có thể xóa lớp hoạt động ban đầu 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à dịch vụ hỗ trợ tiếp cận. Phần này giải thích các tùy chọn 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

Để được coi là dịch vụ hỗ trợ tiếp cận, bạn phải thêm phần tử service (thay vì phần tử activity) trong phần tử application trong tệp kê khai. Ngoài ra, trong phần tử service, bạn cũng phải sử dụng bộ lọc ý định của dịch vụ hỗ trợ tiếp cận. Để tương thích với Android 4.1 trở lên, tệp kê khai cũng phải bảo vệ dịch vụ 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 tệp đó. 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ác bản khai báo này là bắt buộc đối với tất cả các dịch vụ hỗ trợ tiếp cận được triển khai trên Android 1.6 (API cấp độ 4) trở lên.

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

Dịch vụ hỗ trợ tiếp cận cũng 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ụ xử lý và thông tin bổ sung về dịch vụ đó. Cấu hình của 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 sử dụng bản sao của lớp này và setServiceInfo() trong thời gian chạy. Tuy nhiên, không phải tất cả các tùy chọn cấu hình đều khả dụng bằng phương pháp này.

Kể từ Android 4.0, bạn có thể đưa phần tử <meta-data> vào tệp kê khai với tham chiếu đến tệp cấu hình. Tệp này cho phép bạn đặt đầy đủ các tùy chọn cho dịch vụ hỗ trợ tiếp cận, như hiển thị 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 (siêu dữ liệu) này chỉ tệp XML mà bạn tạo trong thư mục tài nguyên <project_dir>/res/xml/accessibility_service_config.xml của ứng dụng. Mã sau đây hiển thị nội dung mẫu 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 dịch vụ hỗ trợ tiếp cận, hãy truy cập các liên kết đến tài liệu tham khảo này:

Để biết thêm thông tin về các 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.

Hãy cấu hình dịch vụ hỗ trợ tiếp cận của bạn

Việc đặt các biến cấu hình cho dịch vụ hỗ trợ tiếp cận sẽ cho hệ thống biết cách thức và thời điểm bạn muốn thực hiện việc này. Bạn muốn phản hồi những loại sự kiện nào? Dịch vụ cần hoạt động (active)cho tất cả ứng dụng hay chỉ những tên gói cụ thể? Nó sử dụng các loại phản hồi nào?

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

Kotlin

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

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

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

        // Default services are invoked only if no package-specific ones are present
        // for the type of AccessibilityEvent generated. This service *is*
        // application-specific, so the flag isn't necessary. If this was a
        // general-purpose service, it would be worth considering 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
    // won't be passed to this service.
    info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
            AccessibilityEvent.TYPE_VIEW_FOCUSED;

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

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

    // Default services are invoked only if no package-specific ones are present
    // for the type of AccessibilityEvent generated. This service *is*
    // application-specific, so the flag isn't necessary. If this was a
    // general-purpose service, it would be worth considering setting the
    // DEFAULT flag.

    // info.flags = AccessibilityServiceInfo.DEFAULT;

    info.notificationTimeout = 100;

    this.setServiceInfo(info);

}

Tùy chọn thứ hai là định cấu hình dịch vụ bằng tệp XML. Một số tùy 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 tùy chọn cấu hình tương tự ở trên, được xác định bằng XML, sẽ có dạng như sau:

<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 đi theo tuyến XML, hãy nhớ tham chiếu tuyến này trong tệp kê khai bằng cách thêm thẻ <meta-data> vào thông tin khai báo dịch vụ, trỏ vào 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 từ lớp đó. Các phương thức này được trình bày theo thứ tự mà hệ thống Android gọi, từ khi bắt đầu cung cấp dịch vụ (onServiceConnected()), trong khi ứng dụng đang chạy (onAccessibilityEvent(), onInterrupt()) đến thời gian ngừng hoạt động (onUnbind()).

  • onServiceConnected() – (không bắt buộc) Hệ thống này gọi phương thức này khi kết nối thành công với dịch vụ hỗ trợ tiếp cận của bạn. 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 dịch vụ trong thời gian chạy hoặc thực hiện điều chỉnh một lần, đây là vị trí thuận tiện để gọi setServiceInfo().
  • onAccessibilityEvent() – (bắt buộc) Phương thức này được hệ thống gọi lại khi phát hiện một AccessibilityEvent khớp với thông số lọc sự kiện do dịch vụ hỗ trợ tiếp cận của bạn chỉ định. Ví dụ: khi người dùng nhấp vào một nút hoặc tập trung vào một quyền kiểm soát giao diện người dùng trong một ứng dụng mà dịch vụ hỗ trợ tiếp cận của bạn cung cấp phản hồi. Khi điều này xảy ra, hệ thống sẽ gọi phương thức này, chuyển AccessibilityEvent được liên kết mà dịch vụ có thể diễn giải và sử dụng để cung cấp 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) Phương thức này được gọi khi hệ thống muốn làm gián đoạ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 tiêu điểm đến một tùy chọn đ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) Phương thức này được gọi khi hệ thống sắp ngừng cung cấp dịch vụ hỗ trợ tiếp cận. 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 hủy phân bổ 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 của 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 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 nội dung Nhận 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 thông 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ý. Việc có thể chỉ định thông tin này cho phép các dịch vụ hỗ trợ tiếp cận hợp tác với nhau và cho phép bạn, với tư cách nhà phát triển, có được sự linh hoạt khi xử lý các loại sự kiện cụ thể từ ứ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 thông số này bị bỏ qua, thì 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ụ cho mọi ứng dụng. Bạn có thể đặt thông số này trong các tệp cấu hình dịch vụ hỗ trợ tiếp cận, trong đó có 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 đặt bằng thành viê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 thông số này trong tệp cấu hình dịch vụ hỗ trợ tiếp cận bằng thuộc tính android:accessibilityEventTypes dưới dạng danh sách được phân tách bằng ký tự | (ví dụ:accessibilityEventTypes="typeViewClicked|typeViewFocused") hoặc thiết lập bằng thành viên AccessibilityServiceInfo.eventTypes.

Khi thiết lập dịch vụ hỗ trợ tiếp cận, hãy cân nhắc kỹ 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 thể xử lý. 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 dịch vụ 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 độ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 (stream) này bằng cách đặt tùy 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 bản sao AudioManager của thiết bị.

Đoạn mã sau đây minh họa cách một dịch vụ hỗ trợ tiếp cận có thể sử dụng thư 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 Có gì mới về Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu từ 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 để bật và tắt bất kỳ dịch vụ nào (bao gồm cả dịch vụ của bạn) được cài đặt trên thiết bị.

Để 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, khi bắt đầu chạy.

Để biết thêm thông tin, hãy xem video hội thảo Có gì mới về Hỗ trợ tiếp cận của Android tại Google I/O 2017, bắt đầu từ 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 kết xuất bằng phần mềm và đang chạy Android 8.0 (API cấp độ 26) trở lên, bạn sẽ thấy nút hỗ trợ tiếp cận ở bên phải thanh điều hướng. 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, tùy thuộc vào nội dung hiện đ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 trong 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().

Lưu ý: Tính năng này chỉ có trên các thiết bị cung cấp khu vực điều hướng kết xuất bằng phần mềm. Các dịch vụ phải luôn sử dụng isAccessibilityButtonAvailable() và phản hồi các thay đổi dựa trên khả năng sử dụng nút hỗ trợ tiếp cận bằng cách triển khai onAvailabilityChanged(). Bằng cách đó, người dùng luôn có thể truy cập chức năng của dịch vụ này, ngay cả khi nút hỗ trợ tiếp cận không được hỗ trợ hoặc không hoạt động.

Đoạn mã sau đây minh họa cách bạn có thể định cấu hình 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ề Hỗ trợ tiếp cận của Android từ Google I/O 2017, bắt đầu từ 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ơ chế nhập liệu thay thế, vuốt theo hướng (lên, xuống, sang trái và sang phải) dọc theo cảm biến vân tay của thiết bị. Để định cấu hình 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_FINGERPRINT 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().

Lưu ý: Bạn nên cho phép người dùng tắt tính năng hỗ trợ dịch vụ hỗ trợ tiếp cận cho các cử chỉ vân tay. Mặc dù nhiều dịch vụ hỗ trợ tiếp cận có thể nghe đồng thời các cử chỉ vân tay, nhưng làm như vậy sẽ khiến các dịch vụ xung đột với nhau.

Lưu ý không phải mọi thiết bị đều có cảm biến vân tay. Để xác định 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 đang sử dụng cảm biến vân tay cho mục đích xác thực. Để xác định thời điểm có 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"
    ... />

MyFingerprintGestureService.java

Kotlin

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

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ề Hỗ trợ tiếp cận của Android từ Google I/O 2017, bắt đầu từ 9:03.

Chuyển văn bản thành giọng 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 đặt tất cả các chuỗi trong các đối tượng LocaleSpan, như minh họa 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 Có gì mới về Hỗ trợ tiếp cận của Android từ Google I/O 2017, bắt đầu từ 10:59.

Thực hiện tác vụ cho người dùng

Kể từ Android 4.0 (API cấp độ 14), các dịch vụ hỗ trợ tiếp cận có thể hành động thay mặt người dùng, bao gồm cả việc thay đổi tiêu điểm nhập và chọn (kích hoạt) các thành phần trên giao diện người dùng. Trong Android 4.1 (API cấp độ 16), phạm vi của các hành động đã được mở rộng để bao gồm danh sách cuộn và 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 (chẳng hạn như chuyển đến Màn hình chính), nhấn nút Quay lại, mở màn hình thông báo và danh sách ứng dụng gần đây. Android 4.1 cũng bao gồm một loại tiêu điểm mới là Accessibility Focus (Tiêu đ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.

Những chức năng mới này giúp nhà phát triển các dịch vụ hỗ trợ tiếp cận có thể tạo các chế độ điều hướng thay thế như thao tác bằng cử chỉ và giúp người dùng khuyết tật cải thiện khả năng kiểm soát thiết bị Android.

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 mặt cho người dùng. Tính năng này được thêm trong Android 4.1 (API cấp độ 16) và đòi hỏi bạn kích hoạt 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 viên flags của phiên bản AccessibilityServiceInfo của dịch vụ thành FLAG_REQUEST_TOUCH_EXPLORATION_MODE, như minh họa 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ử 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 thực hiện các thao tác đối với 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 hỗ trợ cho các cử chỉ tiếp tục hoặc các cử chỉ có lập trình chứa nhiều đối tượng Path.

Khi chỉ định trình tự của các thao tác (stroke), bạn phải chỉ định rằng chúng thuộc về 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ư trong đoạn mã sau đây:

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 Có gì mới về Hỗ trợ tiếp cận của Android từ Google I/O 2017, bắt đầu từ 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 mặt người dùng để khiến việc tương tác với các ứng dụng trở nên đơn giản và 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 Android 4.0 (API cấp độ 14) và mở rộng đáng kể bằng Android 4.1 (API cấp độ 16).

Để thay mặt 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 từ một vài hoặc nhiều ứ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 trongtệ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 từ 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 chế độ xem để xác định hành động cần thực hiện và sau đó thực hiện hành động thay mặt người dùng sử dụ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

            // take action 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

        // take action 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 nút Quay lại, mở màn hình thông báo hoặc danh sách ứng dụng gần đây, hãy sử dụng performGlobalAction().

Sử dụng các loại tiêu điểm

Android 4.1 (API cấp độ 16) giới thiệu một loại trọng tâm giao diện người dùng mới có tên Accessibility Focus (Tiêu đ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 tiêu điểm này để chọn bất kỳ phần tử giao diện người dùng nào hiển thị và thao tác trên phần tử đó. Loại tiêu điểm này khác với Tiêu điểm nhập vốn phổ biến hơn và giúp xác định phần tử giao diện người dùng trên màn hình 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 bảng điều khiển D-pad.

Accessibility Focus (Tiêu điểm hỗ trợ tiếp cận) hoàn toàn riêng biệt và độc lập với Input Focus (Tiêu điểm nhập). Trên thực tế, có thể một phần tử trong giao diện người dùng có Tiêu điểm hỗ trợ nhập trong khi một phần tử khác có Tiêu điểm hỗ trợ tiếp cận. Mục đích của tính năng Accessibility Focus (Tiêu đ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 bất kỳ phần tử hiển thị nào trên màn hình, bất kể phần tử đó có thể lấy tiêu điểm đầu vào từ góc nhìn hệ thống hay không. Bạn có thể xem tiêu điểm hỗ trợ tiếp cận trong thực tế bằng cách kiểm thử các cử chỉ hỗ trợ tiếp cận. Để biết thêm thông tin về việc kiểm thử tính năng này, xem phần Kiểm thử thao tác bằng cử chỉ.

Lưu ý: Các dịch vụ Hỗ trợ tiếp cận sử dụng Accessibility Focus (Tiêu điểm hỗ trợ tiếp cận) sẽ chịu trách nhiệm đồng bộ hóa Input Focus (Tiêu điểm đầu vào) hiện tại khi một phần tử có thể dùng loại tiêu điểm này. Các dịch vụ không đồng bộ hóa Input Focus (Tiêu điểm đầu vào) với Accessibility Focus (Tiêu điểm hỗ trợ truy cập) có nguy cơ gây ra sự cố trong các ứng dụng yêu cầu tiêu điểm đầu vào sẽ nằm ở một vị trí cụ thể khi thực hiện một số thao tác nhất định.

Dịch vụ hỗ trợ tiếp cận có thể xác định phần tử giao diện người dùng nào có Input Focus (Tiêu điểm đầu vào) hoặc Accessibility Focus (Tiêu đ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ính năng Input Focus (Tiêu điểm đầu vào) bằng 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 Accessibility Focus (Tiêu đ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ũng 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ề sự kiện

Hệ thống Android cung cấp thông tin cho các dịch vụ hỗ trợ tiếp cận về tương tác của giao diện người dùng thông qua các đối tượng AccessibilityEvent. Trước phiên bản Android 4.0, thông tin có sẵn trong một sự kiện hỗ trợ tiếp cận, đồng thời việc cung cấp lượng lớn thông tin chi tiết về một tùy chọn kiểm soát giao diện người dùng mà người dùng chọn đã cung cấp rất ít thông tin theo ngữ cảnh. 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 tùy chọn kiểm soát đã 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 thứ Hai đến thứ Sáu và dịch vụ hỗ trợ tiếp cận thông báo "4 giờ 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 là rất khó hiểu. Trong trường hợp này, ngữ cảnh kiểm soát 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 cuộc họp.

Phiên bản Android 4.0 mở rộng đáng kể lượng thông tin mà dịch vụ hỗ trợ tiếp cận có thể có được về một tương tác trên giao diện người dùng bằng cách soạn các sự kiện hỗ trợ tiếp cận dựa trên hệ phân cấp chế độ xem. Hệ phân cấp chế độ xem là tập hợp 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, hệ thống 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ề sự kiện giao diện người dùng thông qua một AccessibilityEvent mà hệ thống chuyển cho 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. Bắt đầu từ Android 4.0 (và được hỗ trợ trong các bản phát hành trước đây thông qua đối tượng AccessibilityEventCompat trong Thư viện hỗ trợ), bạn có thể lấy thêm thông tin về sự kiện bằng cách sử dụng các lệnh gọi sau:

  • 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 đã đóng góp cho AccessibilityEvent mà 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.
  • AccessibilityEvent.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 chế độ xem (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 chế độ xem đính kèm hoặc chế độ xem con.

    Lưu ý quan trọng: Khả năng điều tra hệ phân cấp chế độ xem từ một AccessibilityEvent có thể làm hiển thị thông tin riêng tư của người dùng cho dịch vụ hỗ trợ tiếp cận của bạn. Vì lý do này, dịch vụ của bạn phải yêu cầu cấp truy cập này thông qua tệp XML cấu hình dịch vụ hỗ trợ tiếp cận bằng cách bao gồm thuộc tính canRetrieveWindowContent và đặt thuộc tính này là true. Nếu bạn không đưa tùy chọn cài đặt này vào tệp xml cấu hình dịch vụ, các lệnh gọi đến getSource() sẽ không thực hiện được.

    Lưu ý: Trong Android 4.1 (API cấp độ 16) trở lên, phương thức getSource(), cũng như AccessibilityNodeInfo.getChild()getParent() trả về chỉ các đối tượng view được coi là quan trọng đối với khả năng hỗ trợ tiếp cận (các chế độ xem thu hút nội dung hoặc phản hồi thao tác của người dùng). Nếu cần có tất cả các chế độ xem, dịch vụ của bạn có thể yêu cầu các chế độ xem đó bằng cách đặt thành viên flags của bản sao AccessibilityServiceInfo dịch vụ thành FLAG_INCLUDE_NOT_IMPORTANT_VIEWS.

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 các cửa sổ thay đổi. 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ề chế độ xem 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ậ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 cửa sổ đã thay đổi. Ví dụ: khung có thể phát hiện khi một ngăn có tiêu đề mới hoặc khi một ngăn đã biến mất.

Thu thập thông tin chi tiết về nút hỗ trợ tiếp cận

Bước này không bắt buộc nhưng rất hữu ích. Nền tảng Android cho phép AccessibilityService truy vấn hệ phân cấp chế độ xem, thu thập thông tin về thành phần giao diện người dùng đã tạo ra sự kiện cùng chế độ xem mẹ và con của sự kiện đó. Để làm điều này, hãy đảm bảo bạn đặt dòng sau trong cấu hình XML của mình:

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 nguồn 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. Ví dụ sau là một đoạn mã mà khi nhận được sự kiện, đoạn mã đó sẽ:

  1. Ngay lập tức lấy ảnh chính chế độ xem mẹ nơi khởi tạo sự kiện
  2. Trong chế độ xem đó, tìm nhãn và hộp đánh dấu là chế độ xem 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.
  4. 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 chế độ xem, 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 fired 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 or not it's complete, based on
    // the text inside the label, and the state of the check-box.
    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 fired 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 or not it's complete, based on
    // the text inside the label, and the state of the check-box.
    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 nó 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ú giải công cụ

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 chú giải công cụ trong giao diện người dùng của ứng dụng. Sử dụng getTooltipText() để đọc văn bản của chú giải công cụ, đồng thời sử dụng ACTION_SHOW_TOOLTIP ACTION_HIDE_TOOLTIP để hướng dẫn các bản sao của View hiển thị hoặc ẩn chú giải công cụ của chúng.

Văn bản gợi ý

Android 8.0 (API cấp độ 26) 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.
  • Để truy cập phần văn bản gợi ý, hãy sử dụng getHintText(). Ngay cả khi một đối tượng hiện không hiển thị văn bản gợi ý, thì lệnh 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 tọa độ 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 tọa độ 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 hóa

Một số đối tượng AccessibilityNodeInfo sử dụng bản sao AccessibilityNodeInfo.RangeInfo để cho biết rằng phần tử 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 (cấp API độ 26) trở lên đại diện cho các dải giá trị theo cách chuẩn hóa:

  • Đố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 sẽ biểu thị 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 thực sự đế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 chế độ xem đã 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 tận dụng các tài nguyên sau đây:

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