Membuat Tampilan Kustom

Codelab ini adalah bagian dari kursus Android Lanjutan di Kotlin. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan, tetapi ini tidak bersifat wajib. Semua codelab kursus tercantum di halaman landing codelab Android Lanjutan di Kotlin.

Pengantar

Android menawarkan banyak subclass View, seperti Button, TextView, EditText, ImageView, CheckBox, atau RadioButton. Anda dapat menggunakan subclass ini untuk membuat UI yang memungkinkan interaksi pengguna dan menampilkan informasi di aplikasi Anda. Jika tidak ada subclass View yang memenuhi kebutuhan Anda, Anda dapat membuat subclass View yang dikenal sebagai tampilan kustom .

Untuk membuat tampilan kustom, Anda dapat memperluas subclass View yang ada (seperti Button atau EditText), atau membuat subclass View Anda sendiri. Dengan memperluas View secara langsung, Anda dapat membuat elemen UI interaktif dengan ukuran dan bentuk apa pun dengan mengganti metode onDraw() untuk View agar dapat menggambarnya.

Setelah membuat tampilan kustom, Anda dapat menambahkannya ke tata letak aktivitas dengan cara yang sama seperti menambahkan TextView atau Button.

Pelajaran ini menunjukkan cara membuat tampilan kustom dari awal dengan memperluas View.

Yang harus sudah Anda ketahui

  • Cara membuat aplikasi dengan Aktivitas dan menjalankannya menggunakan Android Studio.

Yang akan Anda pelajari

  • Cara memperluas View untuk membuat tampilan kustom.
  • Cara menggambar tampilan kustom yang berbentuk lingkaran.
  • Cara menggunakan pemroses untuk menangani interaksi pengguna dengan tampilan kustom.
  • Cara menggunakan tampilan kustom dalam tata letak.

Yang akan Anda lakukan

  • Perluas View untuk membuat tampilan kustom.
  • Lakukan inisialisasi tampilan kustom dengan nilai gambar dan lukisan.
  • Ganti onDraw() untuk menggambar tampilan.
  • Gunakan pemroses untuk menyediakan perilaku tampilan kustom.
  • Tambahkan tampilan kustom ke tata letak.

Aplikasi CustomFanController menunjukkan cara membuat subclass tampilan kustom dengan memperluas class View. Subclass baru ini disebut DialView.

Aplikasi menampilkan elemen UI melingkar yang menyerupai kontrol kipas fisik, dengan setelan untuk mati (0), rendah (1), sedang (2), dan tinggi (3). Saat pengguna mengetuk tampilan, indikator pilihan akan berpindah ke posisi berikutnya: 0-1-2-3, dan kembali ke 0. Selain itu, jika pilihan adalah 1 atau lebih tinggi, warna latar belakang bagian melingkar tampilan akan berubah dari abu-abu menjadi hijau (menunjukkan bahwa daya kipas aktif).

Tampilan adalah elemen penyusun dasar UI aplikasi. Class View menyediakan banyak subclass, yang disebut sebagai widget UI, yang mencakup banyak kebutuhan antarmuka pengguna aplikasi Android biasa.

Komponen penyusun UI seperti Button dan TextView adalah subclass yang memperluas class View. Untuk menghemat waktu dan upaya pengembangan, Anda dapat memperluas salah satu subkelas View ini. Tampilan kustom mewarisi tampilan dan perilaku induknya, dan Anda dapat mengganti perilaku atau aspek tampilan yang ingin diubah. Misalnya, jika Anda memperluas EditText untuk membuat tampilan kustom, tampilan akan bertindak seperti tampilan EditText, tetapi juga dapat disesuaikan untuk menampilkan, misalnya, tombol X yang menghapus teks dari kolom entri teks.

Anda dapat memperluas subclass View, seperti EditText, untuk mendapatkan tampilan kustom—pilih yang paling mendekati apa yang ingin Anda capai. Anda kemudian dapat menggunakan tampilan kustom seperti subclass View lainnya dalam satu atau beberapa tata letak sebagai elemen XML dengan atribut.

Untuk membuat tampilan kustom Anda sendiri dari awal, perluas class View itu sendiri. Kode Anda menggantikan metode View untuk menentukan tampilan dan fungsi tampilan. Kunci untuk membuat tampilan kustom Anda sendiri adalah Anda bertanggung jawab untuk menggambar seluruh elemen UI dengan ukuran dan bentuk apa pun ke layar. Jika Anda membuat subclass tampilan yang ada seperti Button, class tersebut akan menangani gambar untuk Anda. (Anda akan mempelajari lebih lanjut cara menggambar nanti dalam codelab ini.)

