Komponen Tampilan Kustom

Android menawarkan model canggih dan andal yang terbagi menjadi komponen terpisah untuk mem-build UI, berdasarkan class tata letak dasar: View dan ViewGroup. Sebagai permulaan, platform ini mencakup berbagai subclass View dan ViewGroup bawaan—masing-masing disebut widget dan tata letak— yang dapat digunakan untuk mem-build UI.

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

Tata letak yang tersedia antara lain LinearLayout, FrameLayout, RelativeLayout, dan lainnya. Untuk contoh lainnya, lihat Objek Tata Letak Umum.

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

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

  • Anda dapat membuat jenis View yang sepenuhnya dirender secara kustom, misalnya tombol "kontrol volume" yang dirender menggunakan grafis 2D, dan yang menyerupai kontrol elektronik analog.
  • Anda dapat menggabungkan sekumpulan komponen View menjadi satu komponen baru, mungkin untuk membuat sesuatu seperti Kotak Kombinasi (kombinasi daftar pop-up dan kolom teks entri bebas), kontrol pemilih panel ganda (panel kiri dan kanan dengan daftar di masing-masing panel, tempat Anda dapat menetapkan kembali letak setiap item dalam setiap daftar), dan sebagainya.
  • Anda dapat mengganti cara komponen EditText dirender di layar ( Tutorial Notepad menggunakan efek yang baik ini untuk membuat halaman notepad bergaris).
  • Anda dapat mencatat peristiwa lain seperti penekanan tombol dan menanganinya dengan cara kustom (seperti untuk game).

Bagian di bawah ini menjelaskan cara membuat View kustom dan menggunakannya di aplikasi. Untuk mengetahui informasi referensi yang mendetail, buka class View.

Pendekatan Dasar

Berikut ringkasan umum tentang hal yang perlu diketahui untuk memulai pembuatan 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 perlu diganti dimulai dengan 'on', misalnya, onDraw(), onMeasure(), dan onKeyDown(). 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, class ekstensi baru dapat digunakan sebagai pengganti tampilan yang menjadi dasarnya.

Tips: Class ekstensi dapat didefinisikan sebagai class dalam di dalam aktivitas yang menggunakannya. Ini berguna karena class ekstensi mengontrol akses ke class dalam, tetapi tidak diperlukan (mungkin Anda ingin membuat View publik baru untuk penggunaan yang lebih luas di aplikasi).

Komponen yang Disesuaikan Sepenuhnya

Komponen yang disesuaikan sepenuhnya dapat digunakan untuk membuat komponen grafis yang muncul sesuai keinginan Anda. Mungkin VU meter grafis yang tampak seperti pengukur analog lama, atau tampilan teks untuk menyanyikan lagu, dengan bola yang bergerak mengikuti kata sehingga Anda dapat bernyanyi bersama mesin karaoke. Apa pun itu, Anda memerlukan sesuatu yang tidak dapat dilakukan oleh komponen bawaan, bagaimana pun Anda menggabungkannya.

Untungnya, Anda dapat dengan mudah membuat komponen yang terlihat dan berperilaku sesuai keinginan, yang mungkin hanya dibatasi oleh imajinasi Anda, ukuran layar, dan daya pemrosesan yang tersedia (ingat bahwa pada akhirnya aplikasi mungkin harus dijalankan pada perangkat dengan daya yang jauh lebih sedikit dibandingkan workstation desktop).

