Membuat komponen tampilan kustom

Coba cara Compose
Jetpack Compose adalah toolkit UI yang direkomendasikan untuk Android. Pelajari cara menggunakan tata letak di Compose.

Android menawarkan model canggih dan andal yang terbagi menjadi komponen terpisah untuk membangun UI, berdasarkan class tata letak dasar View dan ViewGroup. Platform ini mencakup berbagai subclass View dan ViewGroup bawaan—yang masing-masing disebut widget dan tata letak—yang dapat Anda gunakan untuk membuat UI.

Daftar sebagian widget yang tersedia mencakup Button, TextView, EditText, ListView, CheckBox, RadioButton, Gallery, Spinner, dan AutoCompleteTextView, ImageSwitcher, dan TextSwitcher yang memiliki tujuan khusus lainnya.

Di antara tata letak yang tersedia adalah LinearLayout, FrameLayout, RelativeLayout, dan lainnya. Untuk contoh lainnya, lihat Tata letak umum.

Jika tidak ada widget atau tata letak bawaan yang memenuhi kebutuhan, Anda dapat membuat subclass View Anda sendiri. Jika hanya perlu melakukan sedikit penyesuaian pada widget atau tata letak yang ada, Anda dapat membuat subclass untuk widget atau tata letak dan mengganti metodenya.

Membuat subclass View Anda sendiri memberi Anda kontrol yang akurat atas tampilan dan fungsi elemen layar. Untuk memberikan gambaran tentang kontrol yang Anda dapatkan dengan tampilan kustom, berikut beberapa contoh hal yang dapat Anda lakukan dengan tampilan kustom:

  • Anda dapat membuat jenis View yang sepenuhnya dirender secara kustom—misalnya, tombol "kontrol volume", yang dirender menggunakan grafis 2D, yang menyerupai kontrol elektronik analog.
  • Anda dapat menggabungkan grup komponen View menjadi komponen tunggal baru, mungkin untuk membuat sesuatu seperti kotak kombinasi (kombinasi daftar pop-up dan kolom teks entri bebas), kontrol pemilih dua panel (panel kiri dan kanan dengan daftar di setiap panel, tempat Anda dapat menetapkan ulang item yang ada dalam daftar), dan seterusnya.
  • Anda dapat mengganti cara komponen EditText dirender di layar. Aplikasi contoh NotePad menggunakan efek ini untuk membuat halaman notepad bergaris.
  • Anda dapat merekam peristiwa lainnya, seperti penekanan tombol, dan menanganinya dengan cara kustom, seperti untuk game.

Bagian berikut menjelaskan cara membuat tampilan kustom dan menggunakannya dalam aplikasi Anda. Untuk informasi referensi mendetail, lihat class View.

Pendekatan dasar

Berikut ini ringkasan umum tentang hal yang perlu diketahui untuk membuat komponen View Anda sendiri:

  1. Perluas class atau subclass View yang ada dengan class Anda sendiri.
  2. Ganti beberapa metode dari superclass. Metode superclass yang akan diganti dimulai dengan on—misalnya, onDraw(), onMeasure(), dan onKeyDown(). Hal ini mirip dengan peristiwa on di Activity atau ListActivity yang Anda ganti untuk siklus proses dan hook fungsi lainnya.
  3. Gunakan class ekstensi baru. Setelah selesai, Anda dapat menggunakan class ekstensi baru sebagai pengganti tampilan yang menjadi dasarnya.

Komponen yang disesuaikan sepenuhnya

Anda dapat membuat komponen grafis yang disesuaikan sepenuhnya, yang muncul sesuai keinginan Anda. Mungkin Anda menginginkan VU meter grafis yang terlihat seperti pengukur analog lama, atau tampilan teks menyanyi bersama dengan bola yang memantul mengikuti kata saat Anda bernyanyi bersama mesin karaoke. Anda mungkin menginginkan sesuatu yang tidak dapat dilakukan oleh komponen bawaan, terlepas dari cara Anda menggabungkannya.

Untungnya, Anda dapat membuat komponen yang terlihat dan berperilaku dengan cara apa pun yang Anda inginkan, yang hanya dibatasi oleh imajinasi Anda, ukuran layar, dan daya pemrosesan yang tersedia, mengingat bahwa aplikasi Anda mungkin harus berjalan pada sesuatu dengan daya yang jauh lebih sedikit daripada workstation desktop Anda.

