Hỗ trợ nhiều mật độ pixel

Các thiết bị Android không chỉ có các kích thước màn hình khác nhau—điện thoại di động, máy tính bảng, TV, v.v. nhưng cũng có màn hình với các kích thước pixel khác nhau. Một thiết bị có thể có 160 pixel mỗi inch, trong khi một thiết bị khác có thể đáp ứng 480 pixel trong cùng một không gian. Nếu bạn không xem xét những biến thể này trong mật độ pixel, hệ thống có thể điều chỉnh tỷ lệ hình ảnh của bạn, dẫn đến hình ảnh bị mờ hoặc hình ảnh có thể xuất hiện ở kích thước không chính xác.

Trang này cho bạn biết cách bạn có thể thiết kế ứng dụng của mình để hỗ trợ các mật độ pixel khác nhau bằng cách sử dụng các đơn vị đo lường không phụ thuộc vào độ phân giải và cung cấp tài nguyên bitmap thay thế cho mỗi mật độ pixel.

Hãy xem video sau để biết tổng quan về những kỹ thuật này.

Để biết thêm thông tin về cách thiết kế thành phần biểu tượng, hãy xem Nguyên tắc về biểu tượng trong Material Design.

Dùng pixel không phụ thuộc vào mật độ

Tránh sử dụng pixel để xác định khoảng cách hoặc kích thước. Xác định phương diện bằng pixel là một vấn đề bởi vì các màn hình khác nhau có mật độ pixel khác nhau, cùng một số pixel tương ứng với các kích thước vật lý khác nhau trên thiết bị khác nhau.

Hình ảnh cho thấy 2 màn hình thiết bị mẫu có mật độ khác nhau
Hình 1: Hai màn hình có cùng kích thước có thể có số lượng pixel khác nhau.

Để duy trì kích thước hiển thị của giao diện người dùng trên các màn hình có mật độ khác nhau, hãy thiết kế giao diện người dùng bằng cách sử dụng pixel không phụ thuộc vào mật độ (dp) làm đơn vị đo lường. Một dp là đơn vị pixel ảo gần bằng 1 pixel trên màn hình có mật độ điểm ảnh trung bình (160 dpi hay mật độ "đường cơ sở"). Android chuyển giá trị này sang số pixel thực thích hợp cho mật độ khác nhau.

Hãy xem xét 2 thiết bị trong hình 1. Một chế độ xem Chiều rộng 100 pixel sẽ hiển thị lớn hơn nhiều trên thiết bị ở bên trái. Một lượt xem được xác định là rộng 100 dp sẽ hiển thị cùng một kích thước trên cả hai màn hình.

Khi xác định kích thước văn bản, bạn có thể sử dụng thuộc tính có thể mở rộng pixel (sp) làm đơn vị. Đơn vị sp là có cùng kích thước với dp theo mặc định nhưng lại thay đổi kích thước theo lựa chọn ưu tiên của người dùng cỡ chữ. Tuyệt đối không sử dụng sp cho kích thước bố cục.

Ví dụ: để chỉ định khoảng cách giữa hai khung hiển thị, hãy sử dụng dp:

<Button android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/clickme"
    android:layout_marginTop="20dp" />

Khi chỉ định kích thước văn bản, hãy sử dụng sp:

<TextView android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textSize="20sp" />

Chuyển đổi đơn vị dp sang đơn vị pixel

Trong một số trường hợp, bạn cần biểu thị kích thước bằng dp và sau đó chuyển đổi chúng thành pixel. Chuyển đổi đơn vị dp sang pixel màn hình như sau:

px = dp * (dpi / 160)

Lưu ý: Tuyệt đối không mã hoá cứng phương trình này để tính pixel. Thay vào đó, hãy sử dụng TypedValue.applyDimension(), để chuyển đổi nhiều loại kích thước (dp, sp, v.v.) thành pixel cho bạn.

