Hỗ trợ thiết bị có thể gập lại 3 lần và thiết bị có thể gập lại ở chế độ ngang

Một thiết bị có thể gập lại ở chế độ ngang, ở tư thế đóng và mở hoàn toàn, nằm bên cạnh một thiết bị có 3 màn hình ở tư thế đóng và mở hoàn toàn.

Nhà phát triển thường gặp phải những khó khăn riêng khi tạo ứng dụng cho thiết bị có thể gập lại, đặc biệt là những thiết bị như Samsung Trifold hoặc Pixel Fold nguyên bản, mở ở định dạng ngang (rotation_0 = landscape). Các lỗi của nhà phát triển bao gồm:

  • Giả định sai về hướng thiết bị
  • Các trường hợp sử dụng bị bỏ qua
  • Không tính toán lại hoặc lưu vào bộ nhớ đệm các giá trị trong quá trình thay đổi cấu hình

Sau đây là một số vấn đề cụ thể liên quan đến thiết bị:

  • Hướng tự nhiên của thiết bị không khớp giữa màn hình ngoài và màn hình trong (giả định dựa trên rotation_0 = portrait), khiến các ứng dụng gặp lỗi khi gập và mở thiết bị
  • Mật độ màn hình khác nhau và cách xử lý thay đổi cấu hình mật độ không chính xác
  • Các vấn đề về bản xem trước của camera do cảm biến camera phụ thuộc vào hướng tự nhiên

Để mang lại trải nghiệm chất lượng cao cho người dùng trên thiết bị có thể gập lại, hãy tập trung vào các khía cạnh quan trọng sau:

  • Xác định hướng của ứng dụng dựa trên vùng màn hình thực tế mà ứng dụng chiếm, chứ không phải hướng thực của thiết bị
  • Cập nhật bản xem trước của camera để quản lý chính xác hướng thiết bị và tỷ lệ khung hình, tránh bản xem trước bị lệch sang một bên và ngăn hình ảnh bị kéo giãn hoặc cắt
  • Duy trì tính liên tục của ứng dụng trong quá trình gập hoặc mở thiết bị bằng cách giữ lại trạng thái bằng ViewModel hoặc các phương pháp tương tự, hoặc xử lý thủ công các thay đổi về mật độ màn hình và thay đổi về hướng, điều này giúp tránh việc khởi động lại ứng dụng hoặc mất trạng thái
  • Đối với các ứng dụng sử dụng cảm biến chuyển động, hãy điều chỉnh hệ toạ độ cho phù hợp với hướng hiện tại của màn hình và tránh các giả định dựa trên rotation_0 = portrait, đảm bảo các hoạt động tương tác chính xác của người dùng

Xây dựng ứng dụng thích ứng

Nếu ứng dụng của bạn đã thích ứng và tuân thủ cấp độ tối ưu hoá (Cấp 2) được nêu trong nguyên tắc về chất lượng ứng dụng cho màn hình lớn, thì ứng dụng đó sẽ hoạt động tốt trên các thiết bị có thể gập lại. Nếu không, trước khi kiểm tra kỹ các thông tin cụ thể về thiết bị có thể gập lại 3 lần và thiết bị có thể gập lại ở chế độ ngang, hãy xem lại các khái niệm cơ bản về hoạt động phát triển thích ứng trên Android.

Bố cục thích ứng (Adaptive Layouts)

Giao diện người dùng của bạn không chỉ phải xử lý nhiều kích thước màn hình mà còn phải xử lý các thay đổi về tỷ lệ khung hình theo thời gian thực, chẳng hạn như mở và chuyển sang chế độ nhiều cửa sổ hoặc chế độ cửa sổ trên máy tính. Hãy xem bài viết Giới thiệu về bố cục thích ứng để biết thêm hướng dẫn về cách:

  • Thiết kế và triển khai bố cục thích ứng
  • Điều chỉnh chế độ điều hướng chính của ứng dụng dựa trên kích thước cửa sổ
  • Sử dụng các lớp kích thước cửa sổ để điều chỉnh giao diện người dùng của ứng dụng
  • Đơn giản hoá việc triển khai bố cục chuẩn, chẳng hạn như danh sách-chi tiết, bằng cách sử dụng Jetpack API
Ứng dụng ở chế độ hòm thư trên một thiết bị có thể gập lại đang mở và ứng dụng tương tự ở chế độ toàn màn hình với bố cục thích ứng trên một thiết bị có thể gập lại đang mở khác.
Hình 1. Sự khác biệt giữa bố cục không thích ứng (hộp thư) và bố cục thích ứng.