Untuk membuat tampilan kustom, ikuti langkah-langkah umum berikut:

  • Buat class tampilan kustom yang memperluas View, atau memperluas subclass View (seperti Button atau EditText).
  • Jika Anda memperluas subclass View yang ada, ganti hanya perilaku atau aspek tampilan yang ingin Anda ubah.
  • Jika Anda memperluas class View, gambar bentuk tampilan kustom dan kontrol penampilannya dengan mengganti metode View seperti onDraw() dan onMeasure() di class baru.
  • Tambahkan kode untuk merespons interaksi pengguna dan, jika perlu, menggambar ulang tampilan kustom.
  • Gunakan class tampilan kustom sebagai widget UI dalam tata letak XML aktivitas Anda. Anda juga dapat menentukan atribut kustom untuk tampilan, guna memberikan penyesuaian untuk tampilan dalam tata letak yang berbeda.

Dalam tugas ini, Anda akan:

  • Buat aplikasi dengan ImageView sebagai placeholder sementara untuk tampilan kustom.
  • Perluas View untuk membuat tampilan kustom.
  • Lakukan inisialisasi tampilan kustom dengan nilai gambar dan lukisan.

Langkah 1: Buat aplikasi dengan placeholder ImageView

  1. Buat aplikasi Kotlin dengan judul CustomFanController menggunakan template Empty Activity. Pastikan nama paketnya adalah com.example.android.customfancontroller.
  2. Buka activity_main.xml di tab Text untuk mengedit kode XML.
  3. Ganti TextView yang ada dengan kode ini. Teks ini berfungsi sebagai label dalam aktivitas untuk tampilan kustom.
<TextView
       android:id="@+id/customViewLabel"
       android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:padding="16dp"
       android:textColor="@android:color/black"
       android:layout_marginStart="8dp"
       android:layout_marginEnd="8dp"
       android:layout_marginTop="24dp"
       android:text="Fan Control"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"/>
  1. Tambahkan elemen ImageView ini ke tata letak. Ini adalah placeholder untuk tampilan kustom yang akan Anda buat dalam codelab ini.
<ImageView
       android:id="@+id/dialView"
       android:layout_width="200dp"
       android:layout_height="200dp"
       android:background="@android:color/darker_gray"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="8dp"
       android:layout_marginRight="8dp"
       android:layout_marginTop="8dp"/>
  1. Ekstrak resource string dan dimensi di kedua elemen UI.
  2. Klik tab Desain. Tata letaknya akan terlihat seperti ini:

Langkah 2. Buat class tampilan kustom Anda

  1. Buat class Kotlin baru bernama DialView.
  2. Ubah definisi class untuk memperluas View. Impor android.view.View bila diminta.
  3. Klik View, lalu klik bohlam merah. Pilih Tambahkan konstruktor Android View menggunakan '@JvmOverloads'. Android Studio menambahkan konstruktor dari class View. Anotasi @JvmOverloads menginstruksikan compiler Kotlin untuk membuat kelebihan beban untuk fungsi ini yang menggantikan nilai parameter default.
class DialView @JvmOverloads constructor(
   context: Context,
   attrs: AttributeSet? = null,
   defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
  1. Di atas definisi class DialView, tepat di bawah impor, tambahkan enum tingkat teratas untuk merepresentasikan kecepatan kipas yang tersedia. Perhatikan bahwa enum ini berjenis Int karena nilainya adalah resource string, bukan string sebenarnya. Android Studio akan menampilkan error untuk resource string yang tidak ada di setiap nilai ini; Anda akan memperbaikinya di langkah selanjutnya.
private enum class FanSpeed(val label: Int) {
   OFF(R.string.fan_off),
   LOW(R.string.fan_low),
   MEDIUM(R.string.fan_medium),
   HIGH(R.string.fan_high);
}
  1. Di bawah enum, tambahkan konstanta ini. Anda akan menggunakannya sebagai bagian dari menggambar indikator dan label dial.
private const val RADIUS_OFFSET_LABEL = 30      
private const val RADIUS_OFFSET_INDICATOR = -35
  1. Di dalam class DialView, tentukan beberapa variabel yang Anda butuhkan untuk menggambar tampilan kustom. Impor android.graphics.PointF bila diminta.
private var radius = 0.0f                   // Radius of the circle.
private var fanSpeed = FanSpeed.OFF         // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)
  • radius adalah radius lingkaran saat ini. Nilai ini ditetapkan saat tampilan digambar di layar.
  • fanSpeed adalah kecepatan kipas saat ini, yang merupakan salah satu nilai dalam enumerasi FanSpeed. Secara default, nilainya adalah OFF.
  • Terakhir, postPosition adalah titik X,Y yang akan digunakan untuk menggambar beberapa elemen tampilan di layar.

Nilai ini dibuat dan diinisialisasi di sini, bukan saat tampilan benar-benar digambar, untuk memastikan langkah penggambaran yang sebenarnya berjalan secepat mungkin.

  1. Juga di dalam definisi class DialView, inisialisasi objek Paint dengan beberapa gaya dasar. Impor android.graphics.Paint dan android.graphics.Typeface bila diminta. Seperti sebelumnya dengan variabel, gaya ini diinisialisasi di sini untuk membantu mempercepat langkah penggambaran.
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
   style = Paint.Style.FILL
   textAlign = Paint.Align.CENTER
   textSize = 55.0f
   typeface = Typeface.create( "", Typeface.BOLD)
}
  1. Buka res/values/strings.xml dan tambahkan resource string untuk kecepatan kipas:
<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string>

Setelah membuat tampilan kustom, Anda harus dapat menggambarnya. Saat Anda memperluas subclass View seperti EditText, subclass tersebut akan menentukan tampilan dan atribut tampilan serta menggambarnya di layar. Oleh karena itu, Anda tidak perlu menulis kode untuk menggambar tampilan. Anda dapat mengganti metode induk untuk menyesuaikan tampilan.

Jika Anda membuat tampilan sendiri dari awal (dengan memperluas View), Anda bertanggung jawab untuk menggambar seluruh tampilan setiap kali layar dimuat ulang, dan untuk mengganti metode View yang menangani gambar. Agar dapat menggambar tampilan kustom yang memperluas View dengan benar, Anda harus:

  • Hitung ukuran tampilan saat pertama kali muncul, dan setiap kali ukuran tampilan tersebut berubah, dengan mengganti metode onSizeChanged().
  • Ganti metode onDraw() untuk menggambar tampilan kustom, menggunakan objek Canvas yang diberi gaya oleh objek Paint.
  • Panggil metode invalidate() saat merespons klik pengguna yang mengubah cara tampilan digambar untuk membatalkan validasi seluruh tampilan, sehingga memaksa panggilan ke onDraw() untuk menggambar ulang tampilan.

Metode onDraw() dipanggil setiap kali layar di-refresh, yang dapat terjadi berkali-kali dalam satu detik. Untuk alasan performa dan menghindari gangguan visual, Anda harus melakukan tindakan seminimal mungkin di onDraw(). Secara khusus, jangan menempatkan alokasi di onDraw(), karena alokasi dapat menyebabkan pembersihan sampah memori yang dapat menyebabkan operasi tersendat secara visual.

Class Canvas dan Paint menawarkan sejumlah pintasan gambar yang berguna:

Anda akan mempelajari Canvas dan Paint lebih lanjut dalam codelab berikutnya. Untuk mempelajari lebih lanjut cara Android menggambar tampilan, lihat Cara Android Menggambar Tampilan.

Dalam tugas ini, Anda akan menggambar tampilan kustom pengontrol kipas ke layar—tombol itu sendiri, indikator posisi saat ini, dan label indikator—dengan metode onSizeChanged() dan onDraw(). Anda juga akan membuat metode helper, computeXYForSpeed(), untuk menghitung posisi X,Y saat ini dari label indikator pada dial.

Langkah 1. Menghitung posisi dan menggambar tampilan

  1. Di class DialView, di bawah inisialisasi, ganti metode onSizeChanged() dari class View untuk menghitung ukuran dial tampilan kustom. Impor kotlin.math.min bila diminta.

    Metode onSizeChanged() dipanggil setiap kali ukuran tampilan berubah, termasuk saat pertama kali digambar ketika tata letak di-inflate. Ganti onSizeChanged() untuk menghitung posisi, dimensi, dan nilai lain apa pun yang terkait dengan ukuran tampilan kustom Anda, alih-alih menghitungnya ulang setiap kali Anda menggambar. Dalam hal ini, Anda menggunakan onSizeChanged() untuk menghitung radius elemen lingkaran dial saat ini.
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
   radius = (min(width, height) / 2.0 * 0.8).toFloat()
}
  1. Di bawah onSizeChanged(), tambahkan kode ini untuk menentukan fungsi ekstensi computeXYForSpeed() untuk class PointF . Impor kotlin.math.cos dan kotlin.math.sin bila diminta. Fungsi ekstensi pada class PointF ini menghitung koordinat X, Y di layar untuk label teks dan indikator saat ini (0, 1, 2, atau 3), mengingat posisi FanSpeed saat ini dan radius dial. Anda akan menggunakannya di onDraw().
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
   // Angles are in radians.
   val startAngle = Math.PI * (9 / 8.0)   
   val angle = startAngle + pos.ordinal * (Math.PI / 4)
   x = (radius * cos(angle)).toFloat() + width / 2
   y = (radius * sin(angle)).toFloat() + height / 2
}
  1. Ganti metode onDraw() untuk merender tampilan di layar dengan class Canvas dan Paint. Impor android.graphics.Canvas bila diminta. Berikut adalah penggantian kerangka:
