Saat mengembangkan aplikasi, pernah terpikirkankah jika warna tema aplikasi kita itu bisa menyesuaikan dengan konten yang ditampilkan? Misal, warna untuk widget Toolbar bisa menyesuaikan dengan warna dominan sebuah gambar yang sedang ditampilkan. Tentu lebih menarik loh. Mau tahu bagaimana caranya bisa seperti itu? Yuk kenalan dengan Android Palette.
Apa itu Android Palette
Palette merupakan sebuah API yang disediakan google yang dapat membantu kita untuk mendapatkan informasi warna dari sebuah gambar. Mekanisme dari Palette ini adalah mengekstrak gambar bitmap dan disesuaikan dengan beberapa profil. Berikut adalah 6 profil warna utama yang bisa kita dapatkan dari sebuah gambar menggunakan Palette:
💻 Mulai Belajar Pemrograman
Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.
Daftar Sekarang- Light Vibrant
- Vibrant
- Dark Vibrant
- Light Muted
- Muted
- Dark Muted
Selain itu, kita juga bisa mendapatkan informasi yang lebih detail dari setiap profil warna di atas dengan memanfaatkan Palette.Swatch. Dengannya, kita bisa mendapatkan informasi seperti nilai HSL dan nilai populasi warna setiap pixel. Kita bisa menggunakannya untuk membuat skema warna yang komprehensif untuk aplikasi yang kita kembangkan.
Menggunakan Android Palette
Untuk menggunakan Android Palette sendiri cukup mudah. Kita tinggal menambahkan dependensi berikut ke dalam build.gradle:
1 2 3 4 |
dependencies { ... implementation 'androidx.palette:palette:1.0.0' } |
Nah, untuk mendapatkan daftar warna, terdapat 2 cara. Pertama, dengan memanfaatkan fungsi sinkronus yaitu fungsi generate. Contohnya seperti berikut:
1 |
fun createPaletteSync(bitmap: Bitmap): Palette = Palette.from(bitmap).generate() |
Kedua, memanfaatkan fungsi asinkron seperti berikut:
1 2 3 4 |
fun createPaletteAsync(bitmap: Bitmap) { Palette.from(bitmap).generate { palette -> } } |
Cara di atas digunakan jika kita ingin memakai Palette dari thread lain. Cukup sederhana bukan?
Implementasi Android Palette ke dalam proyek
Baik, mari coba kita implementasi bersama Palette ke dalam proyek Android. Dimulai dengan membuat project baru pada Android studio dilanjutkan dengan menambahkan dependensi Palette seperti yang sudah disebutkan di atas. Setelah selesai, buka berkas activity_main.xml dan ubah isinya menjadi seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <View android:id="@+id/prevDominant" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevMutedDarkLight" /> <TextView android:id="@+id/textView11" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Dominant" app:layout_constraintBottom_toBottomOf="@+id/prevDominant" app:layout_constraintStart_toEndOf="@+id/prevDominant" app:layout_constraintTop_toTopOf="@+id/prevDominant" /> <View android:id="@+id/prevMutedDarkLight" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevMutedDark" /> <TextView android:id="@+id/textView10" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Muted Light" app:layout_constraintBottom_toBottomOf="@+id/prevMutedDarkLight" app:layout_constraintStart_toEndOf="@+id/prevMutedDarkLight" app:layout_constraintTop_toTopOf="@+id/prevMutedDarkLight" /> <View android:id="@+id/prevMutedDark" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevMuted" /> <TextView android:id="@+id/textView9" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Muted Dark" app:layout_constraintBottom_toBottomOf="@+id/prevMutedDark" app:layout_constraintStart_toEndOf="@+id/prevMutedDark" app:layout_constraintTop_toTopOf="@+id/prevMutedDark" /> <View android:id="@+id/prevMuted" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevVibrantLight" /> <TextView android:id="@+id/textView8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Muted" app:layout_constraintBottom_toBottomOf="@+id/prevMuted" app:layout_constraintStart_toEndOf="@+id/prevMuted" app:layout_constraintTop_toTopOf="@+id/prevMuted" /> <View android:id="@+id/prevVibrantLight" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevVibrantDark" /> <TextView android:id="@+id/textView4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Vibrant Light" app:layout_constraintBottom_toBottomOf="@+id/prevVibrantLight" app:layout_constraintStart_toEndOf="@+id/prevVibrantLight" app:layout_constraintTop_toTopOf="@+id/prevVibrantLight" /> <View android:id="@+id/prevVibrantDark" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/prevVibrant" /> <TextView android:id="@+id/textView2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Vibrant Dark" app:layout_constraintBottom_toBottomOf="@+id/prevVibrantDark" app:layout_constraintStart_toEndOf="@+id/prevVibrantDark" app:layout_constraintTop_toTopOf="@+id/prevVibrantDark" /> <View android:id="@+id/prevVibrant" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginStart="16dp" android:layout_marginTop="32dp" android:background="@color/colorAccent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/imagePreview" /> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Vibrant" app:layout_constraintBottom_toBottomOf="@+id/prevVibrant" app:layout_constraintStart_toEndOf="@+id/prevVibrant" app:layout_constraintTop_toTopOf="@+id/prevVibrant" /> <ImageView android:id="@+id/imagePreview" android:layout_width="150dp" android:layout_height="220dp" android:layout_marginTop="8dp" android:scaleType="centerCrop" android:src="@mipmap/ic_launcher" app:layout_constraintEnd_toEndOf="@+id/spinnerSelectImage" app:layout_constraintStart_toStartOf="@+id/spinnerSelectImage" app:layout_constraintTop_toBottomOf="@+id/spinnerSelectImage" /> <Spinner android:id="@+id/spinnerSelectImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:spinnerMode="dialog" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
Susunan layout di atas terdapat sebuah Spinner, ImageView serta beberapa TextView dan View. Beberapa View di atas akan kita gunakan untuk menampilkan cuplikan warna setiap profil sebuah gambar. Tampilan dari susunan layout di atas kurang lebih akan seperti berikut:
Berikutnya, mari kita tambahkan beberapa buah gambar yang akan kita coba dapatkan daftar warnanya. Untuk gambarnya sendiri bisa diunduh di sini. Setelah diunduh, tambahkan gambar ke dalam project yang sudah dibuat. Pastikan gambar tersebut berada di dalam folder drawable, bukan di dalam drawable-v24 agar dapat berjalan dengan baik pada device dengan level di bawah API 24.
Selanjutnya, mari kita modifikasi sedikit isi dari kelas MainActivity. Langsung saja buka kelas tersebut kemudian tambahkan beberapa baris kode berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
private val images = listOf( R.drawable.poster_aladdin, R.drawable.poster_angel_has_fallen, R.drawable.poster_avengers_endgame, R.drawable.poster_cars, R.drawable.poster_dark_phoenix, R.drawable.poster_fast___furious_presents_hobbs___shaw, R.drawable.poster_godzilla_king_of_the_monsters, R.drawable.poster_good_boys, R.drawable.poster_it_chapter_two, R.drawable.poster_john_wick, R.drawable.poster_john_wick_chapter_3__parabellum, R.drawable.poster_men_in_black_international, R.drawable.poster_red_shoes_and_the_seven_dwarfs, R.drawable.poster_spiderman_far_from_home, R.drawable.poster_the_caged_flower, R.drawable.poster_the_dead_dont_die, R.drawable.poster_the_lion_king, R.drawable.poster_the_old_man___the_gun ) private val spinnerItems = Array(images.size) { "Gambar ${it + 1}" } |
Variable images di atas akan digunakan sebagai variabel yang akan menampung resources gambar yang akan coba kita dapatkan daftar warnanya. Sedangkan variabel spinnerItems, seperti namanya, variabel tersebut akan kita gunakan untuk ditampilkan pada Spinner yang akan mewakili setiap gambar yang akan kita gunakan.
Masih di kelas MainActivity, tambahkan fungsi untuk mendapatkan warna berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private fun generatePalette(bitmap: Bitmap) { Palette.from(bitmap).generate { palette -> val defValue = 0x000000 prevVibrant.setBackgroundColor(palette?.getVibrantColor(defValue) ?: defValue) prevVibrantDark.setBackgroundColor(palette?.getDarkVibrantColor(defValue) ?: defValue) prevVibrantLight.setBackgroundColor(palette?.getLightVibrantColor(defValue) ?: defValue) prevMuted.setBackgroundColor(palette?.getMutedColor(defValue) ?: defValue) prevMutedDark.setBackgroundColor(palette?.getDarkMutedColor(defValue) ?: defValue) prevMutedDarkLight.setBackgroundColor(palette?.getLightMutedColor(defValue) ?: defValue) prevDominant.setBackgroundColor(palette?.getDominantColor(defValue) ?: defValue) } } |
Fungsi di atas memiliki parameter dengan tipe bitmap yang digunakan sebagai argumen untuk fungsi generate. Kemudian di dalam fungsi generate itu sendiri, terdapat beberapa fungsi lagi yang digunakan untuk mendapatkan warna berdasarkan profil yang sudah disebutkan di awal.
Nah, jika diperhatikan lagi kode di atas, di dalam fungsi generate terdapat 1 (satu) variabel yaitu defValue. Fungsinya apa? Variabel tersebut akan digunakan sebagai nilai default ketika nilai dari warna yang ingin didapat tidak tersedia.
Terakhir, ubahlah isi dari fungsi onCreate menjadi seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val adapter = ArrayAdapter( this, android.R.layout.simple_spinner_dropdown_item, spinnerItems ) spinnerSelectImage.adapter = adapter spinnerSelectImage.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(p0: AdapterView<*>?) { } override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { imagePreview.setImageResource(images[p2]) generatePalette(BitmapFactory.decodeResource(resources, images[p2])) } } } |
Seperti halnya ketika kita membuat sebuah Spinner, fungsi onCreate di atas terdapat inisialisasi adapter untuk Spinner dan event listener ketika item yang ada pada Spinner dipilih. Oke, sampai di sini kita sudah selesai dan isi dari kelas MainActivity kurang lebih seperti berikut:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val adapter = ArrayAdapter( this, android.R.layout.simple_spinner_dropdown_item, spinnerItems ) spinnerSelectImage.adapter = adapter spinnerSelectImage.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(p0: AdapterView<*>?) { } override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { imagePreview.setImageResource(images[p2]) generatePalette(BitmapFactory.decodeResource(resources, images[p2])) } } } private fun generatePalette(bitmap: Bitmap) { Palette.from(bitmap).generate { palette -> val defValue = 0x000000 prevVibrant.setBackgroundColor(palette?.getVibrantColor(defValue) ?: defValue) prevVibrantDark.setBackgroundColor(palette?.getDarkVibrantColor(defValue) ?: defValue) prevVibrantLight.setBackgroundColor(palette?.getLightVibrantColor(defValue) ?: defValue) prevMuted.setBackgroundColor(palette?.getMutedColor(defValue) ?: defValue) prevMutedDark.setBackgroundColor(palette?.getDarkMutedColor(defValue) ?: defValue) prevMutedDarkLight.setBackgroundColor(palette?.getLightMutedColor(defValue) ?: defValue) prevDominant.setBackgroundColor(palette?.getDominantColor(defValue) ?: defValue) } } private val images = listOf( R.drawable.poster_aladdin, R.drawable.poster_angel_has_fallen, R.drawable.poster_avengers_endgame, R.drawable.poster_cars, R.drawable.poster_dark_phoenix, R.drawable.poster_fast___furious_presents_hobbs___shaw, R.drawable.poster_godzilla_king_of_the_monsters, R.drawable.poster_good_boys, R.drawable.poster_it_chapter_two, R.drawable.poster_john_wick, R.drawable.poster_john_wick_chapter_3__parabellum, R.drawable.poster_men_in_black_international, R.drawable.poster_red_shoes_and_the_seven_dwarfs, R.drawable.poster_spiderman_far_from_home, R.drawable.poster_the_caged_flower, R.drawable.poster_the_dead_dont_die, R.drawable.poster_the_lion_king, R.drawable.poster_the_old_man___the_gun ) private val spinnerItems = Array(images.size) { "Gambar ${it + 1}" } } |
Jika dirasa sudah selesai, proyeknya bisa langsung dijalankan. Jika tidak terdapat error, maka akan seperti berikut:
Kesimpulan
Menentukan warna untuk sebuah style adalah sesuatu yang tidak mudah dalam pengembangan aplikasi. Sebabnya, ketika kita salah menentukan warna, pengguna aplikasi akan cepat bosan berlama-lama berselancar di dalam aplikasi. Dengan Android Palette, kita bebas membiarkan pengguna memilih warna yang akan digunakan berdasarkan konten yang akan ditampilkan.
Sampai disini dulu ya tutorial kali ini. Jika ada yang kurang jelas atau ada yang mau ditanyakan, silakan tulis di kolom komentar. Untuk yang mau melihat kode sumber dari project yang sudah kita buat, teman-teman bisa cek di laman Github ini.
Salam!
Artikel terkait:
https://www.dicoding.com/blog/kenal-lebih-dekat-dengan-constraintlayout/