Untuk membuat komponen yang disesuaikan sepenuhnya:

  1. Tentu saja, tampilan paling umum yang dapat diperluas adalah View, jadi biasanya Anda akan memulai dengan memperluasnya untuk membuat komponen super baru.
  2. Anda dapat menyediakan konstruktor yang mampu mengambil atribut dan parameter dari XML. Anda juga dapat memakai atribut dan parameter sendiri (mungkin warna dan rentang VU meter, atau lebar dan peredaman jarum, dan sebagainya.)
  3. Anda mungkin ingin membuat pemroses peristiwa, pengakses properti, dan pengubah sendiri, dan mungkin juga perilaku yang lebih rumit dalam class komponen.
  4. Kemungkinan besar Anda ingin mengganti onMeasure() dan juga perlu mengganti onDraw() jika ingin agar komponen menampilkan sesuatu. Meskipun keduanya memiliki perilaku default, onDraw() default tidak akan melakukan apa pun, dan onMeasure() default akan selalu menetapkan ukuran 100x100—yang mungkin bukan ukuran yang Anda inginkan.
  5. Metode on... lain juga dapat diganti sesuai kebutuhan.

Memperluas onDraw() dan onMeasure()

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

Catatan: Ini tidak berlaku untuk grafis 3D. Jika ingin menggunakan grafis 3D, Anda harus memperluas SurfaceView dan bukan View, dan menggambar dari thread terpisah. Buka contoh GLSurfaceViewActivity untuk mengetahui detailnya.

onMeasure() sedikit lebih terlibat. onMeasure() merupakan bagian penting dari perenderan kontrak antara komponen Anda dan container-nya. onMeasure() harus diganti agar dapat melaporkan pengukuran bagian-bagian yang terdapat di dalamnya secara efisien dan akurat. 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 selesai dihitung. Jika gagal memanggil metode ini dari metode onMeasure() yang telah diganti, hasilnya akan menjadi pengecualian pada waktu pengukuran.

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

  1. Metode onMeasure() yang telah diganti dipanggil dengan spesifikasi ukuran lebar dan tinggi (parameter widthMeasureSpec dan heightMeasureSpec, keduanya merupakan kode bilangan bulat yang mewakili dimensi) yang harus diperlakukan sebagai persyaratan untuk batasan terhadap pengukuran lebar dan tinggi yang harus dihasilkan. Referensi lengkap tentang jenis batasan yang dapat dijadikan persyaratan oleh spesifikasi ini dapat ditemukan di dokumentasi referensi dalam View.onMeasure(int, int) (dokumentasi referensi ini juga menjelaskan keseluruhan operasi pengukuran dengan baik).
  2. Metode onMeasure() komponen harus menghitung lebar dan tinggi pengukuran yang diperlukan untuk merender komponen. Metode ini harus mencoba tetap dalam spesifikasi yang diteruskan, meskipun dapat memilih untuk melebihi spesifikasi tersebut, (dalam hal ini, induk dapat memilih apa yang harus dilakukan, termasuk menyesuaikan, men-scroll, melempar pengecualian, atau meminta onMeasure() untuk mencoba lagi, mungkin dengan spesifikasi pengukuran berbeda).
  3. Setelah lebar dan tinggi dihitung, metode setMeasuredDimension(int width, int height) harus dipanggil dengan pengukuran yang telah dihitung. Jika gagal melakukannya, pengecualian akan dilempar.

Berikut ringkasan beberapa metode standar lainnya yang dipanggil framework pada tampilan:

Kategori Metode Deskripsi
Kreasi Konstruktor Ada formulir konstruktor yang dipanggil saat tampilan dibuat dari kode dan formulir konstruktor yang dipanggil saat tampilan di-inflate dari file tata letak. Formulir kedua harus mengurai dan menerapkan atribut apa pun yang ditetapkan dalam file tata letak.
onFinishInflate() Dipanggil setelah tampilan dan semua turunannya telah di-inflate dari XML.
Tata Letak onMeasure(int, int) Dipanggil guna 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 telah berubah.
Gambar onDraw(Canvas) Dipanggil saat tampilan harus merender kontennya.
Pemrosesan peristiwa onKeyDown(int, KeyEvent) Dipanggil saat peristiwa tombol baru 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 dipasang ke jendela.
onDetachedFromWindow() Dipanggil saat tampilan dilepas dari jendelanya.
onWindowVisibilityChanged(int) Dipanggil saat visibilitas jendela yang berisi tampilan telah berubah.

Kontrol Compound