override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
   
}
  1. Di dalam onDraw(), tambahkan baris ini untuk menyetel warna cat ke abu-abu (Color.GRAY) atau hijau (Color.GREEN) bergantung pada apakah kecepatan kipas OFF atau nilai lainnya. Impor android.graphics.Color bila diminta.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
  1. Tambahkan kode ini untuk menggambar lingkaran untuk dial, dengan metode drawCircle(). Metode ini menggunakan lebar dan tinggi tampilan saat ini untuk menemukan pusat lingkaran, radius lingkaran, dan warna cat saat ini. Properti width dan height adalah anggota superclass View dan menunjukkan dimensi tampilan saat ini.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
  1. Tambahkan kode berikut untuk menggambar lingkaran yang lebih kecil untuk tanda indikator kecepatan kipas, juga dengan metode drawCircle(). Bagian ini menggunakan PointF.Metode ekstensi computeXYforSpeed() untuk menghitung koordinat X,Y untuk pusat indikator berdasarkan kecepatan kipas saat ini.
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
  1. Terakhir, gambar label kecepatan kipas (0, 1, 2, 3) di posisi yang sesuai di sekitar kenop. Bagian metode ini memanggil PointF.computeXYForSpeed() lagi untuk mendapatkan posisi setiap label, dan menggunakan kembali objek pointPosition setiap kali untuk menghindari alokasi. Gunakan drawText() untuk menggambar label.
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
   pointPosition.computeXYForSpeed(i, labelRadius)
   val label = resources.getString(i.label)
   canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}

Metode onDraw() yang sudah selesai akan terlihat seperti ini:

override fun onDraw(canvas: Canvas) {
   super.onDraw(canvas)
   // Set dial background color to green if selection not off.
   paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
   // Draw the dial.
   canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
   // Draw the indicator circle.
   val markerRadius = radius + RADIUS_OFFSET_INDICATOR
   pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
   paint.color = Color.BLACK
   canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
   // Draw the text labels.
   val labelRadius = radius + RADIUS_OFFSET_LABEL
   for (i in FanSpeed.values()) {
       pointPosition.computeXYForSpeed(i, labelRadius)
       val label = resources.getString(i.label)
       canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
   }
}

Langkah 2. Tambahkan tampilan ke tata letak

Untuk menambahkan tampilan kustom ke UI aplikasi, Anda harus menentukannya sebagai elemen dalam tata letak XML aktivitas. Kontrol tampilan dan perilakunya dengan atribut elemen XML, seperti yang Anda lakukan untuk elemen UI lainnya.

  1. Di activity_main.xml, ubah tag ImageView untuk dialView menjadi com.example.android.customfancontroller.DialView, dan hapus atribut android:background. Baik DialView maupun ImageView asli mewarisi atribut standar dari class View, sehingga tidak perlu mengubah atribut lainnya. Elemen DialView baru terlihat seperti ini:
<com.example.android.customfancontroller.DialView
       android:id="@+id/dialView"
       android:layout_width="@dimen/fan_dimen"
       android:layout_height="@dimen/fan_dimen"
       app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       android:layout_marginLeft="@dimen/default_margin"
       android:layout_marginRight="@dimen/default_margin"
       android:layout_marginTop="@dimen/default_margin" />
  1. Jalankan aplikasi. Tampilan kontrol kipas Anda akan muncul di aktivitas.

Tugas terakhir adalah mengaktifkan tampilan kustom Anda untuk melakukan tindakan saat pengguna mengetuk tampilan. Setiap ketukan akan memindahkan indikator pilihan ke posisi berikutnya: nonaktif-1-2-3 dan kembali ke nonaktif. Selain itu, jika pilihan adalah 1 atau lebih tinggi, ubah latar belakang dari abu-abu menjadi hijau, yang menunjukkan bahwa daya kipas menyala.

Agar tampilan kustom dapat diklik, Anda:

  • Tetapkan properti isClickable tampilan ke true. Hal ini memungkinkan tampilan kustom Anda merespons klik.
  • Terapkan performClick() class View untuk melakukan operasi saat tampilan diklik.
  • Panggil metode invalidate(). Hal ini memberi tahu sistem Android untuk memanggil metode onDraw() guna menggambar ulang tampilan.

Biasanya, dengan tampilan Android standar, Anda menerapkan OnClickListener() untuk melakukan tindakan saat pengguna mengklik tampilan tersebut. Untuk tampilan kustom, Anda mengimplementasikan metode performClick() class View, lalu memanggil super.performClick(). Metode performClick() default juga memanggil onClickListener(), sehingga Anda dapat menambahkan tindakan ke performClick() dan membiarkan onClickListener() tersedia untuk penyesuaian lebih lanjut oleh Anda atau developer lain yang mungkin menggunakan tampilan kustom Anda.

  1. Di DialView.kt, di dalam enumerasi FanSpeed, tambahkan fungsi ekstensi next() yang mengubah kecepatan kipas saat ini ke kecepatan berikutnya dalam daftar (dari OFF ke LOW, MEDIUM, dan HIGH, lalu kembali ke OFF). Enumerasi lengkapnya sekarang terlihat seperti ini:
private enum class FanSpeed(val label: Int) {
   OFF(R.string.fan_off),
   LOW(R.string.fan_low),
   MEDIUM(R.string.fan_medium),
   HIGH(R.string.fan_high);

   fun next() = when (this) {
       OFF -> LOW
       LOW -> MEDIUM
       MEDIUM -> HIGH
       HIGH -> OFF
   }
}
  1. Di dalam class DialView, tepat sebelum metode onSizeChanged(), tambahkan blok init(). Menetapkan properti isClickable tampilan ke benar (true) memungkinkan tampilan tersebut menerima input pengguna.
init {
   isClickable = true
}
  1. Di bawah init(),, ganti metode performClick() dengan kode di bawah ini.
override fun performClick(): Boolean {
   if (super.performClick()) return true

   fanSpeed = fanSpeed.next()
   contentDescription = resources.getString(fanSpeed.label)
  
   invalidate()
   return true
}

Panggilan ke super.performClick() harus terjadi terlebih dahulu, yang mengaktifkan peristiwa aksesibilitas serta memanggil onClickListener().

Dua baris berikutnya menaikkan kecepatan kipas dengan metode next(), dan menyetel deskripsi konten tampilan ke resource string yang merepresentasikan kecepatan saat ini (mati, 1, 2, atau 3).

Terakhir, metode invalidate() membatalkan seluruh tampilan, sehingga memanggil onDraw() untuk menggambar ulang tampilan. Jika ada sesuatu di tampilan kustom Anda yang berubah karena alasan apa pun, termasuk interaksi pengguna, dan perubahan tersebut perlu ditampilkan, panggil invalidate().

  1. Jalankan aplikasi. Ketuk elemen DialView untuk memindahkan indikator dari nonaktif ke 1. Tombol akan berubah menjadi hijau. Dengan setiap ketukan, indikator harus berpindah ke posisi berikutnya. Saat indikator kembali ke nonaktif, kenop harus berubah menjadi abu-abu lagi.

Contoh ini menunjukkan mekanisme dasar penggunaan atribut kustom dengan tampilan kustom Anda. Anda menentukan atribut kustom untuk class DialView dengan warna yang berbeda untuk setiap posisi kenop kipas.

  1. Buat dan buka res/values/attrs.xml.
  2. Di dalam <resources>, tambahkan elemen resource <declare-styleable>.
  3. Di dalam elemen resource <declare-styleable>, tambahkan tiga elemen attr, satu untuk setiap atribut, dengan name dan format. format seperti jenis, dan dalam hal ini, adalah color.
<?xml version="1.0" encoding="utf-8"?>
<resources>
       <declare-styleable name="DialView">
           <attr name="fanColor1" format="color" />
           <attr name="fanColor2" format="color" />
           <attr name="fanColor3" format="color" />
       </declare-styleable>
</resources>
  1. Buka file tata letak activity_main.xml.
  2. Di DialView, tambahkan atribut untuk fanColor1, fanColor2, dan fanColor3, lalu tetapkan nilainya ke warna yang ditampilkan di bawah. Gunakan app: sebagai awalan untuk atribut kustom (seperti pada app:fanColor1), bukan android: karena atribut kustom Anda termasuk dalam namespace schemas.android.com/apk/res/your_app_package_name, bukan namespace android.
app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"

Untuk menggunakan atribut di class DialView, Anda perlu mengambilnya. AttributeSet tersebut disimpan dalam AttributeSet, yang diberikan ke kelas Anda saat dibuat, jika ada. Anda mengambil atribut di init, dan menetapkan nilai atribut ke variabel lokal untuk penyimpanan dalam cache.

  1. Buka file class DialView.kt.
  2. Di dalam DialView, deklarasikan variabel untuk menyimpan nilai atribut dalam cache.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0
  1. Di blok init, tambahkan kode berikut menggunakan fungsi ekstensi withStyledAttributes. Anda menyediakan atribut dan tampilan, serta menetapkan variabel lokal. Mengimpor withStyledAttributes juga akan mengimpor fungsi getColor() yang tepat.
context.withStyledAttributes(attrs, R.styleable.DialView) {
   fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
   fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
   fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}
  1. Gunakan variabel lokal di onDraw() untuk menyetel warna tombol berdasarkan kecepatan kipas saat ini. Ganti baris tempat warna cat ditetapkan (paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN) dengan kode di bawah.
