Mendukung kepadatan piksel yang berbeda-beda

Perangkat Android tidak hanya memiliki ukuran layar yang berbeda—handset, tablet, TV, dll.—tetapi juga memiliki layar dengan ukuran piksel yang berbeda. Satu perangkat mungkin memiliki 160 piksel per inci, sementara perangkat lainnya sesuai dengan 480 piksel dalam ruang yang sama. Jika Anda tidak mempertimbangkan variasi ini dalam kepadatan piksel, sistem mungkin menskalakan gambar, yang menghasilkan gambar buram, atau gambar mungkin muncul pada ukuran yang salah.

Halaman ini menunjukkan cara mendesain aplikasi untuk mendukung berbagai kepadatan piksel dengan menggunakan satuan pengukuran yang tidak bergantung pada resolusi dan menyediakan resource bitmap alternatif untuk setiap kepadatan piksel.

Tonton video berikut untuk ringkasan tentang teknik ini.

Untuk informasi selengkapnya tentang cara mendesain aset ikon, lihat Panduan ikon Desain Material.

Menggunakan piksel kepadatan mandiri

Hindari penggunaan {i>pixel<i} untuk menentukan jarak atau ukuran. Menentukan dimensi dengan piksel menjadi masalah karena layar yang berbeda memiliki kepadatan piksel yang berbeda pula, sehingga jumlah piksel yang sama sesuai dengan ukuran fisik yang berbeda di perangkat yang berbeda.

Gambar yang menunjukkan dua contoh perangkat yang ditampilkan dengan kepadatan yang berbeda
Gambar 1: Dua layar dengan ukuran yang sama dapat memiliki jumlah piksel yang berbeda.

Untuk mempertahankan ukuran UI yang terlihat pada layar dengan kepadatan yang berbeda, desain UI Anda menggunakan piksel kepadatan mandiri (dp) sebagai unit pengukuran Anda. Satu dp adalah satuan piksel virtual yang kurang lebih sama dengan satu piksel pada layar berkepadatan sedang (160 dpi, atau kepadatan "dasar bawaan"). Android menerjemahkan nilai ini ke jumlah piksel sungguhan yang sesuai untuk setiap kepadatan.

Pertimbangkan kedua perangkat dalam gambar 1. Tampilan yang lebarnya 100 piksel muncul jauh lebih besar pada perangkat di sebelah kiri. Tampilan yang ditetapkan selebar 100 dp akan muncul dengan ukuran yang sama di kedua layar.

Saat menentukan ukuran teks, Anda dapat menggunakan piksel skalabel (sp) sebagai satuan. Satuan sp memiliki ukuran yang sama dengan dp, secara default, tetapi ukurannya akan berubah berdasarkan ukuran teks pilihan pengguna. Jangan pernah menggunakan sp untuk ukuran tata letak.

Misalnya, untuk menentukan jarak antara dua tampilan, gunakan dp:

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

Saat menentukan ukuran teks, gunakan sp:

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

Mengonversikan satuan dp menjadi satuan piksel

Dalam beberapa kasus, Anda perlu menyatakan dimensi dalam dp, lalu mengonversinya menjadi piksel. Konversi satuan dp ke piksel layar adalah sebagai berikut:

px = dp * (dpi / 160)

Catatan: Jangan pernah melakukan hardcode pada persamaan ini untuk menghitung piksel. Sebagai gantinya, gunakan TypedValue.applyDimension(), yang mengonversi banyak jenis dimensi (dp, sp, dll.) menjadi piksel untuk Anda.

Bayangkan sebuah aplikasi saat gestur scroll atau ayunkan jari dikenali setelah jari pengguna bergerak setidaknya 16 piksel. Pada layar dasar pengukuran, jari pengguna harus memindahkan 16 pixels / 160 dpi, yang sama dengan 1/10 inci (atau 2,5 mm), sebelum gestur dikenali.

Pada perangkat yang memiliki layar berkepadatan tinggi (240 dpi), jari pengguna harus memindahkan 16 pixels / 240 dpi, yang sama dengan 1/15 inci (atau 1,7 mm). Jaraknya jauh lebih pendek, sehingga aplikasi tampak lebih sensitif bagi pengguna.

Untuk memperbaiki masalah ini, nyatakan batas gestur dalam kode dalam dp, lalu konversikan menjadi piksel sebenarnya. Contoh:

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...

Kolom DisplayMetrics.density menentukan faktor penskalaan yang digunakan untuk mengonversi satuan dp menjadi piksel sesuai dengan kepadatan piksel saat ini. Pada layar berkepadatan sedang, DisplayMetrics.density sama dengan 1,0, dan pada layar berkepadatan tinggi, sama dengan 1,5. Pada layar berkepadatan ekstra tinggi, sama dengan 2,0, dan pada layar berkepadatan rendah, sama dengan 0,75. Angka ini digunakan oleh TypedValue.applyDimension() untuk mendapatkan jumlah piksel aktual pada layar saat ini.