Hãy tưởng tượng một ứng dụng nhận dạng được cử chỉ cuộn hoặc hất sau khi ngón tay của người dùng đã di chuyển ít nhất 16 pixel. Trên đường cơ sở màn hình, ngón tay của người dùng phải di chuyển 16 pixels / 160 dpi, bằng 1/10 inch (hoặc 2,5 mm), trước cử chỉ được nhận dạng.

Trên thiết bị với màn hình có mật độ điểm ảnh cao (240 dpi), thì ngón tay của người dùng phải di chuyển 16 pixels / 240 dpi, bằng 1/15 inch (hoặc 1,7 mm). Khoảng cách ngắn hơn nhiều và do đó ứng dụng có vẻ nhạy cảm hơn đối với người dùng.

Để khắc phục vấn đề này, hãy biểu thị ngưỡng cử chỉ trong mã bằng dp và sau đó chuyển đổi nó thành pixel thực tế. Ví dụ:

Kotlin

// The gesture threshold expressed in dp
private const val GESTURE_THRESHOLD_DP = 16.0f

private var gestureThreshold: Int = 0

// Convert the dps to pixels, based on density scale
gestureThreshold = TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  resources.displayMetrics).toInt()

// Use gestureThreshold as a distance in pixels...

Java

// The gesture threshold expressed in dp
private final float GESTURE_THRESHOLD_DP = 16.0f;

// Convert the dps to pixels, based on density scale
int gestureThreshold = (int) TypedValue.applyDimension(
  COMPLEX_UNIT_DIP,
  GESTURE_THRESHOLD_DP + 0.5f,
  getResources().getDisplayMetrics());

// Use gestureThreshold as a distance in pixels...

Trường DisplayMetrics.density chỉ định hệ số tỷ lệ dùng để chuyển đổi đơn vị dp thành pixel theo mật độ pixel hiện tại. Trên màn hình có độ phân giải trung bình, DisplayMetrics.density bằng 1.0 và trên màn hình mật độ cao, nó bằng 1,5. Trên màn hình có độ phân giải cực cao, nó bằng 2.0 và trên màn hình mật độ thấp, nó bằng 0, 75. Con số này là được TypedValue.applyDimension() dùng để lấy số pixel thực tế cho màn hình hiện tại.

Dùng các giá trị cấu hình được điều chỉnh theo tỷ lệ

Bạn có thể sử dụng lớp ViewConfiguration để truy cập vào các truy cập chung quãng đường, tốc độ và thời gian mà hệ thống Android sử dụng. Ví dụ: khoảng cách tính bằng pixel mà khung sử dụng làm ngưỡng cuộn có thể đạt được với getScaledTouchSlop():

Kotlin

private val GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).scaledTouchSlop

Java

private final int GESTURE_THRESHOLD_DP = ViewConfiguration.get(myContext).getScaledTouchSlop();

Các phương thức trong ViewConfiguration bắt đầu bằng tiền tố getScaled được đảm bảo sẽ trả về một giá trị tính bằng pixel hiển thị chính xác bất kể giá trị mật độ pixel.

Ưu tiên đồ hoạ vectơ

Giải pháp thay thế cho việc tạo nhiều phiên bản hình ảnh có mật độ cụ thể là chỉ tạo một đồ họa vectơ. Đồ hoạ vectơ tạo một hình ảnh bằng XML để xác định đường dẫn và màu sắc, thay vì sử dụng bitmap pixel. Như vậy, vectơ có thể chuyển tỷ lệ theo bất kỳ kích thước nào mà không cần điều chỉnh tỷ lệ cấu phần phần mềm, mặc dù các cấu phần phần mềm này thường phù hợp nhất để minh hoạ, chẳng hạn như biểu tượng, thay vì ảnh.

Đồ hoạ vectơ thường được cung cấp dưới dạng tệp SVG (Đồ hoạ vectơ có thể mở rộng), nhưng Android không hỗ trợ định dạng này nên bạn phải chuyển đổi tệp SVG thành Vectơ của Android đối tượng có thể vẽ.