Untuk membuat komponen yang disesuaikan sepenuhnya, pertimbangkan hal berikut:

  • Tampilan paling umum yang dapat diperluas adalah View, jadi biasanya Anda memulai dengan memperluas tampilan ini untuk membuat komponen super baru.
  • Anda dapat menyediakan konstruktor, yang dapat mengambil atribut dan parameter dari XML, serta dapat menggunakan atribut dan parameter Anda sendiri, seperti warna dan rentang VU meter atau lebar dan peredaman jarum.
  • Anda mungkin ingin membuat pemroses peristiwa, pengakses properti, dan pengubah sendiri serta perilaku yang lebih canggih di class komponen Anda.
  • Anda kemungkinan besar ingin mengganti onMeasure() dan juga perlu mengganti onDraw() jika ingin komponen menampilkan sesuatu. Meskipun keduanya memiliki perilaku default, onDraw() default tidak melakukan apa pun, dan onMeasure() default selalu menetapkan ukuran 100x100, yang mungkin tidak Anda inginkan.
  • Anda juga dapat mengganti metode on lain, sesuai kebutuhan.

Memperluas onDraw() dan onMeasure()

Metode onDraw() menghasilkan Canvas tempat Anda dapat mengimplementasikan apa pun yang Anda inginkan: grafis 2D, komponen standar atau kustom lainnya, teks bergaya, atau hal lain yang Anda inginkan.

onMeasure() sedikit lebih terlibat. onMeasure() adalah bagian penting dari kontrak rendering antara komponen Anda dan container-nya. onMeasure() harus diganti untuk melaporkan pengukuran bagian yang ada di dalamnya secara efisien dan akurat. Hal ini dibuat sedikit lebih kompleks dengan persyaratan batas dari induk—yang diteruskan ke metode onMeasure()—dan dengan persyaratan untuk memanggil metode setMeasuredDimension() dengan lebar dan tinggi yang diukur setelah dihitung. Jika Anda tidak memanggil metode ini dari metode onMeasure() yang diganti, metode tersebut akan menghasilkan pengecualian pada waktu pengukuran.

Pada level yang tinggi, penerapan onMeasure() akan terlihat seperti ini:

  • Metode onMeasure() yang diganti dipanggil dengan spesifikasi lebar dan tinggi, yang diperlakukan sebagai persyaratan untuk batasan pengukuran lebar dan tinggi yang Anda hasilkan. Parameter widthMeasureSpec dan heightMeasureSpec adalah kode bilangan bulat yang mewakili dimensi. Referensi lengkap untuk jenis batasan yang dapat diwajibkan oleh spesifikasi ini dapat ditemukan di dokumentasi referensi dalam View.onMeasure(int, int) Dokumentasi referensi ini juga menjelaskan seluruh operasi pengukuran.
  • Metode onMeasure() komponen Anda menghitung lebar dan tinggi pengukuran, yang diperlukan untuk merender komponen. Aplikasi harus berusaha tetap berada dalam spesifikasi yang diteruskan, meskipun dapat melebihi spesifikasi tersebut. Dalam hal ini, induk dapat memilih tindakan yang harus dilakukan, termasuk memotong, men-scroll, menampilkan pengecualian, atau meminta onMeasure() untuk mencoba lagi, mungkin dengan spesifikasi pengukuran yang berbeda.
  • Saat lebar dan tinggi dihitung, panggil metode setMeasuredDimension(int width, int height) dengan pengukuran yang telah dihitung. Kegagalan dalam melakukannya akan menghasilkan pengecualian.

Berikut ringkasan metode standar lain yang dipanggil framework pada tampilan:

Kategori Metode Deskripsi
Kreasi Konstruktor Ada bentuk konstruktor yang dipanggil saat tampilan dibuat dari kode dan bentuk yang dipanggil saat tampilan di-inflate dari file tata letak. Bentuk kedua mengurai dan menerapkan atribut yang ditetapkan dalam file tata letak.
onFinishInflate() Dipanggil setelah tampilan dan semua turunannya di-inflate dari XML.
Tata Letak onMeasure(int, int) Dipanggil untuk menentukan persyaratan ukuran untuk tampilan ini dan semua turunannya.
onLayout(boolean, int, int, int, int) Dipanggil saat tampilan ini harus menetapkan ukuran dan posisi ke semua turunannya.
onSizeChanged(int, int, int, int) Dipanggil saat ukuran tampilan ini diubah.
Gambar onDraw(Canvas) Dipanggil saat tampilan harus merender kontennya.
Pemrosesan peristiwa onKeyDown(int, KeyEvent) Dipanggil saat peristiwa tombol ke bawah terjadi.
onKeyUp(int, KeyEvent) Dipanggil saat peristiwa tombol ke atas terjadi.
onTrackballEvent(MotionEvent) Dipanggil saat peristiwa gerakan trackball terjadi.
onTouchEvent(MotionEvent) Dipanggil saat peristiwa gerakan layar sentuh terjadi.
Fokus onFocusChanged(boolean, int, Rect) Dipanggil saat tampilan mendapatkan atau kehilangan fokus.
onWindowFocusChanged(boolean) Dipanggil saat jendela yang berisi tampilan mendapatkan atau kehilangan fokus.
Pemasangan onAttachedToWindow() Dipanggil saat tampilan dilampirkan ke jendela.
onDetachedFromWindow() Dipanggil saat tampilan dilepas dari jendelanya.
onWindowVisibilityChanged(int) Dipanggil saat visibilitas jendela yang berisi tampilan diubah.