Các lớp kích thước cửa sổ

Thiết bị có thể gập lại, bao gồm cả thiết bị có thể gập lại ở chế độ ngang và thiết bị có 3 màn hình, có thể chuyển đổi ngay lập tức giữa các lớp kích thước cửa sổ nhỏ, trung bình và lớn. Việc tìm hiểu và triển khai các lớp này giúp đảm bảo ứng dụng của bạn hiển thị đúng các thành phần điều hướng và mật độ nội dung cho trạng thái hiện tại của thiết bị.

Hình ảnh minh hoạ một ứng dụng trên các thiết bị có kích thước thuộc lớp kích thước cửa sổ thu gọn, trung bình và mở rộng.
Hình 2. Các lớp kích thước cửa sổ.

Ví dụ sau đây sử dụng thư viện thích ứng Material 3 để xác định lượng không gian mà ứng dụng có sẵn bằng cách trước tiên gọi hàm currentWindowAdaptiveInfo(), sau đó sử dụng bố cục tương ứng cho 3 lớp kích thước cửa sổ:

val adaptiveInfo = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)
val windowSizeClass = adaptiveInfo.windowSizeClass

when {
  windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> // Large
  windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND) -> // Medium
  else -> // Compact
}

Để biết thêm thông tin, hãy xem bài viết Sử dụng các lớp kích thước cửa sổ.

Chất lượng ứng dụng cho thiết bị màn hình lớn

Việc tuân thủ Cấp 2 (Tối ưu hoá cho màn hình lớn) hoặc Cấp 1 (Có tính năng đặc trưng cho màn hình lớn) trong các nguyên tắc về Chất lượng ứng dụng cho màn hình lớn giúp đảm bảo ứng dụng của bạn mang lại trải nghiệm hấp dẫn cho người dùng trên các thiết bị có 3 màn hình, thiết bị có thể gập lại ở chế độ ngang và các thiết bị màn hình lớn khác. Các nguyên tắc này bao gồm những bước kiểm tra quan trọng ở nhiều cấp bậc để chuyển từ trạng thái sẵn sàng thích ứng sang trải nghiệm khác biệt.

Android 16 trở lên

Đối với các ứng dụng nhắm đến Android 16 (API cấp 36) trở lên, hệ thống sẽ bỏ qua các quy tắc hạn chế về hướng, khả năng đổi kích thước và tỷ lệ khung hình trên những màn hình có chiều rộng nhỏ nhất >= 600 dp. Ứng dụng lấp đầy toàn bộ cửa sổ hiển thị, bất kể tỷ lệ khung hình hoặc hướng mà người dùng ưu tiên, đồng thời chế độ tương thích tạo hòm thư không còn được dùng nữa.

Các điểm cần cân nhắc đặc biệt

Thiết bị có thể gập lại 3 lần và thiết bị có thể gập lại ở chế độ ngang có những hành vi phần cứng riêng biệt cần được xử lý cụ thể, đặc biệt là về cảm biến, bản xem trước của camera và tính liên tục của cấu hình (giữ lại trạng thái khi gập, mở hoặc đổi kích thước).

Bản xem trước của máy ảnh

Một vấn đề thường gặp trên thiết bị có thể gập lại theo hướng ngang hoặc khi tính toán tỷ lệ khung hình (trong các trường hợp như nhiều cửa sổ, cửa sổ trên máy tính hoặc màn hình được kết nối) là khi bản xem trước của camera xuất hiện bị kéo giãn, nghiêng, cắt hoặc xoay.

Giả định không khớp

Vấn đề này thường xảy ra trên các thiết bị có màn hình lớn và thiết bị có thể gập lại vì các ứng dụng có thể giả định mối quan hệ cố định giữa các tính năng của camera (chẳng hạn như tỷ lệ khung hình và hướng cảm biến) và các tính năng của thiết bị (chẳng hạn như hướng thiết bị và hướng tự nhiên).

Các kiểu dáng mới thách thức giả định này. Thiết bị có thể gập lại có thể thay đổi kích thước màn hình và tỷ lệ khung hình mà không cần thay đổi hướng thiết bị. Ví dụ: việc mở thiết bị sẽ làm thay đổi tỷ lệ khung hình, nhưng nếu người dùng không xoay thiết bị, thì hướng xoay của thiết bị vẫn giữ nguyên. Nếu giả định rằng tỷ lệ khung hình tương quan với hướng xoay của thiết bị, thì ứng dụng có thể xoay hoặc chia tỷ lệ bản xem trước của camera không chính xác. Điều tương tự có thể xảy ra nếu một ứng dụng giả định hướng cảm biến của camera khớp với hướng dọc của thiết bị. Điều này không phải lúc nào cũng đúng đối với các thiết bị có thể gập lại ở hướng ngang.