Bạn có thể chuyển đổi SVG thành vectơ vẽ được bằng cách sử dụng Vector Asset Studio như sau:

  1. Trong cửa sổ Project (Dự án), hãy nhấp chuột phải vào thư mục res rồi chọn Mới > Thành phần vectơ.
  2. Chọn Tệp cục bộ (SVG, PSD).
  3. Xác định tệp bạn muốn nhập và điều chỉnh.

    Hình ảnh minh hoạ cách nhập SVG trong Android Studio
    Hình 2: Nhập SVG bằng Android Studio.

    Bạn có thể nhận thấy một số lỗi trong cửa sổ Asset Studio chỉ ra rằng các vectơ vẽ được không hỗ trợ một số thuộc tính của tệp. Việc này không ngăn bạn nhập tệp; các cơ sở lưu trú không được hỗ trợ sẽ bị bỏ qua.

  4. Nhấp vào Tiếp theo.

  5. Trên màn hình tiếp theo, hãy xác nhận nhóm tài nguyên mà bạn muốn đưa tệp vào dự án rồi nhấp vào Finish (Hoàn tất).

    Vì một vectơ vẽ được có thể được sử dụng trên tất cả mật độ pixel, tệp này sẽ nằm trong thư mục đối tượng có thể vẽ mặc định của bạn, như minh hoạ sau đây thứ bậc. Bạn không cần phải sử dụng thư mục cụ thể về mật độ.

    res/
      drawable/
        ic_android_launcher.xml
    

Để biết thêm thông tin về cách tạo đồ hoạ vectơ, hãy đọc vectơ vẽ được tài liệu.

Cung cấp bitmap thay thế

Để cung cấp chất lượng đồ hoạ tốt trên các thiết bị có mật độ pixel khác nhau, cung cấp nhiều phiên bản của mỗi bitmap trong ứng dụng của bạn—mỗi phiên bản cho một bitmap nhóm mật độ, ở độ phân giải tương ứng. Nếu không, Android phải mở rộng quy mô để bitmap chiếm cùng một không gian hiển thị trên mỗi màn hình, dẫn đến việc điều chỉnh tỷ lệ các hiệu ứng giả như làm mờ.

Hình ảnh cho thấy kích thước tương đối của các bitmap ở nhiều kích thước mật độ
Hình 3: Kích thước tương đối của bitmap trong các bộ chứa mật độ khác nhau.

Bạn có thể dùng một số bộ chứa mật độ trong ứng dụng của mình. Bảng 1 mô tả các bộ hạn định cấu hình khác nhau hiện có và các loại màn hình áp dụng.

Bảng 1. Bộ hạn định cấu hình cho các bộ hạn định khác nhau mật độ pixel.

Bộ định tính mật độ Mô tả
ldpi Tài nguyên dành cho màn hình có độ phân giải thấp (ldpi) (~120 dpi).
mdpi Tài nguyên cho màn hình có độ phân giải trung bình (mdpi) (~160 dpi). Đây là đường cơ sở mật độ.
hdpi Tài nguyên cho màn hình có độ phân giải cao (hdpi) (~240 dpi).
xhdpi Tài nguyên cho màn hình có độ phân giải cực cao (xhdpi) (~320 dpi).
xxhdpi Tài nguyên cho màn hình có độ phân giải cực cực cao (xxhdpi) (~480 dpi).
xxxhdpi Tài nguyên cho mật độ cực cực cực cao (xxxhdpi) sử dụng (~640 dpi).
nodpi Tài nguyên cho mọi mật độ. Đây là những tài nguyên không phụ thuộc vào mật độ. Hệ thống không điều chỉnh tỷ lệ các tài nguyên được gắn thẻ bằng bộ hạn định này, bất kể mật độ của màn hình hiện tại là như thế nào.
tvdpi Tài nguyên cho màn hình ở đâu đó giữa mdpi và hdpi; xấp xỉ ~213 dpi. Đây không được coi là nhóm mật độ "chính". Chủ yếu là nhằm mục đích cho TV và hầu hết các ứng dụng đều không cần đến màn hình này, cung cấp mdpi và hdpi là đủ cho hầu hết các ứng dụng và hệ thống chia tỷ lệ chúng thành phù hợp. Nếu bạn thấy cần cung cấp tài nguyên tvdpi, kích thước chúng ở hệ số 1,33 * mdpi. Ví dụ: hình ảnh 100x100 pixel cho Màn hình mdpi là 133x133 pixel cho tvdpi.