Jika Anda tidak ingin membuat komponen yang disesuaikan sepenuhnya, tetapi ingin menyatukan komponen yang dapat digunakan kembali yang terdiri atas sekumpulan kontrol yang ada, maka membuat Komponen Compound (atau Kontrol Compound) mungkin cocok untuk Anda. Singkatnya, tindakan ini akan menyatukan sejumlah kontrol (atau tampilan) yang lebih kecil menjadi sekumpulan item logis yang dapat diperlakukan sebagai satu item. Misalnya, Kotak Kombinasi dapat dianggap sebagai kombinasi dari satu baris kolom EditText dan tombol yang berdekatan, dengan PopupList terpasang. Jika menekan tombol tersebut dan memilih sesuatu dari daftar, pilihan Anda akan mengisi kolom EditText, tetapi pengguna juga dapat mengetik sesuatu secara langsung ke kolom EditText jika menginginkannya.

Di Android, sebenarnya ada dua View lain yang tersedia untuk melakukannya: Spinner dan AutoCompleteTextView, tetapi terlepas dari itu, konsep Kotak Kombinasi merupakan contoh yang mudah dipahami.

Untuk membuat komponen compound:

  1. Titik awal yang umum adalah Layout, jadi buat class yang memperluas Layout. Mungkin dalam kasus Kotak kombinasi, kita dapat menggunakan LinearLayout dengan orientasi horizontal. Ingat bahwa tata letak lain dapat disarangkan di dalamnya, sehingga komponen compound dapat menjadi kompleks dan terstruktur secara bebas. Perlu diketahui bahwa seperti Activity, Anda dapat menggunakan pendekatan deklaratif (berbasis XML) untuk membuat komponen yang terdapat di dalamnya, atau menyarangkannya secara terprogram dari kode.
  2. Dalam konstruktor untuk class baru, ambil parameter apa pun yang diharapkan superclass, lalu teruskan parameter tersebut melalui konstruktor superclass terlebih dahulu. Selanjutnya Anda dapat menyiapkan tampilan lain untuk digunakan dalam komponen baru; ini adalah tempat Anda akan membuat kolom EditText dan PopupList. Perlu diketahui bahwa Anda juga dapat memperkenalkan atribut dan parameter sendiri ke XML yang dapat diambil dan digunakan oleh konstruktor.
  3. Anda juga dapat membuat pemroses untuk peristiwa yang dapat dihasilkan oleh tampilan yang ada di dalamnya, misalnya, metode pemroses untuk Pemroses Klik Item Daftar agar memperbarui konten EditText jika pilihan daftar dibuat.
  4. Anda juga dapat membuat properti sendiri dengan pengakses dan pengubah, misalnya, izinkan nilai EditText ditetapkan di awal dalam komponen dan mengajukan kueri untuk kontennya saat diperlukan.
  5. Dalam kasus perluasan Layout, Anda tidak perlu mengganti metode onDraw() dan onMeasure() karena tata letak akan memiliki perilaku default yang kemungkinan akan berfungsi dengan baik. Namun, Anda masih dapat menggantikannya jika perlu.
  6. Anda dapat mengganti metode on... lain, seperti onKeyDown(), untuk memilih nilai default tertentu dari daftar pop-up kotak kombinasi saat tombol tertentu ditekan.

Ringkasnya, penggunaan Layout sebagai dasar untuk Kontrol Kustom memiliki beberapa keuntungan, termasuk:

  • Seperti pada layar aktivitas, Anda dapat menentukan tata letak menggunakan file XML deklaratif, atau membuat tampilan secara terprogram dan menyarangkannya ke tata letak dari kode.
  • Metode onDraw() dan onMeasure() (serta sebagian besar metode on... lainnya) kemungkinan akan memiliki perilaku yang sesuai, sehingga Anda tidak perlu menggantinya.
  • Pada akhirnya, Anda dapat dengan cepat membuat tampilan compound yang kompleks secara bebas dan menggunakannya kembali seolah tampilan tersebut merupakan komponen tunggal.