Giải pháp 1: Jetpack CameraX (Tốt nhất)

Giải pháp đơn giản và mạnh mẽ nhất là sử dụng thư viện Jetpack CameraX. Phần tử giao diện người dùng PreviewView được thiết kế để tự động xử lý tất cả các độ phức tạp của bản xem trước:

  • PreviewView điều chỉnh chính xác theo hướng cảm biến, hướng xoay thiết bị và tỷ lệ.
  • Chế độ này duy trì tỷ lệ khung hình của hình ảnh do camera chụp, thường bằng cách căn giữa và cắt (FILL_CENTER).
  • Bạn có thể đặt loại tỷ lệ thành FIT_CENTER để tạo hiệu ứng hòm thư cho bản xem trước nếu cần.

Để biết thêm thông tin, hãy xem phần Triển khai bản xem trước trong tài liệu về CameraX.

Giải pháp 2: CameraViewfinder

Nếu bạn đang sử dụng cơ sở mã Camera2 hiện có, thì thư viện CameraViewfinder (tương thích ngược với API cấp 21) là một giải pháp hiện đại khác. Thao tác này giúp đơn giản hoá việc hiển thị nguồn cấp dữ liệu camera bằng cách sử dụng TextureView hoặc SurfaceView và áp dụng tất cả các phép biến đổi cần thiết (tỷ lệ khung hình, tỷ lệ và hướng xoay) cho bạn.

Để biết thêm thông tin, hãy xem bài đăng trên blog Giới thiệu về kính ngắm của camera và hướng dẫn cho nhà phát triển về Bản xem trước của camera.

Giải pháp 3: Triển khai Camera2 theo cách thủ công

Nếu không thể sử dụng CameraX hoặc CameraViewfinder, bạn phải tính toán hướng và tỷ lệ khung hình theo cách thủ công, đồng thời đảm bảo các phép tính được cập nhật trong mỗi lần thay đổi cấu hình:

  • Lấy hướng cảm biến camera (ví dụ: 0, 90, 180, 270 độ) từ CameraCharacteristics.
  • Lấy chế độ xoay màn hình hiện tại của thiết bị (ví dụ: 0, 90, 180, 270 độ).
  • Sử dụng hai giá trị này để xác định các phép biến đổi cần thiết cho SurfaceView hoặc TextureView.
  • Đảm bảo tỷ lệ khung hình của đầu ra Surface khớp với tỷ lệ khung hình của bản xem trước của camera để tránh bị biến dạng.
  • Ứng dụng camera có thể đang chạy trên một phần màn hình, ở chế độ nhiều cửa sổ hoặc chế độ cửa sổ trên máy tính hoặc trên màn hình được kết nối. Vì lý do này, bạn không nên sử dụng kích thước màn hình để xác định kích thước của khung ngắm camera, thay vào đó, hãy sử dụng các chỉ số cửa sổ.

Để biết thêm thông tin, hãy xem hướng dẫn cho nhà phát triển về Bản xem trước của camera và video Ứng dụng Camera trên nhiều hệ số hình dạng.

Giải pháp 4: Thực hiện các thao tác cơ bản với camera bằng một ý định

Nếu không cần nhiều tính năng của camera, thì một giải pháp đơn giản và dễ hiểu là thực hiện các thao tác cơ bản với camera, chẳng hạn như chụp ảnh hoặc quay video bằng ứng dụng camera mặc định trên thiết bị. Bạn không cần tích hợp với một thư viện camera; thay vào đó, hãy sử dụng một Ý định.

Để biết thêm thông tin, hãy xem phần Ý định của máy ảnh.

Cấu hình và tính liên tục