Để tạo các đối tượng bitmap có thể vẽ thay thế cho các mật độ khác nhau, hãy làm theo 3:4:6:8:12:16 tỷ lệ giữa sáu mật độ chính. Ví dụ: nếu bạn có một bitmap có thể vẽ có kích thước 48x48 pixel cho màn hình có mật độ trung bình, kích thước là:

  • 36x36 (0.75x) cho mật độ thấp (ldpi)
  • 48x48 (1x đường cơ sở) cho mật độ trung bình (mdpi)
  • 72x72 (1,5x) cho mật độ cao (hdpi)
  • 96x96 (2x) cho mật độ cực cao (xhdpi)
  • 144x144 (3.0x) cho mật độ cực cực cao (xxhdpi)
  • 192x192 (4.0x) cho mật độ cực siêu siêu cao (xxxhdpi)

Đặt các tệp hình ảnh đã tạo vào thư mục con thích hợp dưới res/:

res/
  drawable-xxxhdpi/
    awesome_image.png
  drawable-xxhdpi/
    awesome_image.png
  drawable-xhdpi/
    awesome_image.png
  drawable-hdpi/
    awesome_image.png
  drawable-mdpi/
    awesome_image.png

Sau đó, bất cứ khi nào bạn tham chiếu đến @drawable/awesomeimage, hệ thống sẽ chọn bitmap phù hợp dựa trên dpi của màn hình. Nếu bạn không cung cấp tài nguyên cụ thể theo mật độ cho mật độ đó, thì hệ thống sẽ định vị kết quả phù hợp nhất tiếp theo và điều chỉnh tỷ lệ cho vừa với màn hình.

Mẹo: Nếu bạn có tài nguyên có thể vẽ mà bạn không muốn hệ thống mở rộng quy mô, chẳng hạn như khi bạn đang thực hiện bạn tự thực hiện một số điều chỉnh đối với hình ảnh trong thời gian chạy, hãy đặt chúng vào thư mục có bộ hạn định cấu hình nodpi. Các tài nguyên có bộ hạn định này được coi là không phụ thuộc vào mật độ và thì hệ thống sẽ không điều chỉnh theo tỷ lệ.

Để biết thêm thông tin về những bộ hạn định cấu hình khác và cách Android chọn tài nguyên thích hợp cho cấu hình màn hình hiện tại, hãy xem phần Tổng quan về tài nguyên ứng dụng.

Đặt biểu tượng ứng dụng trong thư mục mipmap

Cũng giống như các thành phần bitmap khác, bạn cần cung cấp các phiên bản có mật độ cụ thể của biểu tượng ứng dụng của bạn. Tuy nhiên, một số trình chạy ứng dụng cho thấy biểu tượng ứng dụng của bạn chiếm đến 25% lớn hơn nhóm mật độ gọi của thiết bị.

Ví dụ: nếu bộ chứa mật độ của thiết bị là xxhdpi và biểu tượng ứng dụng lớn nhất mà bạn cung cấp nằm trong drawable-xxhdpi, trình chạy ứng dụng sẽ phóng to biểu tượng này, làm cho ảnh trông kém sắc nét hơn.