Menggunakan nilai konfigurasi yang diskalakan sebelumnya

Anda dapat menggunakan class ViewConfiguration untuk mengakses jarak, kecepatan, dan waktu yang umum digunakan oleh sistem Android. Misalnya, jarak dalam piksel yang digunakan oleh framework sebagai nilai minimum scroll dapat diperoleh dengan getScaledTouchSlop():

Kotlin

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

Java

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

Metode dalam ViewConfiguration yang dimulai dengan awalan getScaled dijamin akan menampilkan nilai dalam piksel yang ditampilkan dengan benar, berapa pun kepadatan piksel saat ini.

Pilih grafik vektor

Alternatif untuk membuat beberapa versi gambar khusus kepadatan adalah dengan membuat satu grafik vektor saja. Grafik vektor membuat gambar menggunakan XML untuk menentukan jalur dan warna, bukan menggunakan bitmap piksel. Dengan demikian, grafik vektor dapat menskalakan hingga berbagai ukuran tanpa artefak penskalaan, meskipun biasanya paling cocok untuk ilustrasi seperti ikon, bukan foto.

Grafik vektor sering disediakan sebagai file SVG (Scalable Vector Graphics), tetapi Android tidak mendukung format ini sehingga Anda harus mengonversi file SVG ke format vektor drawable Android.

Anda dapat mengonversi SVG ke vektor drawable menggunakan Vector Asset Studio Android Studio seperti berikut:

  1. Di jendela Project, klik kanan direktori res, lalu pilih New > Vector Asset.
  2. Pilih File lokal (SVG, PSD).
  3. Temukan file yang ingin Anda impor, kemudian lakukan penyesuaian.

    Gambar yang menunjukkan cara mengimpor SVG di Android Studio
    Gambar 2: Mengimpor SVG dengan Android Studio.

    Anda mungkin melihat beberapa error di jendela Asset Studio yang menunjukkan bahwa vektor drawable tidak mendukung beberapa properti file. Hal ini tidak mencegah Anda mengimpor file; properti yang tidak didukung akan diabaikan.

  4. Klik Next.

  5. Di layar berikutnya, konfirmasi set sumber tempat file akan dimasukkan dalam project Anda, lalu klik Finish.

    Karena satu vektor drawable dapat digunakan pada semua kepadatan piksel, file ini akan berada di direktori drawable default, seperti yang ditunjukkan dalam hierarki berikut. Anda tidak perlu menggunakan direktori spesifik kepadatan.

    res/
      drawable/
        ic_android_launcher.xml
    

Untuk informasi selengkapnya tentang cara membuat grafik vektor, baca dokumentasi vektor drawable.

Menyediakan bitmap alternatif

Untuk memberikan kualitas grafis yang baik di perangkat dengan kepadatan piksel yang berbeda, berikan beberapa versi setiap bitmap di aplikasi Anda—satu versi untuk setiap bucket kepadatan, pada resolusi yang terkait. Jika tidak, Android harus menskalakan bitmap Anda agar menempati ruang terlihat yang sama di setiap layar, sehingga mengakibatkan artefak penskalaan seperti pemburaman.

Gambar yang menunjukkan ukuran relatif untuk bitmap dengan ukuran kepadatan yang berbeda
Gambar 3: Ukuran relatif untuk bitmap di bucket kepadatan yang berbeda.

Ada beberapa bucket kepadatan yang tersedia untuk digunakan di aplikasi Anda. Tabel 1 menjelaskan berbagai pengontrol kualitas konfigurasi yang tersedia dan jenis layar yang dapat diterapkan.

Tabel 1. Penentu konfigurasi untuk kepadatan piksel yang berbeda.

Penentu kepadatan Deskripsi
ldpi Resource untuk layar berkepadatan rendah (ldpi) (~120 dpi).
mdpi Resource untuk layar berkepadatan medium (mdpi) (~160 dpi). Ini adalah kepadatan dasar pengukuran.
hdpi Resource untuk layar berkepadatan tinggi (hdpi) (~240 dpi).
xhdpi Resource untuk layar berkepadatan ekstra tinggi (xhdpi) (~320 dpi).
xxhdpi Resource untuk layar kepadatan ekstra ekstra tinggi (xxhdpi) (~480 dpi).
xxxhdpi Resource untuk penggunaan kepadatan ekstra ekstra ekstra tinggi (xxxhdpi) (~640 dpi).
nodpi Resource untuk semua kepadatan. Ukuran ini adalah resource kepadatan mandiri. Sistem tidak menskalakan resource yang diberi tag dengan penentu ini, berapa pun kepadatan layar saat ini.
tvdpi Resource untuk layar antara mdpi dan hdpi; sekitar ~213 dpi. Kepadatan piksel layar ini tidak dianggap sebagai grup kepadatan "utama". Kepadatan ini utamanya ditujukan untuk televisi, dan sebagian besar aplikasi tidak memerlukannya—menyediakan resource mdpi dan hdpi cukup untuk sebagian besar aplikasi, dan sistem akan menskalakan sebagaimana sesuai. Jika merasa perlu menyediakan resource tvdpi, ukurankan resource tersebut pada faktor 1,33 * mdpi. Misalnya, gambar 100x100 piksel untuk layar mdpi adalah 133x133 piksel untuk tvdpi.