paint.color = when (fanSpeed) {
   FanSpeed.OFF -> Color.GRAY
   FanSpeed.LOW -> fanSpeedLowColor
   FanSpeed.MEDIUM -> fanSpeedMediumColor
   FanSpeed.HIGH -> fanSeedMaxColor
} as Int
  1. Jalankan aplikasi Anda, klik tombol, dan setelan warna akan berbeda untuk setiap posisi, seperti yang ditunjukkan di bawah.

Untuk mempelajari lebih lanjut atribut tampilan kustom, lihat Membuat Class Tampilan.

Aksesibilitas adalah serangkaian teknik desain, penerapan, dan pengujian yang memungkinkan aplikasi Anda dapat digunakan oleh semua orang, termasuk penyandang disabilitas.

Disabilitas umum yang dapat memengaruhi penggunaan perangkat Android oleh seseorang mencakup kebutaan, gangguan penglihatan, buta warna, tuli atau gangguan pendengaran, serta keterampilan motorik terbatas. Saat mengembangkan aplikasi dengan mempertimbangkan aksesibilitas, Anda akan membuat pengalaman pengguna menjadi lebih baik, tidak hanya bagi pengguna dengan disabilitas tersebut, tetapi juga bagi semua pengguna lainnya.

Android menyediakan beberapa fitur aksesibilitas secara default di tampilan UI standar seperti TextView dan Button. Namun, saat membuat tampilan kustom, Anda perlu mempertimbangkan cara tampilan kustom tersebut akan menyediakan fitur aksesibilitas seperti deskripsi lisan konten di layar.

Dalam tugas ini, Anda akan mempelajari TalkBack, pembaca layar Android, dan mengubah aplikasi untuk menyertakan petunjuk dan deskripsi yang dapat diucapkan untuk tampilan kustom DialView.

Langkah 1. Menjelajahi TalkBack

TalkBack adalah pembaca layar bawaan Android. Dengan TalkBack diaktifkan, pengguna dapat berinteraksi dengan perangkat Android mereka tanpa melihat layar, karena Android mendeskripsikan elemen layar secara lisan. Pengguna dengan gangguan penglihatan dapat mengandalkan TalkBack untuk menggunakan aplikasi Anda.

Dalam tugas ini, Anda mengaktifkan TalkBack untuk memahami cara kerja pembaca layar dan cara menavigasi aplikasi.

  1. Di perangkat atau emulator Android, buka Setelan > Aksesibilitas > TalkBack.
  2. Ketuk tombol Aktif/Nonaktif untuk mengaktifkan TalkBack.
  3. Ketuk Oke untuk mengonfirmasi izin.
  4. Konfirmasi sandi perangkat Anda, jika diminta. Jika ini adalah pertama kalinya Anda menjalankan TalkBack, tutorial akan diluncurkan. (Tutorial mungkin tidak tersedia di perangkat lama.)
  5. Sebaiknya buka tutorial dengan mata tertutup. Untuk membuka kembali tutorial pada lain waktu, buka Setelan > Aksesibilitas > TalkBack > Setelan > Luncurkan tutorial TalkBack.
  6. Kompilasi dan jalankan aplikasi CustomFanController, atau buka dengan tombol Ringkasan atau Terbaru di perangkat Anda. Dengan TalkBack aktif, perhatikan bahwa nama aplikasi diucapkan, serta teks label TextView ("Fan Control"). Namun, jika Anda mengetuk tampilan DialView itu sendiri, tidak ada informasi yang diucapkan tentang status tampilan (setelan saat ini untuk tombol) atau tindakan yang akan terjadi saat Anda mengetuk tampilan untuk mengaktifkannya.

Langkah 2. Menambahkan deskripsi konten untuk label dial

Deskripsi konten menjelaskan arti dan tujuan tampilan di aplikasi Anda. Label ini memungkinkan pembaca layar seperti fitur TalkBack Android menjelaskan fungsi setiap elemen secara akurat. Untuk tampilan statis seperti ImageView, Anda dapat menambahkan deskripsi konten ke tampilan dalam file tata letak dengan atribut contentDescription. Tampilan teks (TextView dan EditText) otomatis menggunakan teks dalam tampilan sebagai deskripsi konten.

Untuk tampilan kontrol kipas kustom, Anda perlu memperbarui deskripsi konten secara dinamis setiap kali tampilan diklik, untuk menunjukkan setelan kipas saat ini.

  1. Di bagian bawah class DialView, deklarasikan fungsi updateContentDescription() tanpa argumen atau jenis nilai yang ditampilkan.
fun updateContentDescription() {
}
  1. Di dalam updateContentDescription(), ubah properti contentDescription untuk tampilan kustom ke resource string yang terkait dengan kecepatan kipas saat ini (mati, 1, 2, atau 3). Label ini sama dengan yang digunakan di onDraw() saat tombol diputar di layar.
