Computer >> Hướng Dẫn Máy Tính >  >> Hệ Thống >> Android

Tạo Hoạt Hình Android Với Kotlin Dễ Dàng Truy Cập: Hướng Dẫn Thực Tế

Khi tìm kiếm ví dụ cho đóng góp Android đầu tiên, rất ít ví dụ về hoạt hình được viết bằng Kotlin. Cũng rất ít mẫu code minh họa các cân nhắc về khả năng truy cập trong hoạt hình gốc.

Vậy chúng ta hãy bắt đầu! Hãy xem cách viết hoạt hình 'mở rộng' gốc bằng Kotlin, và thảo luận về cách hỗ trợ người dùng bật TalkBack hoặc phóng to văn bản. Tất cả code có sẵn trong kho ví dụ này, tạo một activity đơn lẻ với view có hoạt hình bên trong. Code này được viết chung với Calum Turner.

Tạo Hoạt Hình Android Với Kotlin Dễ Dàng Truy Cập: Hướng Dẫn Thực Tế

GIF kết quả ứng dụng cuối cùng

Khả năng truy cập Android (a11y)

Tất cả thiết bị Android đều có sẵn trình đọc màn hình tên là TalkBack. Tính năng này có thể bật từ cài đặt thiết bị, và có hướng dẫn sử dụng lần đầu tiên tích hợp sẵn. Người dùng dùng cử chỉ để điều hướng trang, và mô tả các phần tử được tập trung sẽ được đọc to. Nếu không có TalkBack, ứng dụng sẽ không thể sử dụng được đối với nhiều người khiếm thị.

Điểm quan trọng nhất là các phần tử đúng phải có thể tập trung (focusable), có mô tả, và các thay đổi trên view phải được thông báo.

Trong cùng menu cài đặt, cỡ chữ cơ bản mặc định có thể điều chỉnh, tỷ lệ từ 1.0. View nên phản ứng với thay đổi cỡ chữ này, với tất cả các phần tử vẫn hiện diện và hoạt động.

Bố cục (Layout)

Chúng ta không đi sâu vào chi tiết style của layout ở đây vì chúng khá riêng biệt cho ví dụ này, nhưng các chi tiết về khả năng truy cập đáng để nhấn mạnh.

Hai thuộc tính được sử dụng: android:contentDescription và android:importantForAccessibility.

contentDescription là nội dung được đọc khi phần tử nhận focus. Đối với bất kỳ ImageView nào nhận focus, điều này là thiết yếu, nếu không trình đọc màn hình sẽ đọc 'unlabelled' (không có nhãn) vô nghĩa cho người dùng.

Nếu đây là một button, mặc định nó sẽ đọc '<mô tả> button, double tap to activate' (nhấn đúp để kích hoạt), nhưng đối với ImageView icon của chúng ta, chúng ta chỉ định thủ công hành động vì không có mặc định này.

android:contentDescription="tap to toggle extra person information"

Chúng ta cũng dùng importantForAccessibility:no để TẮT focus cho TextView '+', vì văn bản dưới hai badge đã cung cấp mô tả, nên dấu '+' nếu được đọc to sẽ gây nhầm lẫn hơn là hữu ích.

Đối với cả hai trường hợp, kiểm thử thủ công trên thiết bị thật với TalkBack bật là cách tốt nhất để biết ngữ cảnh có hợp lý khi không có hình ảnh hay không.

Hoạt hình mở rộng (Expand Animation)

Hoạt hình của chúng ta sẽ kích hoạt khi nhấn icon 'info', bật/tắt việc mở rộng phần chi tiết.

Chúng ta sẽ làm tất cả bên trong một activity đơn lẻ để tập trung chỉ vào code hoạt hình. Trong ứng dụng thực tế, view được áp dụng hơn là nằm trong fragment hoặc recycler view riêng, nên cấu trúc code trừu tượng hơn sẽ được dùng.

Thiết lập listener

Trong onCreate của activity ví dụ, trước tiên phải đặt listener cho icon và truyền vào view cần bật/tắt.

infoIcon.setOnClickListener { toggleCardBody(root.personEntryBody) }

Chúng ta cũng thiết lập một biến trong class để theo dõi xem view có được bật/tắt không, đặt nó ban đầu là đóng.

private var isToggled = false

Bật/tắt hoạt hình mở rộng

Trong layout, chúng ta đã đặt chiều cao của personEntryBody là 0dp.

Để bật mở nó, chúng ta cần biết chiều cao mới để đặt, hoạt hình nên kéo dài bao lâu, và chiều cao nên là bao nhiêu tại từng thời điểm của hoạt hình.

Sau đó cần đặt isToggled thành giá trị ngược lại, và đảm bảo khi nhấn lại nó làm ngược lại.