Mengubah Jenis View yang Ada

Ada opsi yang lebih mudah untuk membuat View kustom, yang bermanfaat dalam situasi tertentu. Jika ada komponen yang sudah sangat mirip dengan yang diinginkan, Anda hanya perlu memperluas komponen tersebut dan mengganti perilaku yang ingin diubah. Anda dapat melakukan semua hal yang biasanya dilakukan dengan komponen yang disesuaikan sepenuhnya, tetapi dengan mulai menggunakan class khusus dalam hierarki View. Anda juga bisa mendapatkan banyak perilaku gratis yang kemungkinan melakukan hal persis seperti yang diinginkan.

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

Impor contoh NotePad ke Android Studio (atau cukup lihat sumber menggunakan link yang tersedia) jika Anda belum melakukannya. Secara khusus, perhatikan definisi LinedEditText dalam file NoteEditor.java.

Berikut beberapa hal yang perlu diketahui dalam file ini:

  1. Definisi

    Class didefinisikan 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 jika diinginkan.
    • Class ini static, artinya tidak menghasilkan "metode sintesis" yang memungkinkannya mengakses data dari class induk. Artinya, class ini benar-benar berperilaku sebagai class terpisah, bukan class yang sangat terkait dengan NoteEditor. Ini adalah cara yang lebih sederhana untuk membuat class dalam jika class tersebut tidak memerlukan akses ke status dari class luar, menjaga agar class yang dihasilkan tetap kecil, dan memungkinkannya digunakan dengan mudah dari class lain.
    • Class ini memperluas EditText, yakni View yang telah dipilih untuk disesuaikan dalam kasus ini. Setelah selesai, class baru akan dapat menggantikan tampilan EditText normal.
  2. Inisialisasi Class

    Seperti biasa, super dipanggil terlebih dahulu. Selain itu, ini bukan konstruktor default, tetapi konstruktor berparameter. EditText dibuat dengan parameter ini saat di-inflate dari file tata letak XML, karena itu, konstruktor juga harus mengambil dan meneruskannya ke konstruktor superclass.

  3. Metode yang Diganti

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

    Untuk contoh ini, penggantian metode onDraw() memungkinkan kita menggambar garis biru di kanvas tampilan EditText (kanvas diteruskan ke metode onDraw() yang diganti). Metode super.onDraw() dipanggil sebelum metode berakhir. Metode superclass harus dipanggil, dan dalam hal ini, kita melakukannya di akhir setelah menggambar garis yang ingin disertakan.

  4. Menggunakan Komponen Kustom

    Sekarang kita memiliki komponen kustom, tetapi bagaimana cara menggunakannya? Dalam contoh NotePad, komponen kustom digunakan secara langsung dari tata letak deklaratif, jadi lihat note_editor.xml dalam 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 di XML, dan class ditetapkan menggunakan paket lengkap. Perlu diketahui juga bahwa class dalam yang ditetapkan direferensikan menggunakan notasi NoteEditor$LinedEditText, yang merupakan cara standar dalam bahasa pemrograman Java untuk mereferensikan class dalam.

      Jika komponen View kustom tidak didefinisikan sebagai class dalam, maka sebagai gantinya, Anda dapat mendeklarasikan komponen View 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 disarangkan 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 melalui konstruktor EditText, sehingga merupakan parameter yang sama yang akan digunakan untuk tampilan EditText. Perlu diketahui bahwa Anda juga dapat menambahkan parameter sendiri. Hal ini akan dibahas lagi di bawah.

Itu saja yang dapat disampaikan. Ini memang kasus yang sederhana, tetapi itulah tujuan utamanya—proses pembuatan komponen kustom memang sederhana.

Komponen yang lebih rumit dapat menggantikan lebih banyak metode on... dan memperkenalkan beberapa metode helper-nya sendiri, yang secara substansial menyesuaikan properti dan perilakunya. Batasannya hanyalah imajinasi Anda dan hal yang menurut Anda harus dilakukan komponen.