Kontrol gabungan

Jika Anda tidak ingin membuat komponen yang sepenuhnya disesuaikan, tetapi ingin menyatukan komponen yang dapat digunakan kembali yang terdiri dari grup kontrol yang sudah ada, maka membuat komponen majemuk (atau kontrol gabungan) mungkin merupakan pilihan yang terbaik. Singkatnya, hal ini menyatukan sejumlah kontrol atau tampilan yang lebih atomik ke dalam grup item logis yang dapat diperlakukan sebagai satu hal. Misalnya, kotak kombinasi dapat berupa kombinasi kolom EditText baris tunggal dan tombol yang bersebelahan dengan lampiran daftar pop-up. Jika pengguna mengetuk tombol dan memilih sesuatu dari daftar, kolom EditText akan diisi, tetapi pengguna juga dapat mengetik sesuatu langsung ke EditText jika diinginkan.

Di Android, ada dua tampilan lain yang tersedia untuk melakukan hal ini: Spinner dan AutoCompleteTextView. Terlepas dari itu, konsep untuk kotak kombo ini adalah contoh yang baik.

Untuk membuat komponen gabungan, lakukan langkah berikut:

  • Sama seperti Activity, gunakan pendekatan deklaratif (berbasis XML) untuk membuat komponen yang ada di dalamnya atau menambahkan komponen tersebut secara terprogram dari kode Anda. Titik awal yang biasa adalah semacam Layout, jadi buat class yang memperluas Layout. Dalam kasus kotak kombinasi, Anda dapat menggunakan LinearLayout dengan orientasi horizontal. Anda dapat menyusun bertingkat tata letak lain di dalamnya, sehingga komponen compound dapat menjadi kompleks dan terstruktur secara bebas.
  • Dalam konstruktor untuk class baru, ambil parameter apa pun yang diharapkan superclass dan teruskan melalui konstruktor superclass terlebih dahulu. Kemudian, Anda dapat menyiapkan tampilan lain untuk digunakan dalam komponen baru. Di sinilah Anda membuat kolom EditText dan daftar pop-up. Anda dapat memperkenalkan atribut dan parameter sendiri ke dalam XML yang dapat diambil dan digunakan oleh konstruktor.
  • Atau, buat pemroses untuk peristiwa yang mungkin dihasilkan oleh tampilan yang ada. Salah satu contohnya adalah metode pemroses bagi pemroses klik item daftar guna memperbarui konten EditText jika daftar telah dipilih.
  • Atau, buat properti Anda sendiri dengan pengakses dan pengubah. Misalnya, biarkan nilai EditText ditetapkan di awal dalam komponen dan membuat kueri untuk kontennya jika diperlukan.
  • Secara opsional, ganti onDraw() dan onMeasure(). Hal ini biasanya tidak diperlukan saat memperluas Layout, karena tata letak memiliki perilaku default yang kemungkinan akan berfungsi dengan baik.
  • Secara opsional, ganti metode on lainnya, seperti onKeyDown(), misalnya untuk memilih nilai default tertentu dari daftar pop-up kotak kombinasi saat tombol tertentu diketuk.

Ada keuntungan dari menggunakan Layout sebagai dasar untuk kontrol kustom, termasuk hal berikut:

  • Anda dapat menentukan tata letak menggunakan file XML deklaratif, seperti pada layar aktivitas, atau Anda dapat membuat tampilan secara terprogram dan menyarangkannya ke dalam tata letak dari kode Anda.
  • Metode onDraw() dan onMeasure(), serta sebagian besar metode on lainnya, memiliki perilaku yang sesuai, sehingga Anda tidak perlu menggantinya.
  • Anda dapat dengan cepat membuat tampilan gabungan kompleks secara arbitrer dan menggunakannya kembali seolah-olah merupakan komponen tunggal.

Mengubah jenis tampilan yang ada

Jika ada komponen yang mirip dengan yang diinginkan, Anda dapat memperluas komponen tersebut dan mengganti perilaku yang ingin diubah. Anda dapat melakukan semua hal yang Anda lakukan dengan komponen yang disesuaikan sepenuhnya, tetapi dengan memulai dari class yang lebih khusus di hierarki View, Anda bisa mendapatkan beberapa perilaku yang melakukan hal yang Anda inginkan secara gratis.