Guna membuat drawable bitmap alternatif untuk kepadatan yang berbeda, ikuti rasio penskalaan 3:4:6:8:12:16 antara enam kepadatan utama. Misalnya, jika Anda memiliki drawable bitmap berukuran 48x48 piksel untuk layar berkepadatan sedang, ukurannya adalah:

  • 36x36 (0,75x) untuk kepadatan rendah (ldpi)
  • 48x48 (dasar pengukuran 1,0x) untuk kepadatan sedang (mdpi)
  • 72x72 (1,5x) untuk kepadatan tinggi (hdpi)
  • 96x96 (2,0x) untuk kepadatan ekstra tinggi (xhdpi)
  • 144x144 (3,0x) untuk kepadatan ekstra ekstra tinggi (xxhdpi)
  • 192x192 (4,0x) untuk kepadatan ekstra sangat ekstra tinggi (xxxhdpi)

Tempatkan file gambar yang dihasilkan di subdirektori yang sesuai di 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

Kemudian, setiap kali Anda mereferensikan @drawable/awesomeimage, sistem akan memilih bitmap yang sesuai berdasarkan dpi layar. Jika Anda tidak menyediakan resource khusus kepadatan untuk kepadatan tersebut, sistem akan menemukan pencocokan terbaik berikutnya dan menskalakannya agar sesuai dengan layar.

Tips: Jika memiliki resource drawable yang tidak ingin diskalakan sistem, seperti saat Anda melakukan beberapa penyesuaian sendiri pada gambar saat runtime, tempatkan resource tersebut di direktori dengan pengontrol kualitas konfigurasi nodpi. Resource dengan penentu ini dianggap tidak bergantung pada kepadatan, dan sistem tidak akan menskalakannya.

Untuk informasi selengkapnya tentang pengontrol kualitas konfigurasi lainnya dan cara Android memilih resource yang sesuai untuk konfigurasi layar saat ini, lihat Ringkasan resource aplikasi.

Meletakkan ikon aplikasi di direktori mipmap

Seperti aset bitmap lainnya, Anda harus menyediakan versi khusus kepadatan ikon aplikasi Anda. Namun, beberapa peluncur aplikasi menampilkan ikon aplikasi Anda 25% lebih besar dari yang dipanggil oleh bucket kepadatan perangkat.

Misalnya, jika bucket kepadatan perangkat adalah xxhdpi dan ikon aplikasi terbesar yang Anda berikan berada dalam drawable-xxhdpi, peluncur aplikasi akan meningkatkan skala ikon ini, sehingga membuatnya tampak kurang jelas.

Untuk menghindari hal ini, tempatkan semua ikon aplikasi Anda di direktori mipmap, bukan direktori drawable. Tidak seperti direktori drawable, semua direktori mipmap dipertahankan di APK, meskipun Anda mem-build APK khusus kepadatan. Hal ini memungkinkan aplikasi peluncur memilih ikon resolusi terbaik untuk ditampilkan di layar utama.

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

Pada contoh perangkat xxhdpi sebelumnya, Anda dapat menyediakan ikon peluncur berkepadatan lebih tinggi di direktori mipmap-xxxhdpi.

Untuk panduan desain ikon, lihat Ikon sistem.

Untuk membantu mem-build ikon aplikasi, lihat Membuat ikon aplikasi dengan Image Asset Studio.

Saran untuk masalah kepadatan yang tidak biasa

Bagian ini menjelaskan cara Android melakukan penskalaan untuk bitmap pada kepadatan piksel yang berbeda dan cara mengontrol lebih jauh cara bitmap digambar pada kepadatan yang berbeda. Kecuali aplikasi memanipulasi grafis atau Anda mengalami masalah saat berjalan pada kepadatan piksel yang berbeda, Anda dapat mengabaikan bagian ini.