fun updateContentDescription() {
   contentDescription = resources.getString(fanSpeed.label)
}
  1. Scroll ke atas ke blok init(), dan di akhir blok tersebut, tambahkan panggilan ke updateContentDescription(). Tindakan ini menginisialisasi deskripsi konten saat tampilan diinisialisasi.
init {
   isClickable = true
   // ...

   updateContentDescription()
}
  1. Tambahkan panggilan lain ke updateContentDescription() dalam metode performClick(), tepat sebelum invalidate().
override fun performClick(): Boolean {
   if (super.performClick()) return true
   fanSpeed = fanSpeed.next()
   updateContentDescription()
   invalidate()
   return true
}
  1. Kompilasi dan jalankan aplikasi, lalu pastikan TalkBack diaktifkan. Ketuk untuk mengubah setelan tampilan dial dan perhatikan bahwa kini TalkBack mengumumkan label saat ini (nonaktif, 1, 2, 3) serta frasa "Ketuk dua kali untuk mengaktifkan".

Langkah 3. Menambahkan informasi selengkapnya untuk tindakan klik

Anda bisa berhenti di sini dan tampilan Anda akan dapat digunakan di TalkBack. Namun, akan lebih baik jika tampilan Anda tidak hanya menunjukkan bahwa tampilan dapat diaktifkan ("Ketuk dua kali untuk mengaktifkan"), tetapi juga menjelaskan apa yang akan terjadi saat tampilan diaktifkan ("Ketuk dua kali untuk mengubah" atau "Ketuk dua kali untuk mereset").

Untuk melakukannya, Anda menambahkan informasi tentang tindakan tampilan (di sini, tindakan klik atau ketuk) ke objek info node aksesibilitas, melalui delegasi aksesibilitas. Delegasi aksesibilitas memungkinkan Anda menyesuaikan fitur terkait aksesibilitas aplikasi melalui komposisi (bukan pewarisan).

Untuk tugas ini, Anda akan menggunakan class aksesibilitas di library Android Jetpack (androidx.*), untuk memastikan kompatibilitas mundur.

  1. Di DialView.kt, dalam blok init, tetapkan delegasi aksesibilitas pada tampilan sebagai objek AccessibilityDelegateCompat baru. Impor androidx.core.view.ViewCompat dan androidx.core.view.AccessibilityDelegateCompat bila diminta. Strategi ini memungkinkan kompatibilitas mundur terbesar dalam aplikasi Anda.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   
})
  1. Di dalam objek AccessibilityDelegateCompat, ganti fungsi onInitializeAccessibilityNodeInfo() dengan objek AccessibilityNodeInfoCompat, dan panggil metode super. Impor androidx.core.view.accessibility.AccessibilityNodeInfoCompat bila diminta.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)

   }  
})

Setiap tampilan memiliki hierarki node aksesibilitas, yang mungkin atau mungkin tidak sesuai dengan komponen tata letak sebenarnya dari tampilan. Layanan aksesibilitas Android menavigasi node tersebut untuk mengetahui informasi tentang tampilan (seperti deskripsi konten yang dapat diucapkan, atau kemungkinan tindakan yang dapat dilakukan pada tampilan tersebut). Saat membuat tampilan kustom, Anda mungkin juga perlu mengganti informasi node untuk memberikan informasi kustom terkait aksesibilitas. Dalam hal ini, Anda akan mengganti info node untuk menunjukkan bahwa ada informasi kustom untuk tindakan tampilan.

  1. Di dalam onInitializeAccessibilityNodeInfo(), buat objek AccessibilityNodeInfoCompat.AccessibilityActionCompat baru, dan tetapkan ke variabel customClick. Teruskan konstanta AccessibilityNodeInfo.ACTION_CLICK dan string placeholder ke konstruktor. Impor AccessibilityNodeInfo bila diminta.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)
      val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
         AccessibilityNodeInfo.ACTION_CLICK,
        "placeholder"
      )
   }  
})

