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à các 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 mà nhà phát triển thường mắc phải bao gồm:
- Giả định sai về hướng thiết bị
- Bỏ qua các trường hợp sử dụng
- Không tính toán lại hoặc lưu vào bộ nhớ đệm các giá trị qua các thay đổi về cấu hình
Các vấn đề cụ thể liên quan đến thiết bị bao gồm:
- 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 ứng dụng không hoạt động được trong quá trình gập và mở
- Mật độ màn hình khác nhau và xử lý không chính xác thay đổi cấu hình density
- Vấn đề về bản xem trước của máy ảnh do cảm biến của máy ảnh phụ thuộc vào hướng tự nhiên
Để mang lại trải nghiệm người dùng chất lượng cao 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 diện tích màn hình thực mà ứng dụng chiếm, chứ không phải hướng vật lý của thiết bị
- Cập nhật bản xem trước của máy ảnh để 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 theo chiều ngang và ngăn hình ảnh bị kéo giãn hoặc cắt xén
- 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 hoặc giữ lại trạng thái bằng
ViewModelhoặ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, 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 của người dùng được chính xác
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 thích ứng, thì ứng dụng sẽ hoạt động tốt trên 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 chi tiết cụ thể về thiết bị có thể gập lại theo chiều ngang và thiết bị có thể gập lại ba lần, hãy xem lại các khái niệm cơ bản về phát triển ứng dụng thích ứng trên Android sau đây.
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 theo thời gian thực về tỷ lệ khung hình, chẳng hạn như mở ra và chuyển sang chế độ nhiều cửa sổ hoặc chế độ cửa sổ kiểu máy tính. Hãy xem bài viết Giới thiệu về bố cục thích ứng để được hướng dẫn thêm về cách:
- Thiết kế và triển khai bố cục thích ứng
- Điều chỉnh cá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 các bố cục chính tắc, chẳng hạn như danh sách-chi tiết, bằng cách sử dụng các API Jetpack
Các lớp kích thước cửa sổ
Thiết bị có thể gập lại, bao gồm thiết bị có thể gập lại theo chiều ngang và thiết bị có thể gập lại ba lần, có thể chuyển đổi ngay lập tức giữa các lớp kích thước cửa sổ nhỏ gọn, trung bình và mở rộng. Việc 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 thiết bị hiện tại.
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 các bố cục
tương ứng cho 3 lớp kích thước cửa sổ:
val adaptiveInfo = currentWindowAdaptiveInfo()
val windowSizeClass = adaptiveInfo.windowSizeClass
when {
windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_EXPANDED_LOWER_BOUND) -> // Expanded
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 thích ứng
Việc tuân thủ Cấp 2 (Ứng dụng thích ứng được tối ưu hoá) hoặc Cấp 1 (Ứng dụng thích ứng khác biệt) trong Nguyên tắc về chất lượng ứng dụng thích ứng giúp đảm bảo ứng dụng của bạn mang lại trải nghiệm người dùng hấp dẫn trên thiết bị có thể gập lại ba lần, thiết bị có thể gập lại theo chiều ngang, và các thiết bị màn hình lớn khác. Nguyên tắc này bao gồm các bước kiểm tra quan trọng trên nhiều cấp độ để 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 (cấp độ API 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 màn hình có chiều rộng nhỏ nhất >= 600 dp. Các ứng dụng sẽ lấp đầy toàn bộ cửa sổ hiển thị, bất kể tỷ lệ khung hình hay hướng ưu tiên của người dùng và chế độ tương thích dạng hòm thư không còn được sử 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 ba lần và thiết bị có thể gập lại theo chiều ngang có các 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 máy ảnh 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 chiều ngang hoặc 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ổ kiểu máy tính hoặc màn hình được kết nối) là khi bản xem trước của Máy ảnh xuất hiện bị kéo giãn, lệch sang một bên, bị cắt xén hoặc xoay.
Giả định không khớp
Vấn đề này thường xảy ra trên thiết bị 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 máy ảnh (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ị (như hướng thiết bị và hướng tự nhiên).
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 hiển thị 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ẽ thay đổi tỷ lệ khung hình, nhưng nếu người dùng không xoay thiết bị, thì hướng của thiết bị vẫn giữ nguyên. Nếu một ứng dụng giả định rằng tỷ lệ khung hình tương quan với hướng thiết bị, thì ứng dụng đó có thể xoay hoặc điều chỉnh tỷ lệ bản xem trước của máy ảnh 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 máy ảnh 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 thiết bị có thể gập lại theo chiều 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. Thành phần trên 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 cho hướng cảm biến, hướng thiết bị và tỷ lệ.- Ứng dụng này duy trì tỷ lệ khung hình của hình ảnh máy ảnh, thường là bằng cách căn giữa và cắt xén (FILL_CENTER).
- Bạn có thể đặt loại tỷ lệ thành
FIT_CENTERđể tạo hòm thư cho bản xem trước nếu cần.
Để biết thêm thông tin, hãy xem bài viết Triển khai bản xem trước trong tài liệu 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 cấp độ API 21) là một giải pháp hiện đại khác. Thư viện này giúp đơn giản hoá việc hiển thị nguồn cấp dữ liệu từ 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) 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 máy ảnh và bản xem trước của máy ảnh hướng dẫn cho nhà phát triển.
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 tính toán được cập nhật trên mỗi thay đổi về cấu hình:
- Lấy hướng cảm biến của máy ảnh (ví dụ: 0, 90, 180, 270 độ) từ
CameraCharacteristics. - Lấy hướng màn hình hiện tại của thiết bị (ví dụ: 0, 90, 180, 270 độ).
- Sử dụng 2 giá trị này để xác định các phép biến đổi cần thiết cho
SurfaceViewhoặcTextureView. - Đảm bảo tỷ lệ khung hình của
Surfaceđầu ra khớp với tỷ lệ khung hình của bản xem trước của máy ảnh để tránh bị biến dạng. - Ứng dụng máy ảnh có thể đang chạy trong một phần của màn hình, ở chế độ nhiều cửa sổ hoặc chế độ cửa sổ kiểu 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 kính ngắm của máy ảnh, hãy sử dụng 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 máy ảnh và video Ứng dụng máy ảnh của bạn trên nhiều kiểu dáng.
Giải pháp 4: Thực hiện các thao tác cơ bản với máy ảnh bằng ý định
Nếu bạn không cần nhiều tính năng của máy ảnh, thì một giải pháp đơn giản là thực hiện các thao tác cơ bản với máy ảnh, chẳng hạn như chụp ảnh hoặc quay video bằng ứng dụng máy ảnh mặc định trên thiết bị. Bạn không cần tích hợp với thư viện Camera; thay vào đó, hãy sử dụng ý định.
Để biết thêm thông tin, hãy xem bài viết Ý đị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 cườ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ý các thay đổi về cấu hình này và sự kế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:
- Trạng thái ứng dụng mà không gặp sự cố hoặc gây ra các thay đổi gây gián đoạn cho 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 đa phương tiệ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, touchscreen và keyboard.
Xem android:configChanges và Xử 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 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 ba lần và thiết bị có thể gập lại theo chiều ngang 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 độ màn hình 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 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 đó sẽ gọi onConfigurationChanged().
Triển khai onConfigurationChanged()
Khi có thay đổi về mật độ, bạn phải cập nhật tài nguyê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 chế độ xem tuỳ chỉnh, đối tượng có thể vẽ tuỳ chỉnh, v.v. thành 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ế bitmap và đối tượng 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ề, phần đệm
- Cỡ chữ và văn bản: Áp dụng lại cỡ chữ đơn vị sp
- Vẽ
View/Canvastuỳ chỉnh: Cập nhật các giá trị dựa trên pixel dùng để vẽCanvas
Xác định hướng ứng dụng
Không bao giờ dựa vào hướng xoay của thiết bị thực khi xây dựng ứng dụng thích ứng, vì hướng này sẽ bị bỏ qua trên thiết bị 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 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 và 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ị màn hình lớn, hãy xem bài viết Giới hạn 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 theo chiều dọc và thiết bị có thể gập lại theo chiều ngang đều hỗ trợ các tư thế và trạng thái có thể gập lại như tư thế đặt trên bàn và HALF_OPENED. Tuy nhiên, thiết bị có thể gập lại ba lần không hỗ trợ tư thế đặt trên mặt bàn và không thể sử dụng HALF_OPENED. Thay vào đó, thiết bị có thể gập lại ba lần cung cấp 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 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ó thể gập lại và hỗ trợ bản xem trước của máy ảnh trong các hướng dẫn sau đây cho nhà phát triển:
Thiết bị có thể gập lại giúp 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 cảm biến tự nhiên
Đối với các trường hợp sử dụng rất cụ thể (đặc biệt là các ứ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 buộc ứng dụng phải bị khoá ở hướng dọc khi chạy trên màn hình ngoài và bị khoá ở hướng ngang khi chạy trên màn hình trong.
<activity
android:name=".MainActivity"
android:screenOrientation="nosensor">
Gán lại cảm biến trò chơi và XR
Đối với trò chơi và ứng dụng XR, dữ liệu cảm biến thô (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 ở hướng ngang, thì các trục cảm biến sẽ không xoay cùng 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à gán 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 vectơ xoay (được sử dụng trong ứng dụng la bàn hoặc XR), hãy sử dụng SensorManager.remapCoordinateSystem() để ánh xạ hướng ống kính máy ảnh hoặc đầu màn hình cho 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 ứng dụng để đảm bảo khả năng tương thích trên tất cả các kiểu 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 nguyên tắc, nhà sản xuất thiết bị có thể triển khai các biện 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 đầy đủ các giải pháp thay thế về khả năng tương thích được cung cấp trong nền tảng, đặc biệt là những giải pháp liên quan đến bản xem trước của máy ảnh, ghi đè và các thay đổi về API Android 16 có thể thay đổi hành vi của ứng dụng.
Để tìm hiểu thêm về cách xây dựng ứng dụng thích ứng, hãy xem Nguyên tắc về chất lượng ứng dụng thích ứng guidelines.