Misalnya, aplikasi contoh NotePad menunjukkan banyak aspek penggunaan platform Android. Di antaranya adalah memperluas tampilan EditText untuk membuat notepad bergaris. Ini bukan contoh yang sempurna, dan API untuk melakukan ini mungkin berubah, tetapi ini menunjukkan prinsipnya.

Jika Anda belum melakukannya, impor contoh NotePad ke Android Studio atau lihat sumbernya menggunakan link yang diberikan. Secara khusus, lihat definisi LinedEditText dalam file NoteEditor.java.

Berikut beberapa hal yang perlu diketahui dalam file ini:

  1. Definisi

    Class ditentukan dengan baris berikut:
    public static class LinedEditText extends EditText

    LinedEditText didefinisikan sebagai class dalam di dalam aktivitas NoteEditor, tetapi bersifat publik sehingga dapat diakses sebagai NoteEditor.LinedEditText dari luar class NoteEditor.

    Selain itu, LinedEditText bersifat static, yang berarti metode tersebut tidak menghasilkan "metode sintetis" yang memungkinkannya mengakses data dari class induk. Ini artinya berperilaku sebagai class terpisah, bukan class yang sangat terkait dengan NoteEditor. Ini adalah cara yang lebih sederhana untuk membuat class dalam jika tidak memerlukan akses ke status dari class luar. Ini membuat class yang dihasilkan tetap kecil dan memungkinkannya digunakan dengan mudah dari class lain.

    LinedEditText memperluas EditText, yang merupakan tampilan yang akan disesuaikan dalam kasus ini. Setelah Anda selesai, class baru dapat menggantikan tampilan EditText normal.

  2. Inisialisasi class

    Seperti biasa, super dipanggil terlebih dahulu. Ini bukan konstruktor default, tetapi merupakan konstruktor berparameter. EditText dibuat dengan parameter ini saat di-inflate dari file tata letak XML. Dengan demikian, konstruktor juga perlu mengambilnya dan meneruskannya ke konstruktor superclass.

  3. Metode yang diganti

    Contoh ini hanya mengganti metode onDraw(), tetapi Anda mungkin perlu mengganti metode lain saat membuat komponen kustom sendiri.

    Untuk contoh ini, mengganti metode onDraw() memungkinkan Anda menggambar garis biru di kanvas tampilan EditText. Kanvas diteruskan ke metode onDraw() yang telah diganti. Metode super.onDraw() dipanggil sebelum metode berakhir. Metode superclass harus dipanggil. Dalam hal ini, panggil metode tersebut di bagian akhir setelah menggambar garis yang ingin Anda sertakan.

  4. Komponen kustom

    Anda kini memiliki komponen kustom, tetapi bagaimana cara menggunakannya? Dalam contoh NotePad, komponen kustom digunakan langsung dari tata letak deklaratif, jadi lihat note_editor.xml di folder res/layout:

    <view xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.example.android.notepad.NoteEditor$LinedEditText"
        android:id="@+id/note"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:padding="5dp"
        android:scrollbars="vertical"
        android:fadingEdge="vertical"
        android:gravity="top"
        android:textSize="22sp"
        android:capitalize="sentences"
    />
    

    Komponen kustom dibuat sebagai tampilan umum dalam XML, dan class-nya ditetapkan menggunakan paket lengkap. Class dalam yang Anda tentukan direferensikan menggunakan notasi NoteEditor$LinedEditText, yang merupakan cara standar untuk merujuk ke inner class dalam bahasa pemrograman Java.

    Jika komponen tampilan kustom tidak didefinisikan sebagai class dalam, Anda dapat mendeklarasikan komponen tampilan dengan nama elemen XML dan mengecualikan atribut class. Contoh:

    <com.example.android.notepad.LinedEditText
      id="@+id/note"
      ... />
    

    Perlu diketahui bahwa class LinedEditText kini merupakan file class terpisah. Jika class disusun bertingkat di class NoteEditor, teknik ini tidak akan berfungsi.

    Atribut dan parameter lain dalam definisi tersebut adalah atribut dan parameter yang diteruskan ke konstruktor komponen kustom, kemudian diteruskan ke konstruktor EditText, sehingga merupakan parameter yang sama dengan yang Anda gunakan untuk tampilan EditText. Anda juga dapat menambahkan parameter sendiri.

Membuat komponen kustom semudah yang Anda butuhkan.

Komponen yang lebih canggih dapat mengganti lebih banyak metode on dan memperkenalkan metode helpernya sendiri, yang secara substansial menyesuaikan properti dan perilakunya. Satu-satunya batasan adalah imajinasi Anda dan apa yang harus dilakukan komponen tersebut.