Để tránh điều này, hãy đặt tất cả biểu tượng ứng dụng trong thư mục mipmap thay vì trong thư mục drawable. Bỏ thích drawable thư mục, tất cả các thư mục mipmap đều được giữ lại trong APK, ngay cả nếu bạn tạo APK có mật độ cụ thể. Điều này cho phép các ứng dụng trình chạy chọn ứng dụng tốt nhất biểu tượng độ phân giải để hiển thị trên màn hình chính.

res/
  mipmap-xxxhdpi/
    launcher_icon.png
  mipmap-xxhdpi/
    launcher_icon.png
  mipmap-xhdpi/
    launcher_icon.png
  mipmap-hdpi/
    launcher_icon.png
  mipmap-mdpi/
    launcher_icon.png

Trong ví dụ trước về thiết bị xxhdpi, bạn có thể cung cấp biểu tượng trình chạy có độ phân giải cao hơn trong thư mục mipmap-xxxhdpi.

Để biết nguyên tắc thiết kế biểu tượng, hãy xem phần Biểu tượng hệ thống.

Để được trợ giúp tạo biểu tượng ứng dụng, hãy xem phần Tạo biểu tượng ứng dụng bằng Image Asset Studio.

Lời khuyên cho các vấn đề không phổ biến về mật độ

Phần này mô tả cách Android điều chỉnh tỷ lệ cho bitmap về các mật độ pixel khác nhau và cách bạn có thể kiểm soát thêm bitmap được vẽ trên các mật độ khác nhau. Trừ phi ứng dụng của bạn thao túng đồ hoạ hoặc gặp vấn đề khi chạy trên nhiều mật độ pixel, bạn có thể bỏ qua phần này.