Untuk lebih memahami cara mendukung beberapa kepadatan saat memanipulasi grafis saat runtime, Anda perlu mengetahui cara sistem membantu memastikan skala yang tepat untuk bitmap. Tindakan ini dilakukan dengan cara berikut:

  1. Prapenskalaan resource, seperti drawable bitmap

    Berdasarkan kepadatan layar saat ini, sistem akan menggunakan resource khusus kepadatan dari aplikasi Anda. Jika resource tidak tersedia dalam kepadatan yang benar, sistem akan memuat resource default dan meningkatkan atau menurunkan skalanya sesuai kebutuhan. Sistem menganggap bahwa resource default (resource dari direktori tanpa penentu konfigurasi) didesain untuk kepadatan piksel dasar (mdpi) dan mengubah ukuran bitmap tersebut ke ukuran yang sesuai dengan kepadatan piksel saat ini.

    Jika Anda meminta dimensi resource yang telah diskalakan sebelumnya, sistem akan menampilkan nilai yang mewakili dimensi tersebut setelah penskalaan. Misalnya, bitmap yang didesain pada 50x50 piksel untuk layar mdpi akan diskalakan ke 75x75 piksel pada layar hdpi (jika tidak ada resource alternatif untuk hdpi), dan sistem akan melaporkan ukuran tersebut.

    Ada beberapa situasi yang mungkin membuat Anda tidak ingin Android melakukan prapenskalaan resource. Cara termudah untuk menghindari prapenskalaan adalah dengan memasukkan resource dalam direktori resource dengan penentu konfigurasi nodpi. Contoh:

    res/drawable-nodpi/icon.png

    Jika sistem menggunakan bitmap icon.png dari folder ini, sistem tidak akan menskalakannya berdasarkan kepadatan perangkat saat ini.

  2. Penskalaan otomatis dimensi dan koordinat piksel

    Anda dapat menonaktifkan dimensi dan gambar prapenskalaan dengan menetapkan android:anyDensity ke "false" dalam manifes atau secara terprogram untuk Bitmap dengan menetapkan inScaled ke "false". Pada kasus ini, sistem akan otomatis menskalakan nilai koordinat piksel absolut dan nilai dimensi piksel apa pun pada waktu menggambar. Sistem melakukannya untuk memastikan elemen layar yang ditentukan dengan piksel tetap ditampilkan pada ukuran fisik yang kurang-lebih sama dengan yang dapat ditampilkan pada kepadatan piksel dasar (mdpi). Sistem menangani penskalaan ini secara transparan ke aplikasi dan melaporkan dimensi piksel yang diskalakan ke aplikasi, bukan dimensi piksel fisik.

    Sebagai contoh, misalkan sebuah perangkat memiliki layar berkepadatan tinggi WVGA 480x800 dan ukurannya sama dengan layar HVGA tradisional—tetapi perangkat menjalankan aplikasi yang telah menonaktifkan penskalaan sebelumnya. Dalam hal ini, sistem "berbohong" pada aplikasi saat membuat kueri untuk dimensi layar dan melaporkan 320x533, perkiraan terjemahan mdpi untuk kepadatan piksel.

    Kemudian, saat aplikasi melakukan operasi menggambar, seperti membatalkan validasi persegi panjang dari (10,10) hingga (100, 100), sistem akan mengubah koordinat dengan menskalakannya ke jumlah yang sesuai, dan benar-benar membatalkan validasi area (15,15) hingga (150, 150). Perbedaan ini dapat menyebabkan perilaku tak terduga jika aplikasi Anda secara langsung memanipulasi bitmap yang diskalakan, tetapi hal ini dianggap sebagai konsekuensi yang wajar untuk memastikan performa aplikasi yang terbaik. Jika Anda mengalami situasi ini, baca Mengonversi satuan dp ke satuan piksel.

    Biasanya, Anda tidak menonaktifkan prapenskalaan. Cara terbaik untuk mendukung beberapa layar adalah dengan mengikuti teknik dasar yang dijelaskan di halaman ini.

Jika aplikasi Anda memanipulasi bitmap atau berinteraksi secara langsung dengan piksel di layar dengan cara lain, Anda mungkin perlu mengambil langkah tambahan untuk mendukung kepadatan piksel yang berbeda. Misalnya, jika Anda merespons gestur sentuh dengan menghitung jumlah piksel yang dilalui jari, Anda harus menggunakan nilai piksel kepadatan mandiri yang sesuai, bukan piksel sebenarnya, tetapi Anda dapat mengonversi antara nilai dp dan px.

Menguji di semua kepadatan piksel

Uji aplikasi Anda di beberapa perangkat dengan kepadatan piksel yang berbeda sehingga Anda dapat memastikan UI diskalakan dengan benar. Menguji pada perangkat fisik jika memungkinkan; gunakan Android Emulator jika Anda tidak memiliki akses ke perangkat fisik untuk semua kepadatan piksel yang berbeda.

Jika ingin menguji di perangkat fisik, tetapi tidak ingin membeli perangkat, Anda dapat menggunakan Firebase Test Lab untuk mengakses perangkat di pusat data Google.