Android cung cấp mọi nguyên liệu để tạo nên một ứng dụng 5 sao cho thiết bị màn hình lớn. Các công thức trong cuốn sổ tay này chọn lọc và kết hợp các nguyên liệu được lựa chọn kỹ lưỡng để giải quyết những vấn đề cụ thể khi phát triển ứng dụng. Mỗi công thức đều có các phương pháp hay nhất, đoạn mã ví dụ chất lượng cao và hướng dẫn từng bước, giúp bạn trở thành một đầu bếp hàng đầu chuyên tạo ra các ứng dụng trên màn hình lớn.
Xếp hạng theo sao
Các công thức được xếp hạng sao dựa trên mức độ phù hợp với nguyên tắc về Chất lượng trên ứng dụng màn hình lớn.
Đáp ứng tiêu chí Cấp 1, có sự khác biệt trên màn hình lớn | |
Đáp ứng tiêu chí Cấp 2, tối ưu hoá cho màn hình lớn | |
Đáp ứng tiêu chí Cấp 3, sẵn sàng cho màn hình lớn | |
Cung cấp một số chức năng trên màn hình lớn, nhưng không đáp ứng được các nguyên tắc về chất lượng đối với ứng dụng màn hình lớn | |
Đáp ứng nhu cầu của một trường hợp sử dụng cụ thể, nhưng không hỗ trợ đúng cách cho màn hình lớn |
Hỗ trợ máy ảnh của Chromebook
Thu hút sự chú ý của người dùng Chromebook trên Google Play.
Nếu ứng dụng máy ảnh của bạn chỉ hoạt động với các tính năng máy ảnh cơ bản, đừng để cửa hàng ứng dụng ngăn người dùng Chromebook cài đặt ứng dụng chỉ vì bạn vô tình chỉ định các tính năng máy ảnh nâng cao trên điện thoại cao cấp.
Chromebook tích hợp sẵn máy ảnh mặt trước (hướng về phía người dùng) phù hợp với ứng dụng hội nghị truyền hình, ảnh chụp nhanh và các ứng dụng khác. Tuy nhiên, không phải Chromebook nào cũng có máy ảnh mặt sau (hướng ra ngoài), đồng thời hầu hết máy ảnh mặt trước của người dùng trên Chromebooks đều không hỗ trợ tính năng tự động lấy nét hoặc đèn flash.
Các phương pháp hay nhất
Các ứng dụng máy ảnh linh hoạt hỗ trợ tất cả các thiết bị, bất kể cấu hình máy ảnh như thiết bị có máy ảnh trước, máy ảnh sau, máy ảnh bên ngoài được kết nối bằng USB.
Để đảm bảo các cửa hàng ứng dụng cung cấp ứng dụng của bạn cho nhiều thiết bị nhất, hãy luôn khai báo tất cả các tính năng của máy ảnh mà ứng dụng dùng và cho biết rõ các tính năng đó có bắt buộc hay không.
Nguyên liệu
- Quyền
CAMERA
: Cấp cho ứng dụng của bạn quyền truy cập vào máy ảnh của thiết bị - Phần tử tệp kê khai
<uses-feature>
: Thông báo cho cửa hàng ứng dụng về những tính năng mà ứng dụng sử dụng - Thuộc tính
required
: Cho các cửa hàng ứng dụng biết liệu ứng dụng có thể hoạt động mà không cần tính năng đã chỉ định hay không
Các bước
Tóm tắt
Khai báo quyền sử dụng CAMERA
. Khai báo các tính năng của máy ảnh hỗ trợ máy ảnh cơ bản. Chỉ định rõ mỗi tính năng có bắt buộc hay không.
1. Khai báo quyền sử dụng CAMERA
Thêm quyền sau đây vào tệp kê khai ứng dụng:
<uses-permission android:name="android.permission.CAMERA" />
2. Khai báo các tính năng cơ bản của máy ảnh
Thêm các tính năng sau vào tệp kê khai ứng dụng:
<uses-feature android:name="android.hardware.camera.any" android:required="false" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.camera.flash" android:required="false" />
3. Chỉ định rõ mỗi tính năng có bắt buộc hay không
Đặt android:required="false"
cho tính năng android.hardware.camera.any
để cho phép các thiết bị có máy ảnh tích hợp, máy ảnh bên ngoài, hoặc thậm chí thiết bị không có máy ảnh nào cũng đều có thể truy cập vào ứng dụng của bạn.
Đối với các tính năng khác, đoạn mã trên đặt android:required="false"
để đảm bảo các thiết bị như Chromebook (không có máy ảnh mặt sau, tính năng tự động lấy nét hoặc đèn flash) có thể truy cập vào ứng dụng của bạn trên các cửa hàng ứng dụng.
Kết quả
Người dùng Chromebook có thể tải và cài đặt ứng dụng của bạn từ Google Play và các cửa hàng ứng dụng khác. Các thiết bị hỗ trợ máy ảnh đầy đủ tính năng như điện thoại sẽ không bị hạn chế về chức năng của máy ảnh.
Bằng cách đặt rõ các tính năng máy ảnh mà ứng dụng của bạn hỗ trợ và chỉ định các tính năng mà ứng dụng yêu cầu, bạn đã cung cấp ứng dụng cho nhiều thiết bị nhất có thể.
Tài nguyên khác
Để biết thêm thông tin, hãy xem nội dung Các tính năng phần cứng của máy ảnh trong tài liệu <uses-feature>
.
Hướng ứng dụng bị hạn chế trên điện thoại nhưng không bị hạn chế trên các thiết bị màn hình lớn
Ứng dụng của bạn hoạt động tốt trên điện thoại ở hướng dọc, vậy nên bạn hạn chế ứng dụng chỉ ở chế độ dọc. Tuy nhiên, bạn có thể làm được nhiều việc hơn trên màn hình lớn theo hướng ngang.
Làm thế nào để bạn có thể sử dụng theo cả hai cách – nghĩa là hạn chế ứng dụng ở hướng dọc trên màn hình nhỏ, nhưng vẫn bật được hướng ngang trên màn hình lớn?
Các phương pháp hay nhất
Một ứng dụng tối ưu luôn tôn trọng các lựa chọn ưu tiên của người dùng, chẳng hạn như hướng thiết bị.
Theo nguyên tắc về chất lượng ứng dụng màn hình lớn, các ứng dụng sẽ hỗ trợ tất cả các cấu hình thiết bị, bao gồm cả hướng dọc và ngang, chế độ nhiều cửa sổ, cũng như các trạng thái gập và mở của thiết bị có thể gập lại. Ứng dụng phải tối ưu hoá bố cục và giao diện người dùng cho các cấu hình khác nhau, đồng thời phải lưu và khôi phục trạng thái trong quá trình thay đổi cấu hình.
Công thức này chỉ là một giải pháp tạm thời — một khoảng nhỏ hỗ trợ màn hình lớn. Sử dụng công thức này cho đến khi bạn có thể cải thiện ứng dụng của mình để cung cấp hỗ trợ đầy đủ cho tất cả các cấu hình thiết bị.
Nguyên liệu
screenOrientation
: Tuỳ chọn cài đặt tệp kê khai ứng dụng cho phép bạn chỉ định cách ứng dụng của bạn phản hồi các thay đổi về hướng trên thiết bị- Jetpack WindowManager: Một bộ thư viện cho phép bạn xác định kích thước và tỷ lệ khung hình của cửa sổ ứng dụng; tương thích ngược với API cấp 14
Activity#setRequestedOrientation()
: Phương thức mà bạn có thể thay đổi hướng ứng dụng trong thời gian chạy
Các bước
Tóm tắt
Cho phép ứng dụng xử lý các thay đổi về hướng theo mặc định trong tệp kê khai ứng dụng. Trong thời gian chạy, hãy xác định kích thước cửa sổ ứng dụng. Nếu cửa sổ ứng dụng nhỏ, hãy hạn chế hướng ứng dụng bằng cách ghi đè chế độ cài đặt hướng của tệp kê khai.
1. Chỉ định chế độ cài đặt hướng trong tệp kê khai ứng dụng
Bạn có thể tránh khai báo phần tử screenOrientation
của tệp kê khai ứng dụng (trong trường hợp này, hướng được đặt mặc định thành unspecified
) hoặc đặt hướng màn hình thành fullUser
. Nếu người dùng chưa khoá chế độ xoay dựa trên cảm biến, ứng dụng của bạn sẽ hỗ trợ tất cả các hướng của thiết bị.
<activity
android:name=".MyActivity"
android:screenOrientation="fullUser">
Sự khác biệt giữa việc sử dụng unspecified
và fullUser
là khó thấy nhưng quan trọng. Nếu bạn không khai báo một giá trị screenOrientation
, thì hệ thống sẽ chọn hướng và chính sách mà hệ thống sử dụng để xác định hướng có thể khác nhau giữa các thiết bị. Mặt khác, việc chỉ định fullUser
sẽ phù hợp hơn với hành vi mà người dùng đã xác định cho thiết bị: nếu người dùng đã khoá chế độ xoay dựa trên cảm biến, thì ứng dụng sẽ tuân theo lựa chọn ưu tiên của người dùng; nếu không, hệ thống sẽ cho phép sử dụng bất kỳ hướng nào trong 4 hướng màn hình có thể có (dọc, ngang, dọc lộn ngược hoặc ngang lộn ngược). Hãy xem android:screenOrientation
.
2. Xác định kích thước màn hình
Với tệp kê khai được đặt để hỗ trợ tất cả các hướng được người dùng cho phép, bạn có thể chỉ định hướng ứng dụng theo phương thức lập trình dựa trên kích thước màn hình.
Thêm thư viện Jetpack WindowManager vào tệp build.gradle
hoặc build.gradle.kts
của mô-đun:
Kotlin
implementation("androidx.window:window:version
") implementation("androidx.window:window-core:version
")
Groovy
implementation 'androidx.window:window:version
' implementation 'androidx.window:window-core:version
'
Sử dụng phương thức Jetpack WindowManager WindowMetricsCalculator#computeMaximumWindowMetrics()
để lấy kích thước màn hình thiết bị dưới dạng đối tượng WindowMetrics
. Bạn có thể so sánh các chỉ số của cửa sổ với lớp kích thước cửa sổ để quyết định thời điểm giới hạn hướng.
Lớp kích thước của Windows cung cấp các điểm ngắt giữa màn hình nhỏ và màn hình lớn.
Sử dụng các điểm ngắt WindowWidthSizeClass#COMPACT
và WindowHeightSizeClass#COMPACT
để xác định kích thước màn hình:
Kotlin
/** Determines whether the device has a compact screen. **/ fun compactScreen() : Boolean { val metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this) val width = metrics.bounds.width() val height = metrics.bounds.height() val density = resources.displayMetrics.density val windowSizeClass = WindowSizeClass.compute(width/density, height/density) return windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.COMPACT || windowSizeClass.windowHeightSizeClass == WindowHeightSizeClass.COMPACT }
Java
/** Determines whether the device has a compact screen. **/ private boolean compactScreen() { WindowMetrics metrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this); int width = metrics.getBounds().width(); int height = metrics.getBounds().height(); float density = getResources().getDisplayMetrics().density; WindowSizeClass windowSizeClass = WindowSizeClass.compute(width/density, height/density); return windowSizeClass.getWindowWidthSizeClass() == WindowWidthSizeClass.COMPACT || windowSizeClass.getWindowHeightSizeClass() == WindowHeightSizeClass.COMPACT; }
- Lưu ý:
- Những ví dụ trên được triển khai dưới dạng phương thức của một hoạt động; và do đó, hoạt động được tham chiếu như
this
trong đối số củacomputeMaximumWindowMetrics()
. - Phương thức
computeMaximumWindowMetrics()
được dùng thay chocomputeCurrentWindowMetrics()
, vì bạn có thể chạy ứng dụng ở chế độ nhiều cửa sổ, nghĩa là bỏ qua chế độ cài đặt hướng màn hình. Chẳng có lý do gì phải xác định kích thước cửa sổ ứng dụng và ghi đè tuỳ chọn cài đặt hướng, trừ phi cửa sổ ứng dụng chiếm toàn bộ màn hình thiết bị.
Vui lòng xem WindowManager để biết hướng dẫn về cách khai báo các phần phụ thuộc nhằm cung cấp phương thức computeMaximumWindowMetrics()
cho ứng dụng của bạn.
3. Ghi đè chế độ cài đặt trong tệp kê khai ứng dụng
Khi đã xác định thiết bị có kích thước màn hình thu gọn, bạn có thể gọi Activity#setRequestedOrientation()
để ghi đè chế độ cài đặt screenOrientation
của tệp kê khai:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. val container: ViewGroup = binding.container // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) requestedOrientation = if (compactScreen()) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_FULL_USER } }) }
Java
@Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstanceState); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } ... // Replace with a known container that you can safely add a // view to where the view won't affect the layout and the view // won't be replaced. ViewGroup container = binding.container; // Add a utility view to the container to hook into // View.onConfigurationChanged. This is required for all // activities, even those that don't handle configuration // changes. You can't use Activity.onConfigurationChanged, // since there are situations where that won't be called when // the configuration changes. View.onConfigurationChanged is // called in those scenarios. container.addView(new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (compactScreen()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_USER); } } }); }
Bằng cách thêm logic vào phương thức onCreate()
và View.onConfigurationChanged()
, bạn có thể thu được các chỉ số cửa sổ tối đa và ghi đè chế độ cài đặt hướng bất cứ khi nào hoạt động được đổi kích thước hoặc di chuyển giữa các màn hình hiển thị, chẳng hạn như sau khi xoay thiết bị hoặc khi một thiết bị có thể gập lại được gập hoặc mở ra.
Để biết thêm thông tin về thời điểm diễn ra các thay đổi về cấu hình và thời điểm các thay đổi đó dẫn đến việc tạo lại hoạt động, hãy tham khảo bài viết Xử lý các thay đổi về cấu hình
Kết quả
Ứng dụng của bạn vẫn nằm ở hướng dọc trên màn hình nhỏ bất kể thiết bị có xoay hướng nào. Trên màn hình lớn, ứng dụng sẽ hỗ trợ cả hướng ngang lẫn hướng dọc.
Tài nguyên khác
Để được trợ giúp về cách nâng cấp ứng dụng nhằm luôn có thể hỗ trợ tất cả cấu hình thiết bị, hãy xem các mục sau:
Tạm dừng và tiếp tục phát nội dung nghe nhìn bằng Phím cách của bàn phím bên ngoài
Tính năng tối ưu hoá cho màn hình lớn có thể xử lý dữ liệu đầu vào qua bàn phím bên ngoài, chẳng hạn như nhấn Phím cách để tạm dừng hoặc tiếp tục phát các video và nội dung nghe nhìn khác. Tính năng này đặc biệt hữu ích cho máy tính bảng, thường kết nối với bàn phím bên ngoài và Chromebook (thường có bàn phím bên ngoài nhưng có thể sử dụng ở chế độ máy tính bảng).
Khi nội dung nghe nhìn là thành phần duy nhất của cửa sổ (chẳng hạn như phát video toàn màn hình), hãy phản hồi các sự kiện nhấn phím ở cấp hoạt động hoặc ở cấp màn hình (trong Jetpack Compose).
Các phương pháp hay nhất
Bất cứ khi nào ứng dụng của bạn phát một tệp nội dung nghe nhìn, người dùng sẽ có thể tạm dừng và tiếp tục phát bằng cách nhấn Phím cách trên bàn phím vật lý.
Nguyên liệu
KEYCODE_SPACE
: Hằng số mã phím cho Phím cách.
Compose
onPreviewKeyEvent
:Modifier
cho phép một thành phần chặn các sự kiện liên quan đến phím phần cứng khi thành phần đó (hoặc một trong các thành phần con) được lấy làm tâm điểm.onKeyEvent
: Tương tự nhưonPreviewKeyEvent
,Modifier
này cho phép một thành phần chặn các sự kiện liên quan đến phím phần cứng khi thành phần đó (hoặc một trong các thành phần con) được lấy tiêu điểm.
Khung hiển thị
onKeyUp()
: Được gọi khi một phím được nhả ra và không được khung hiển thị xử lý trong một hoạt động.
Các bước
Tóm tắt
Các ứng dụng dựa trên khung hiển thị và các ứng dụng dựa trên Jetpack Compose phản hồi với thao tác nhấn phím trên bàn phím theo những cách tương tự như sau: ứng dụng phải theo dõi các sự kiện nhấn phím, lọc ra các sự kiện đó và phản hồi một số thao tác nhấn phím, chẳng hạn như thao tác nhấn Phím cách.
1. Theo dõi các sự kiện bàn phím
Khung hiển thị
Trong một hoạt động trên ứng dụng của bạn, hãy ghi đè phương thức onKeyUp()
:
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { ... }
Java
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { ... }
Phương thức này được gọi mỗi khi bạn nhả một phím đã nhấn ra. Do đó, phương thức này sẽ kích hoạt chính xác một lần cho mỗi lần nhấn phím.
Compose
Với Jetpack Compose, bạn có thể tận dụng đối tượng sửa đổi onPreviewKeyEvent
hoặc onKeyEvent
trong màn hình để quản lý thao tác nhấn phím:
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
hoặc
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp) {
...
}
...
})
2. Lọc ra các thao tác nhấn Phím cách
Trong phương thức onKeyUp()
hoặc phương thức đối tượng sửa đổi onPreviewKeyEvent
và onKeyEvent
trong Compose, hãy lọc KeyEvent.KEYCODE_SPACE
để gửi đúng sự kiện đến thành phần nội dung nghe nhìn:
Khung hiển thị
Kotlin
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback() return true } return false
Java
if (keyCode == KeyEvent.KEYCODE_SPACE) { togglePlayback(); return true; } return false;
Compose
Column(modifier = Modifier.onPreviewKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
hoặc
Column(modifier = Modifier.onKeyEvent { event ->
if (event.type == KeyEventType.KeyUp && event.key == Key.Spacebar) {
...
}
...
})
Kết quả
Ứng dụng của bạn hiện có thể phản hồi khi người dùng nhấn Phím cách để tạm dừng và tiếp tục phát một video hoặc nội dung nghe nhìn khác.
Tài nguyên khác
Để tìm hiểu thêm về các sự kiện bàn phím và cách quản lý những sự kiện đó, hãy xem bài viết Xử lý hoạt động nhập bằng bàn phím.
Tính năng chống tì tay khi dùng bút cảm ứng
Bút cảm ứng có thể là một công cụ đặc biệt hữu dụng và sáng tạo trên màn hình lớn. Tuy nhiên, khi vẽ, viết hoặc tương tác với ứng dụng bằng bút cảm ứng, đôi lúc người dùng sẽ tì tay lên màn hình. Ứng dụng của bạn có thể sẽ nhận được báo cáo về sự kiện chạm trước khi hệ thống nhận ra và loại bỏ sự kiện đó dưới dạng thao tác vô tình tì tay.
Các phương pháp hay nhất
Ứng dụng của bạn phải xác định các sự kiện chạm dư thừa và bỏ qua những sự kiện đó. Android huỷ thao tác tì tay bằng cách gửi một đối tượng MotionEvent
. Hãy kiểm tra đối tượng đó để tìm ACTION_CANCEL
hoặc ACTION_POINTER_UP
và FLAG_CANCELED
nhằm xác định xem có nên từ chối cử chỉ do tì tay hay không.
Nguyên liệu
MotionEvent
: Biểu thị các sự kiện chạm và chuyển động. Chứa thông tin cần thiết để xác định xem có nên bỏ qua một sự kiện hay không.OnTouchListener#onTouch()
: Nhận các đối tượngMotionEvent
.MotionEvent#getActionMasked()
: Trả về thao tác liên quan đến một sự kiện chuyển động.ACTION_CANCEL
: Hằng sốMotionEvent
cho biết cần huỷ một cử chỉ.ACTION_POINTER_UP
: Hằng sốMotionEvent
cho biết rằng con trỏ không phải là con trỏ đầu tiên đã dịch chuyển lên (nghĩa là con trỏ đã ngừng tiếp xúc với màn hình thiết bị).FLAG_CANCELED
: Hằng sốMotionEvent
cho biết rằng việc con trỏ dịch chuyển lên tạo ra một sự kiện chạm ngoài ý muốn. Đã thêm vào các sự kiệnACTION_POINTER_UP
vàACTION_CANCEL
trên Android 13 (API cấp 33) trở lên.
Các bước
Tóm tắt
Kiểm tra các đối tượng MotionEvent
được gửi đến ứng dụng của bạn. Sử dụng các API MotionEvent
để xác định đặc điểm của sự kiện:
- Sự kiện chạm con trỏ một lần — Kiểm tra để tìm
ACTION_CANCEL
. Trên Android 13 trở lên, hãy kiểm tra để tìm cảFLAG_CANCELED
. - Sự kiện chạm con trỏ nhiều lần — Trên Android 13 trở lên, hãy kiểm tra để tìm
ACTION_POINTER_UP
vàFLAG_CANCELED
.
Phản hồi các sự kiện ACTION_CANCEL
và ACTION_POINTER_UP
/FLAG_CANCELED
.
1. Thu nhận đối tượng sự kiện chuyển động
Thêm một OnTouchListener
vào ứng dụng của bạn:
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> // Process motion event. } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { // Process motion event. });
2. Xác định thao tác và cờ cho sự kiện
Kiểm tra để tìm ACTION_CANCEL
(cho biết sự kiện chạm con trỏ một lần ở mọi cấp độ API). Trên Android 13 trở lên, hãy kiểm tra ACTION_POINTER_UP
để tìm FLAG_CANCELED.
Kotlin
val myView = findViewById<View>(R.id.myView).apply { setOnTouchListener { view, event -> when (event.actionMasked) { MotionEvent.ACTION_CANCEL -> { //Process canceled single-pointer motion event for all SDK versions. } MotionEvent.ACTION_POINTER_UP -> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.flags and MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } } true } }
Java
View myView = findViewById(R.id.myView); myView.setOnTouchListener( (view, event) -> { switch (event.getActionMasked()) { case MotionEvent.ACTION_CANCEL: // Process canceled single-pointer motion event for all SDK versions. case MotionEvent.ACTION_UP: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && (event.getFlags() & MotionEvent.FLAG_CANCELED) == MotionEvent.FLAG_CANCELED) { //Process canceled multi-pointer motion event for Android 13 and higher. } } return true; });
3. Huỷ cử chỉ
Sau khi xác định thấy thao tác tì tay, bạn có thể huỷ các hiệu ứng trên màn hình của cử chỉ.
Ứng dụng phải lưu nhật ký thao tác của người dùng để có thể huỷ thao tác nhập ngoài ý muốn, chẳng hạn như tì tay. Hãy xem ví dụ ở phần Triển khai ứng dụng vẽ cơ bản trong lớp học lập trình Tăng cường hỗ trợ bút cảm ứng trong ứng dụng Android.
Kết quả
Giờ đây, ứng dụng của bạn có thể xác định và từ chối thao tác tì tay đối với các sự kiện chạm con trỏ nhiều lần ở những cấp độ API từ Android 13 trở lên và các sự kiện chạm con trỏ một lần ở mọi cấp độ API.
Tài nguyên khác
Để biết thêm thông tin, hãy xem phần dưới đây:
- Các tính năng và API của Android 13 – Cải thiện khả năng chống tì tay
- Hướng dẫn cho nhà phát triển
- Lớp học lập trình — Tăng cường hỗ trợ bút cảm ứng trong ứng dụng Android
Quản lý trạng thái với WebView
WebView
là một thành phần thường được sử dụng. Thành phần này cung cấp hệ thống quản lý trạng thái tiên tiến. WebView
phải giữ nguyên trạng thái và vị trí cuộn khi có những thay đổi về cấu hình. WebView
có thể rời khỏi vị trí cuộn khi người dùng xoay thiết bị hoặc mở gập màn hình điện thoại, buộc người dùng phải cuộn lại từ đầu WebView
đến vị trí cuộn trước đó.
Các phương pháp hay nhất
Giảm thiểu số lần tạo lại WebView
. WebView
thích hợp trong việc quản lý trạng thái, bạn có thể tận dụng tính năng này bằng cách quản lý càng nhiều thay đổi cấu hình càng tốt. Ứng dụng của bạn phải xử lý việc thay đổi cấu hình vì việc tạo lại Activity
(cách xử lý các thay đổi về cấu hình của hệ thống) sẽ tạo lại WebView
, khiến WebView
bị mất trạng thái.
Nguyên liệu
android:configChanges
: Thuộc tính của phần tử tệp kê khai<activity>
. Liệt kê các thay đổi về cấu hình do hoạt động xử lý.View#invalidate()
: Phương thức khiến khung hiển thị được vẽ lại. Kế thừa từWebView
.
Các bước
Tóm tắt
Để lưu trạng thái WebView
, hãy tránh tạo lại Activity
nhiều nhất có thể, sau đó vô hiệu hoá WebView
để thành phần này thay đổi về kích thước trong khi vẫn giữ nguyên trạng thái.
1. Thêm thay đổi cấu hình vào tệp AndroidManifest.xml
của ứng dụng
Tránh tạo lại hoạt động bằng cách chỉ định thay đổi cấu hình do ứng dụng của bạn xử lý (thay vì để hệ thống xử lý):
<activity
android:name=".MyActivity"
android:configChanges="screenLayout|orientation|screenSize
|keyboard|keyboardHidden|smallestScreenSize" />
2. Vô hiệu hoá WebView
bất cứ khi nào ứng dụng của bạn nhận thấy có sự thay đổi về cấu hình
Kotlin
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) webView.invalidate() }
Java
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); webview.invalidate(); }
Bước này chỉ áp dụng cho hệ thống khung thiển thị, vì Jetpack Compose không cần vô hiệu hoá bất kỳ thứ gì để đổi kích thước các phần tử Composable
một cách chính xác. Tuy nhiên, Compose sẽ thường xuyên tạo lại WebView
nếu không được quản lý đúng cách. Sử dụng trình bao bọc Accompanist WebView để lưu và khôi phục trạng thái WebView
trong các ứng dụng Compose.
Kết quả
Giờ đây, thành phần WebView
của ứng dụng đã giữ nguyên trạng thái và vị trí cuộn khi cấu hình có nhiều sự thay đổi, như thay đổi kích thước, thay đổi hướng hoặc trạng thái gập/mở.
Tài nguyên khác
Để tìm hiểu thêm về thay đổi cấu hình cũng như cách quản lý chúng, hãy xem bài viết Xử lý thay đổi cấu hình.
Quản lý trạng thái với RecyclerView
RecyclerView
có thể hiển thị một lượng lớn dữ liệu bằng cách sử dụng ít tài nguyên đồ họa nhất có thể. Khi RecyclerView
cuộn qua danh sách các mục, RecyclerView
sẽ sử dụng lại các thực thể View
của mục đã cuộn qua để tạo các mục mới khi tiếp tục cuộn trên màn hình. Tuy nhiên, thay đổi cấu hình, chẳng hạn như việc xoay thiết bị, có thể đặt lại trạng thái của RecyclerView
, buộc người dùng phải cuộn lại vị trí trước đó trong danh sách các mục RecyclerView
.
Các phương pháp hay nhất
RecyclerView
phải giữ nguyên trạng thái (cụ thể là vị trí cuộn) và trạng thái của các phần tử trong danh sách tại tất cả thay đổi cấu hình.
Nguyên liệu
RecyclerView.Adapter#setStateRestorationPolicy()
: Chỉ định cáchRecyclerView.Adapter
khôi phục trạng thái sau khi thay đổi cấu hình.ViewModel
: Lưu giữ trạng thái của một hoạt động hoặc mảnh.
Các bước
Tóm tắt
Thiết lập chính sách khôi phục trạng thái của RecyclerView.Adapter
để lưu vị trí cuộn RecyclerView
. Lưu trạng thái của mục trong danh sách RecyclerView
. Thêm trạng thái của mục trong danh sách vào bộ chuyển đổi RecyclerView
rồi khôi phục trạng thái của mục trong danh sách khi được liên kết với ViewHolder
.
1. Bật chính sách khôi phục trạng thái Adapter
Bật chính sách khôi phục trạng thái trong bộ chuyển đổi RecyclerView
để giữ nguyên vị trí cuộn của RecyclerView
trên mọi thay đổi về cấu hình. Thêm thông số của chính sách vào hàm khởi tạo bộ chuyển đổi:
Kotlin
class MyAdapter() : RecyclerView.Adapter() { init { stateRestorationPolicy = StateRestorationPolicy.PREVENT_WHEN_EMPTY } ... }
Java
class MyAdapter extends RecyclerView.Adapter{ public Adapter() { setStateRestorationPolicy(StateRestorationPolicy.PREVENT_WHEN_EMPTY); } ... }
2. Lưu trạng thái của mục trong danh sách có trạng thái
Lưu trạng thái của mục trong danh sách RecyclerView
phức tạp, chẳng hạn như các mục chứa phần tử EditText
. Ví dụ: để lưu trạng thái của EditText
, hãy thêm lệnh gọi lại tương tự như trình xử lý onClick
để ghi lại các thay đổi trong văn bản. Trong lệnh gọi lại, hãy xác định dữ liệu cần lưu:
Kotlin
input.addTextChangedListener( afterTextChanged = { text -> text?.let { // Save state here. } } )
Java
input.addTextChangedListener(new TextWatcher() { ... @Override public void afterTextChanged(Editable s) { // Save state here. } });
Khai báo lệnh gọi lại trong Activity
hoặc Fragment
. Sử dụng ViewModel
để lưu trữ trạng thái.
3. Thêm trạng thái của mục trong danh sách vào Adapter
Thêm trạng thái của mục trong danh sách vào RecyclerView.Adapter
. Truyền trạng thái của mục đến hàm khởi tạo bộ chuyển đổi khi tạo máy chủ lưu trữ Activity
hoặc Fragment
:
Kotlin
val adapter = MyAdapter(items, viewModel.retrieveState())
Java
MyAdapter adapter = new MyAdapter(items, viewModel.retrieveState());
4. Khôi phục trạng thái của mục trong danh sách trên ViewHolder
của bộ chuyển đổi
Trong RecyclerView.Adapter
, khi bạn liên kết ViewHolder
với một mục, hãy khôi phục trạng thái của mục đó:
Kotlin
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { ... val item = items[position] val state = states.firstOrNull { it.item == item } if (state != null) { holder.restore(state) } }
Java
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ... Item item = items[position]; Arrays.stream(states).filter(state -> state.item == item) .findFirst() .ifPresent(state -> holder.restore(state)); }
Kết quả
Giờ đây, RecyclerView
đã có thể khôi phục lại vị trí cuộn và trạng thái của mọi mục trong danh sách RecyclerView
.
Tài nguyên khác
Quản lý bàn phím có thể tháo rời
Hỗ trợ bàn phím có thể tháo rời giúp tối đa hoá năng suất của người dùng trên các thiết bị lớn
màn hình thiết bị. Android kích hoạt thay đổi cấu hình mỗi khi bàn phím
được gắn với hoặc tách ra khỏi một thiết bị, điều này có thể gây mất trạng thái giao diện người dùng. Thông tin
ứng dụng có thể lưu và khôi phục trạng thái để hệ thống có thể xử lý
tạo lại hoạt động hoặc hạn chế việc tạo lại hoạt động đối với các thay đổi về cấu hình bàn phím.
Trong mọi trường hợp, tất cả dữ liệu liên quan đến bàn phím đều được lưu trữ trong một
Đối tượng Configuration
. keyboard
và
Các trường keyboardHidden
của đối tượng cấu hình chứa thông tin về loại
bàn phím và tính sẵn có của bàn phím.
Các phương pháp hay nhất
Các ứng dụng được tối ưu hoá cho màn hình lớn hỗ trợ mọi loại thiết bị đầu vào, từ bàn phím phần mềm và phần cứng cho đến bút cảm ứng, chuột, bàn di chuột và thiết bị ngoại vi khác thiết bị.
Hỗ trợ bàn phím bên ngoài liên quan đến các thay đổi về cấu hình mà bạn có thể quản lý theo một trong hai cách:
- Hãy để hệ thống tạo lại hoạt động hiện đang chạy và bạn đảm nhận việc quản lý trạng thái của ứng dụng.
- Tự quản lý thay đổi cấu hình (hoạt động sẽ không được tạo lại):
- Khai báo mọi giá trị cấu hình liên quan đến bàn phím
- Tạo trình xử lý thay đổi cấu hình
Các ứng dụng cải thiện hiệu suất, thường yêu cầu kiểm soát tốt giao diện người dùng để nhập văn bản và đầu vào khác, có thể hưởng lợi từ phương pháp tự xử lý các thay đổi về cấu hình.
Trong các trường hợp đặc biệt, bạn có thể muốn thay đổi bố cục ứng dụng khi phần cứng bàn phím được gắn hoặc tháo rời, ví dụ: để tạo thêm không gian cho các công cụ hoặc cửa sổ chỉnh sửa.
Vì cách đáng tin cậy duy nhất để theo dõi các thay đổi về cấu hình là ghi đè
phương thức onConfigurationChanged()
của một khung hiển thị, bạn có thể thêm một Chế độ xem mới
đối với hoạt động ứng dụng và phản hồi trong onConfigurationChanged()
của thành phần hiển thị
cho các thay đổi về cấu hình do bàn phím đang được đính kèm hoặc
đã tách.
Nguyên liệu
android:configChanges
: Thuộc tính của phần tử<activity>
trong tệp kê khai ứng dụng. Thông báo cho hệ thống về các thay đổi về cấu hình mà ứng dụng quản lý.View#onConfigurationChanged()
: Phương thức phản ứng với việc nhân bản mới cấu hình ứng dụng.
Các bước
Tóm tắt
Khai báo thuộc tính configChanges
và thêm các giá trị liên quan đến bàn phím. Thêm một
View
vào hệ phân cấp khung hiển thị của hoạt động và theo dõi các thay đổi về cấu hình.
1. Khai báo thuộc tính configChanges
Cập nhật phần tử <activity>
trong tệp kê khai ứng dụng bằng cách thêm các giá trị keyboard|keyboardHidden
vào danh sách các thay đổi về cấu hình đã được quản lý:
<activity
…
android:configChanges="...|keyboard|keyboardHidden">
2. Thêm một khung hiển thị trống vào hệ phân cấp khung hiển thị
Khai báo một khung hiển thị mới và thêm mã xử lý vào phương thức onConfigurationChanged()
của khung hiển thị đó:
Kotlin
val v = object : View(this) { override fun onConfigurationChanged(newConfig: Configuration?) { super.onConfigurationChanged(newConfig) // Handler code here. } }
Java
View v = new View(this) { @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Handler code here. } };
Kết quả
Lúc này, ứng dụng sẽ phản hồi khi bàn phím bên ngoài đang được kết nối hoặc tháo rời mà không tạo lại hoạt động đang chạy.
Tài nguyên khác
Để tìm hiểu cách lưu trạng thái giao diện người dùng của ứng dụng trong quá trình thay đổi cấu hình, chẳng hạn như thành phần đính kèm hoặc tách rời bàn phím, hãy xem bài viết Lưu trạng thái giao diện người dùng.
Đề xuất cho bạn
- Lưu ý: văn bản có đường liên kết sẽ hiện khi JavaScript tắt
- Xử lý thay đổi về cấu hình