private fun toggleCardBody(body: View) { body.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) val maxHeight = body.measuredHeight + body.paddingTop + body.paddingBottom val startHeight = if (isToggled) maxHeight else 0 val targetHeight = if (isToggled) 0 else maxHeight val expandAnimator = ValueAnimator .ofInt(startHeight, targetHeight) .setDuration(200) expandAnimator.addUpdateListener { val value = it.animatedValue as Int body.layoutParams.height = value body.requestLayout() } expandAnimator.doOnEnd { isToggled = !isToggled } expandAnimator.start() }

Vì chiều cao khi view được vẽ ban đầu là 0, chúng ta phải tính toán kích thước mới bằng cách đo lại layout của nó.

Như mô tả trong tài liệu layout view Android, chúng ta có thể dùng measure() cùng với layout params đã gán cho view để đo lại mỗi khi icon info được nhấn.

Để tính chiều cao tối đa, chúng ta phải thủ công cộng thêm padding trên và dưới vào đây, vì chúng không được bao gồm trong measured height.

Tùy thuộc vào isToggled, chúng ta biết là bắt đầu từ 0 hay từ chiều cao tối đa đã mở rộng, và do đó chiều cao đích đối lập.

Chúng ta dùng Value Animator để di chuyển từ giá trị bắt đầu đến giá trị đích kết thúc, và đặt thời lượng tính bằng ms. Thời lượng này dựa hoàn toàn trên kiểm thử thủ công sau này cho cảm giác UX.

ValueAnimator .ofInt(startHeight, targetHeight) .setDuration(200)

Chúng ta gắn thời lượng với chiều cao bằng một update listener, yêu cầu vẽ layout mới sau mỗi cập nhật và điều chỉnh chiều cao mỗi lần.

 expandAnimator.addUpdateListener { val value = it.animatedValue as Int body.layoutParams.height = value body.requestLayout() } expandAnimator.doOnEnd { isToggled = !isToggled } expandAnimator.start()

Vì dùng Kotlin, chúng ta cũng thêm thư viện androidx vào build.gradle để tận dụng extension doOnEnd. Điều này cho phép dễ dàng đảo ngược biến isToggled.

Cuối cùng chúng ta bắt đầu hoạt hình! Đã có một body co giãn khi chạm vào icon!

Hoạt hình mượt mà hơn

Mặc dù hoạt hình kỹ thuật đã hoạt động, một bước thêm hay là thêm interpolator để chuyển động tự nhiên hơn.

expandAnimator.interpolator = FastOutSlowInInterpolator()

Tăng cường khả năng truy cập

Chúng ta sẽ thêm hai điều cuối cùng để giúp người dùng a11y.

Thứ nhất, hỗ trợ điều hướng bằng AccessibilityEvent.

expandAnimator.doOnEnd { if (!isToggled) body.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) isToggled = !isToggled }

Điều này nghĩa là khi hoạt hình chuyển từ đóng sang mở, focus sẽ ngay lập tức nhảy đến phần tử đầu tiên trong body, ở đây là mô tả. Trong layout, chúng ta đặt mô tả hành động của icon info, nhưng vì có thể không thể dựa vào chỉ báo thị giác để người dùng chuyển đến phần tử tiếp theo, chúng ta có thể xử lý giúp họ.

Thứ hai, cho phép cỡ chữ khác nhau. Chiều cao đo được từ measure() không tính đến tỷ lệ phóng to chữ cài trong cài đặt truy cập của thiết bị, nên khi tỷ lệ lớn, phần dưới của mô tả sẽ bị cắt vì quá lớn để vừa.

Chúng ta có thể truy cập tỷ lệ chữ theo chương trình, và scale chiều cao dựa trên tỷ lệ này. Chuyển thành integer vì tỷ lệ chữ có thể ra float, không dùng được làm chiều cao layout.

val a11yFontScale = body.context.resources.configuration.fontScale val maxHeight = ((body.measuredHeight + body.paddingTop + body.paddingBottom) * a11yFontScale).toInt()

Hoàn thành!

Tạo Hoạt Hình Android Với Kotlin Dễ Dàng Truy Cập: Hướng Dẫn Thực Tế

GIF kết quả ứng dụng cuối cùng

Vậy là xong, chúng ta đã hoàn thành hoạt hình cuối cùng! Chỉ với vài dòng code thêm, chúng ta đã tăng đáng kể độ phủ a11y và có một phần mở rộng mượt mà hiển thị badge Kotlin và Android 🎉

Cảm ơn đã đọc 😊

Dưới đây là vài bài viết gần đây của tôi:

  • Tùy chỉnh CodeceptJS E2E tests
  • Testing React with Jest and Enzyme II

Tham khảo thêm hữu ích

  • Bài viết tuyệt vời của Joe Birch về Exploring KTX for Android
  • Android Accessibility Tutorial: Getting Started