Thiết bị có thể gập lại giúp tăng tính linh hoạt của giao diện người dùng nhưng có thể bắt đầu nhiều thay đổi về cấu hình hơn so với thiết bị không gập lại. Ứng dụng của bạn phải quản lý những thay đổi về cấu hình này và các tổ hợp của chúng, chẳng hạn như xoay thiết bị, gập/mở và đổi kích thước cửa sổ ở chế độ nhiều cửa sổ hoặc chế độ máy tính, đồng thời giữ lại hoặc khôi phục trạng thái ứng dụng. Ví dụ: các ứng dụng phải duy trì tính liên tục sau đây:

  • Trạng thái ứng dụng mà không gặp sự cố hoặc gây ra những thay đổi làm gián đoạn người dùng (ví dụ: khi chuyển đổi màn hình hoặc gửi ứng dụng xuống nền)
  • Vị trí nút cuộn của các trường có thể cuộn
  • Văn bản nhập vào các trường văn bản và trạng thái bàn phím
  • Vị trí phát nội dung nghe nhìn để quá trình phát tiếp tục từ nơi dừng trước khi cấu hình thay đổi

Các thay đổi về cấu hình thường được kích hoạt bao gồm screenSize, smallestScreenSize, screenLayout, orientation, density, fontScale, touchscreenkeyboard.

Xem phần android:configChangesXử lý các thay đổi về cấu hình. Để biết thêm thông tin về cách quản lý trạng thái ứng dụng, hãy xem bài viết Lưu trạng thái giao diện người dùng.

Thay đổi về cấu hình mật độ

Màn hình ngoài và màn hình trong của thiết bị có thể gập lại theo chiều ngang và thiết bị có 3 màn hình có thể có mật độ pixel khác nhau. Do đó, bạn cần đặc biệt chú ý đến việc quản lý thay đổi cấu hình cho density. Android thường khởi động lại hoạt động khi mật độ hiển thị thay đổi, điều này có thể gây mất dữ liệu. Để ngăn hệ thống khởi động lại hoạt động, hãy khai báo việc xử lý mật độ trong tệp kê khai và quản lý thay đổi về cấu hình theo phương thức lập trình trong ứng dụng.

Cấu hình AndroidManifest.xml

  • density: Khai báo rằng ứng dụng sẽ xử lý thay đổi về mật độ màn hình
  • Các thay đổi khác về cấu hình: Bạn cũng nên khai báo các thay đổi khác về cấu hình thường xuyên xảy ra, ví dụ: screenSize, orientation, keyboardHidden, fontScale, v.v.

Việc khai báo mật độ (và các thay đổi khác về cấu hình) sẽ ngăn hệ thống khởi động lại hoạt động và thay vào đó gọi onConfigurationChanged().

Triển khai onConfigurationChanged()

Khi có sự thay đổi về mật độ, bạn phải cập nhật các tài nguyên (chẳng hạn như tải lại bitmap hoặc tính toán lại kích thước bố cục) trong lệnh gọi lại:

  • Xác minh rằng DPI đã thay đổi thành newConfig.densityDpi
  • Đặt lại các khung hiển thị tuỳ chỉnh, các đối tượng có thể vẽ tuỳ chỉnh, v.v. về mật độ mới

Các mục tài nguyên cần xử lý

  • Tài nguyên hình ảnh: Thay thế các bitmap và tài nguyên có thể vẽ bằng các tài nguyên dành riêng cho mật độ hoặc điều chỉnh trực tiếp tỷ lệ
  • Đơn vị bố cục (chuyển đổi dp sang px): Tính toán lại kích thước, lề, khoảng đệm của khung hiển thị
  • Phông chữ và cỡ chữ: Áp dụng lại cỡ chữ văn bản theo đơn vị sp
  • Vẽ View/Canvas tuỳ chỉnh: Cập nhật các giá trị dựa trên pixel dùng để vẽ Canvas

Xác định hướng của ứng dụng

Đừng bao giờ dựa vào hướng xoay của thiết bị thực khi tạo ứng dụng thích ứng, vì hướng xoay này sẽ bị bỏ qua trên các thiết bị có màn hình lớn và một ứng dụng ở chế độ nhiều cửa sổ có thể có hướng khác với thiết bị. Thay vào đó, hãy sử dụng Configuration.orientation hoặc WindowMetrics để xác định xem ứng dụng của bạn hiện đang ở hướng ngang hay hướng dọc dựa trên kích thước cửa sổ.

Giải pháp 1: Sử dụng Configuration.orientation

Thuộc tính này xác định hướng mà ứng dụng của bạn hiện đang hiển thị.

Giải pháp 2: Sử dụng WindowMetrics#getBounds()

Bạn có thể lấy ranh giới hiển thị hiện tại của ứng dụng và kiểm tra chiều rộng cũng như chiều cao của ứng dụng để xác định hướng.

Nếu bạn cần giới hạn hướng ứng dụng trên điện thoại (hoặc màn hình ngoài của thiết bị có thể gập lại) nhưng không giới hạn trên thiết bị có màn hình lớn, hãy xem phần Hạn chế hướng ứng dụng trên điện thoại.