Để hiểu rõ hơn về cách bạn có thể hỗ trợ nhiều mật độ khi thao tác với đồ hoạ tại thời gian chạy, bạn cần biết cách hệ thống giúp đảm bảo tỷ lệ thích hợp cho bitmap. Bạn có thể thực hiện việc này theo các cách sau:

  1. Điều chỉnh tỷ lệ tài nguyên trước khi mở rộng, chẳng hạn như đối tượng có thể vẽ bitmap

    Dựa trên mật độ của màn hình hiện tại, hệ thống sẽ sử dụng bất kỳ mật độ cụ thể nào khỏi ứng dụng của bạn. Nếu tài nguyên không có sẵn trong mật độ chính xác, hệ thống sẽ tải các tài nguyên mặc định và tăng hoặc giảm tỷ lệ khi cần. Hệ thống giả định rằng các tài nguyên mặc định (những tài nguyên từ thư mục không có bộ hạn định cấu hình) được thiết kế cho đường cơ sở mật độ pixel (mdpi) và đổi kích thước các bitmap đó thành kích thước thích hợp cho mật độ pixel hiện tại.

    Nếu bạn yêu cầu kích thước của tài nguyên được điều chỉnh theo tỷ lệ, hệ thống sẽ trả về các giá trị biểu thị các tham số sau khi điều chỉnh theo tỷ lệ. Ví dụ: một bitmap được thiết kế ở kích thước 50x50 pixel đối với màn hình mdpi được chia tỷ lệ thành 75x75 pixel trên màn hình hdpi (nếu không có tài nguyên thay thế cho hdpi) và hệ thống sẽ báo cáo kích thước như vậy.

    Có một số tình huống mà có thể bạn không muốn Android điều chỉnh trước khi điều chỉnh quy mô một tài nguyên. Cách dễ nhất để tránh việc điều chỉnh tỷ lệ trước là đặt tài nguyên vào một thư mục tài nguyên bằng bộ hạn định cấu hình nodpi. Ví dụ:

    res/drawable-nodpi/icon.png

    Khi hệ thống sử dụng bitmap icon.png từ thư mục này, hệ thống sẽ không điều chỉnh tỷ lệ dựa trên mật độ hiện tại của thiết bị.

  2. Tự động điều chỉnh tỷ lệ kích thước và toạ độ pixel

    Bạn có thể tắt phương diện và hình ảnh điều chỉnh tỷ lệ trước bằng cách đặt android:anyDensity vào "false" trong tệp kê khai hoặc theo phương thức lập trình cho Bitmap bằng cách đặt inScaled thành "false". Trong trong trường hợp này, hệ thống sẽ tự động điều chỉnh tỷ lệ mọi pixel và toạ độ pixel tuyệt đối các giá trị thứ nguyên tại thời điểm vẽ. Việc này nhằm đảm bảo rằng pixel được xác định các thành phần màn hình vẫn được hiển thị ở cùng kích thước thực chúng có thể hiển thị ở mật độ pixel cơ sở (mdpi). Hệ thống sẽ xử lý điều này chia tỷ lệ rõ ràng cho ứng dụng và báo cáo pixel được điều chỉnh theo tỷ lệ kích thước cho ứng dụng chứ không phải kích thước pixel thực tế.

    Ví dụ: giả sử một thiết bị có màn hình WVGA với độ phân giải cao, với kích thước 480x800 và cùng kích thước như màn hình HVGA truyền thống nhưng chạy một ứng dụng đã vô hiệu hoá trước khi điều chỉnh tỷ lệ. Trong trường hợp này, hệ thống "nói dối" cho ứng dụng khi truy vấn về màn hình kích thước và báo cáo 320x533, bản dịch mdpi gần đúng cho mật độ pixel.

    Sau đó, khi ứng dụng thực hiện các thao tác vẽ, chẳng hạn như làm mất hiệu lực hình chữ nhật từ (10,10) đến (100, 100), hệ thống biến đổi toạ độ bằng cách chia chúng một số lượng thích hợp và làm mất hiệu lực vùng (15,15) thành (150, 150). Sự chênh lệch này có thể gây ra hành vi không mong muốn nếu ứng dụng của bạn trực tiếp điều khiển bitmap được điều chỉnh theo tỷ lệ, nhưng đây được coi là một cách hợp lý đánh đổi nhằm đảm bảo hiệu suất tốt nhất có thể của ứng dụng. Nếu bạn gặp phải vấn đề này tình huống, hãy đọc bài viết Chuyển đổi đơn vị dp sang pixel đơn vị.

    Thông thường, bạn không tắt tính năng điều chỉnh tỷ lệ trước. Cách tốt nhất để hỗ trợ nhiều tuân theo các kỹ thuật cơ bản được mô tả trên trang này.

Nếu ứng dụng của bạn điều khiển bitmap hoặc tương tác trực tiếp với các pixel trên màn hình theo cách khác, bạn có thể cần thực hiện các bước bổ sung để hỗ trợ mật độ pixel. Ví dụ: nếu bạn phản hồi cử chỉ chạm bằng cách đếm số pixel mà một ngón tay di chuyển qua, bạn cần sử dụng giá trị pixel không phụ thuộc vào mật độ, thay vì pixel thực tế, nhưng bạn có thể chuyển đổi giữa các giá trị dp và px.

Kiểm thử trên tất cả mật độ pixel

Kiểm thử ứng dụng trên nhiều thiết bị với các pixel khác nhau để bạn có thể đảm bảo giao diện người dùng điều chỉnh tỷ lệ chính xác. Kiểm thử trên một vật thể thiết bị khi có thể; hãy sử dụng gói thuê bao Android Trình mô phỏng nếu bạn không có quyền truy cập vào thiết bị cho tất cả các mật độ pixel khác nhau.

Nếu bạn muốn thử nghiệm trên thiết bị thực nhưng không muốn mua thiết bị, bạn có thể sử dụng Phòng thử nghiệm Firebase để truy cập vào nhiều thiết bị trong trung tâm dữ liệu của Google.