Class AccessibilityActionCompat mewakili tindakan pada tampilan untuk tujuan aksesibilitas. Tindakan umum adalah klik atau ketuk, seperti yang Anda gunakan di sini, tetapi tindakan lain dapat mencakup mendapatkan atau kehilangan fokus, operasi papan klip (potong/salin/tempel) atau men-scroll dalam tampilan. Konstruktor untuk class ini memerlukan konstanta tindakan (di sini, AccessibilityNodeInfo.ACTION_CLICK), dan string yang digunakan oleh TalkBack untuk menunjukkan tindakan yang dilakukan.

  1. Ganti string "placeholder" dengan panggilan ke context.getString() untuk mengambil resource string. Untuk resource tertentu, uji kecepatan kipas saat ini. Jika kecepatan saat ini adalah FanSpeed.HIGH, stringnya adalah "Reset". Jika kecepatan kipas adalah yang lain, stringnya adalah "Change." Anda akan membuat resource string ini di langkah berikutnya.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
      super.onInitializeAccessibilityNodeInfo(host, info)
      val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
         AccessibilityNodeInfo.ACTION_CLICK,
        context.getString(if (fanSpeed !=  FanSpeed.HIGH) R.string.change else R.string.reset)
      )
   }  
})
  1. Setelah tanda kurung tutup untuk definisi customClick, gunakan metode addAction() untuk menambahkan tindakan aksesibilitas baru ke objek info node.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
   override fun onInitializeAccessibilityNodeInfo(host: View, 
                            info: AccessibilityNodeInfoCompat) {
       super.onInitializeAccessibilityNodeInfo(host, info)
       val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
           AccessibilityNodeInfo.ACTION_CLICK,
           context.getString(if (fanSpeed !=  FanSpeed.HIGH) 
                                 R.string.change else R.string.reset)
       )
       info.addAction(customClick)
   }
})
  1. Di res/values/strings.xml, tambahkan resource string untuk "Ubah" dan "Reset".
<string name="change">Change</string>
<string name="reset">Reset</string>
  1. Kompilasi dan jalankan aplikasi, lalu pastikan TalkBack diaktifkan. Perhatikan bahwa frasa "Ketuk dua kali untuk mengaktifkan" kini menjadi "Ketuk dua kali untuk mengubah" (jika kecepatan kipas kurang dari tinggi atau 3) atau "Ketuk dua kali untuk mereset" (jika kecepatan kipas sudah tinggi atau 3). Perhatikan bahwa perintah "Ketuk dua kali untuk..." disediakan oleh layanan TalkBack itu sendiri.

Download kode untuk codelab yang sudah selesai.

$  git clone https://github.com/googlecodelabs/android-kotlin-drawing-custom-views


Atau, Anda dapat mendownload repositori sebagai file ZIP, mengekstraknya, dan membukanya di Android Studio.

Download Zip

  • Untuk membuat tampilan kustom yang mewarisi tampilan dan perilaku subclass View seperti EditText, tambahkan class baru yang memperluas subclass tersebut, dan lakukan penyesuaian dengan mengganti beberapa metode subclass.
  • Untuk membuat tampilan kustom dengan ukuran dan bentuk apa pun, tambahkan class baru yang merupakan lanjutan dari View.
  • Ganti metode View seperti onDraw() untuk menentukan bentuk dan tampilan dasar tampilan.
  • Gunakan invalidate() untuk memaksa menggambar atau menggambar ulang tampilan.
  • Untuk mengoptimalkan performa, alokasikan variabel dan tetapkan nilai yang diperlukan untuk menggambar dan melukis sebelum menggunakannya di onDraw(), seperti dalam inisialisasi variabel anggota.
  • Ganti performClick(), bukan OnClickListener(), ke tampilan kustom untuk memberikan perilaku interaktif tampilan. Hal ini memungkinkan Anda atau developer Android lain yang mungkin menggunakan class tampilan kustom Anda untuk menggunakan onClickListener() guna memberikan perilaku lebih lanjut.
  • Tambahkan tampilan kustom ke file tata letak XML dengan atribut untuk menentukan tampilannya, seperti yang Anda lakukan pada elemen UI lainnya.
  • Buat file attrs.xml di folder values untuk menentukan atribut kustom. Kemudian, Anda dapat menggunakan atribut kustom untuk tampilan kustom dalam file tata letak XML.

Kursus Udacity:

Dokumentasi developer Android:

Video:

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Instruktur menentukan hal berikut:

  • Memberikan pekerjaan rumah jika diperlukan.
  • Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
  • Memberi nilai tugas pekerjaan rumah.

Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.

Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Pertanyaan 1

Untuk menghitung posisi, dimensi, dan nilai lain saat tampilan kustom pertama kali diberi ukuran, metode mana yang Anda ganti?

onMeasure()

onSizeChanged()

invalidate()

onDraw()

Pertanyaan 2

Untuk menunjukkan bahwa Anda ingin tampilan digambar ulang dengan onDraw(), metode mana yang Anda panggil dari thread UI, setelah nilai atribut berubah?

▢ onMeasure()

▢ onSizeChanged()

▢ invalidate()

▢ getVisibility()

Pertanyaan 3

Metode View mana yang harus Anda ganti untuk menambahkan interaktivitas ke tampilan kustom?

▢ setOnClickListener()

▢ onSizeChanged()

▢ isClickable()

▢ performClick()

Untuk mengetahui link ke codelab lain dalam kursus ini, lihat halaman landing codelab Android Lanjutan di Kotlin.