Tư thế và chế độ hiển thị

Cả thiết bị có thể gập lại ở hướng dọc và thiết bị có thể gập lại ở hướng ngang đều hỗ trợ các tư thế và trạng thái có thể gập lại, chẳng hạn như tư thế trên mặt bàn và HALF_OPENED. Tuy nhiên, các thiết bị có thể gập ba lần không hỗ trợ tư thế đặt trên bàn và không thể sử dụng HALF_OPENED. Thay vào đó, thiết bị có 3 màn hình mang đến màn hình lớn hơn để mang lại trải nghiệm người dùng độc đáo khi mở hoàn toàn.

Để phân biệt ứng dụng của bạn trên các thiết bị có thể gập lại hỗ trợ HALF_OPENED, hãy sử dụng các API Jetpack WindowManager như FoldingFeature.

Tìm hiểu thêm về các tư thế, trạng thái của thiết bị có thể gập lại và khả năng hỗ trợ bản xem trước của camera trong các hướng dẫn sau đây dành cho nhà phát triển:

Thiết bị có thể gập lại mang đến trải nghiệm xem độc đáo. Nhờ chế độ màn hình sau và chế độ Dual Screen, bạn có thể tạo các tính năng đặc biệt cho màn hình trên thiết bị có thể gập lại (chẳng hạn như chế độ xem trước ảnh chân dung tự chụp bằng camera sau và các màn hình khác nhau nhưng hiển thị đồng thời trên màn hình trong và ngoài). Để biết thêm thông tin, hãy xem:

Khoá hướng theo hướng tự nhiên của cảm biến

Đối với những trường hợp sử dụng rất cụ thể (đặc biệt là những ứng dụng cần chiếm toàn bộ màn hình không liên quan đến trạng thái gập của thiết bị), cờ nosensor cho phép bạn khoá ứng dụng theo hướng tự nhiên của thiết bị. Ví dụ: trên Pixel Fold, hướng tự nhiên của thiết bị khi gập là hướng dọc, trong khi hướng tự nhiên khi mở là hướng ngang. Việc thêm cờ nosensor sẽ buộc ứng dụng bị khoá ở chế độ dọc khi chạy trên màn hình ngoài và bị khoá ở chế độ ngang khi chạy trên màn hình trong.

<activity
  android:name=".MainActivity"
  android:screenOrientation="nosensor">

Trò chơi và việc ánh xạ lại cảm biến XR

Đối với các trò chơi và ứng dụng XR, dữ liệu cảm biến thô (chẳng hạn như con quay hồi chuyển hoặc gia tốc kế) được cung cấp trong hệ toạ độ cố định của thiết bị. Nếu người dùng xoay thiết bị để chơi trò chơi ở chế độ ngang, các trục cảm biến sẽ không xoay theo màn hình, dẫn đến các chế độ điều khiển trò chơi không chính xác.

Để khắc phục vấn đề này, hãy kiểm tra Display.getRotation() hiện tại và ánh xạ lại các trục cho phù hợp:

  • Xoay 0: x=x, y=y
  • Xoay 90: x=-y, y=x
  • Xoay 180: x=-x, y=-y
  • Xoay 270: x=y, y=-x

Đối với các vectơ xoay (được dùng trong la bàn hoặc ứng dụng XR), hãy dùng SensorManager.remapCoordinateSystem() để ánh xạ hướng ống kính máy ảnh hoặc đầu màn hình sang các trục mới dựa trên hướng xoay hiện tại.

Khả năng tương thích của ứng dụng

Các ứng dụng phải tuân thủ nguyên tắc về chất lượng của ứng dụng để đảm bảo khả năng tương thích trên tất cả các hệ số hình dạng và màn hình được kết nối. Nếu một ứng dụng không tuân thủ các nguyên tắc này, nhà sản xuất thiết bị có thể triển khai các phương pháp xử lý khả năng tương thích, mặc dù điều này có thể làm giảm trải nghiệm người dùng.

Để biết thêm thông tin, hãy xem danh sách toàn diện về giải pháp tương thích có trong nền tảng, cụ thể là những giải pháp liên quan đến bản xem trước của camera, các chế độ ghi đècác thay đổi về API Android 16 có thể làm thay đổi hành vi của ứng dụng.

Để tìm hiểu thêm về cách tạo ứng dụng thích ứng, hãy xem phần Chất lượng ứng dụng trên màn hình lớn.