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
Viewuntuk 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
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 subclassView(sepertiButtonatauEditText). - Jika Anda memperluas subclass
Viewyang 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 metodeViewsepertionDraw()danonMeasure()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
ImageViewsebagai placeholder sementara untuk tampilan kustom. - Perluas
Viewuntuk membuat tampilan kustom. - Lakukan inisialisasi tampilan kustom dengan nilai gambar dan lukisan.
Langkah 1: Buat aplikasi dengan placeholder ImageView
- Buat aplikasi Kotlin dengan judul
CustomFanControllermenggunakan template Empty Activity. Pastikan nama paketnya adalahcom.example.android.customfancontroller. - Buka
activity_main.xmldi tab Text untuk mengedit kode XML. - Ganti
TextViewyang 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"/>- Tambahkan elemen
ImageViewini 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"/>- Ekstrak resource string dan dimensi di kedua elemen UI.
- Klik tab Desain. Tata letaknya akan terlihat seperti ini:

Langkah 2. Buat class tampilan kustom Anda
- Buat class Kotlin baru bernama
DialView. - Ubah definisi class untuk memperluas
View. Imporandroid.view.Viewbila diminta. - Klik
View, lalu klik bohlam merah. Pilih Tambahkan konstruktor Android View menggunakan '@JvmOverloads'. Android Studio menambahkan konstruktor dari classView. Anotasi@JvmOverloadsmenginstruksikan 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) {- Di atas definisi class
DialView, tepat di bawah impor, tambahkanenumtingkat teratas untuk merepresentasikan kecepatan kipas yang tersedia. Perhatikan bahwaenumini berjenisIntkarena 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);
}- 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- Di dalam class
DialView, tentukan beberapa variabel yang Anda butuhkan untuk menggambar tampilan kustom. Imporandroid.graphics.PointFbila 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)radiusadalah radius lingkaran saat ini. Nilai ini ditetapkan saat tampilan digambar di layar.fanSpeedadalah kecepatan kipas saat ini, yang merupakan salah satu nilai dalam enumerasiFanSpeed. Secara default, nilainya adalahOFF.- Terakhir,
postPositionadalah 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.
- Juga di dalam definisi class
DialView, inisialisasi objekPaintdengan beberapa gaya dasar. Imporandroid.graphics.Paintdanandroid.graphics.Typefacebila 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)
}- Buka
res/values/strings.xmldan 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 objekCanvasyang diberi gaya oleh objekPaint. - Panggil metode
invalidate()saat merespons klik pengguna yang mengubah cara tampilan digambar untuk membatalkan validasi seluruh tampilan, sehingga memaksa panggilan keonDraw()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:
- Gambar teks menggunakan
drawText(). Tentukan jenis huruf dengan memanggilsetTypeface(), dan warna teks dengan memanggilsetColor(). - Gambar bentuk-bentuk dasar menggunakan
drawRect(),drawOval(), dandrawArc(). Ubah apakah bentuk akan diisi, diberi garis batas, atau keduanya dengan memanggilsetStyle(). - Menggambar bitmap menggunakan
drawBitmap().
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
- Di class
DialView, di bawah inisialisasi, ganti metodeonSizeChanged()dari classViewuntuk menghitung ukuran dial tampilan kustom. Imporkotlin.math.minbila diminta.
MetodeonSizeChanged()dipanggil setiap kali ukuran tampilan berubah, termasuk saat pertama kali digambar ketika tata letak di-inflate. GantionSizeChanged()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 menggunakanonSizeChanged()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()
}- Di bawah
onSizeChanged(), tambahkan kode ini untuk menentukan fungsi ekstensicomputeXYForSpeed()untuk classPointF. Imporkotlin.math.cosdankotlin.math.sinbila diminta. Fungsi ekstensi pada classPointFini menghitung koordinat X, Y di layar untuk label teks dan indikator saat ini (0, 1, 2, atau 3), mengingat posisiFanSpeedsaat ini dan radius dial. Anda akan menggunakannya dionDraw().
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
}- Ganti metode
onDraw()untuk merender tampilan di layar dengan classCanvasdanPaint. Imporandroid.graphics.Canvasbila diminta. Berikut adalah penggantian kerangka:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}- Di dalam
onDraw(), tambahkan baris ini untuk menyetel warna cat ke abu-abu (Color.GRAY) atau hijau (Color.GREEN) bergantung pada apakah kecepatan kipasOFFatau nilai lainnya. Imporandroid.graphics.Colorbila diminta.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN- 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. Propertiwidthdanheightadalah anggota superclassViewdan menunjukkan dimensi tampilan saat ini.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)- Tambahkan kode berikut untuk menggambar lingkaran yang lebih kecil untuk tanda indikator kecepatan kipas, juga dengan metode
drawCircle(). Bagian ini menggunakanPointF.Metode ekstensicomputeXYforSpeed()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)- 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 objekpointPositionsetiap kali untuk menghindari alokasi. GunakandrawText()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.
- Di
activity_main.xml, ubah tagImageViewuntukdialViewmenjadicom.example.android.customfancontroller.DialView, dan hapus atributandroid:background. BaikDialViewmaupunImageViewasli mewarisi atribut standar dari classView, sehingga tidak perlu mengubah atribut lainnya. ElemenDialViewbaru 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" />- 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
isClickabletampilan ketrue. Hal ini memungkinkan tampilan kustom Anda merespons klik. - Terapkan
performClick()classViewuntuk melakukan operasi saat tampilan diklik. - Panggil metode
invalidate(). Hal ini memberi tahu sistem Android untuk memanggil metodeonDraw()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.
- Di
DialView.kt, di dalam enumerasiFanSpeed, tambahkan fungsi ekstensinext()yang mengubah kecepatan kipas saat ini ke kecepatan berikutnya dalam daftar (dariOFFkeLOW,MEDIUM, danHIGH, lalu kembali keOFF). 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
}
}- Di dalam class
DialView, tepat sebelum metodeonSizeChanged(), tambahkan blokinit(). Menetapkan propertiisClickabletampilan ke benar (true) memungkinkan tampilan tersebut menerima input pengguna.
init {
isClickable = true
}- Di bawah
init(),, ganti metodeperformClick()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().
- Jalankan aplikasi. Ketuk elemen
DialViewuntuk 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.
- Buat dan buka
res/values/attrs.xml. - Di dalam
<resources>, tambahkan elemen resource<declare-styleable>. - Di dalam elemen resource
<declare-styleable>, tambahkan tiga elemenattr, satu untuk setiap atribut, dengannamedanformat.formatseperti jenis, dan dalam hal ini, adalahcolor.
<?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>- Buka file tata letak
activity_main.xml. - Di
DialView, tambahkan atribut untukfanColor1,fanColor2, danfanColor3, lalu tetapkan nilainya ke warna yang ditampilkan di bawah. Gunakanapp:sebagai awalan untuk atribut kustom (seperti padaapp:fanColor1), bukanandroid:karena atribut kustom Anda termasuk dalam namespaceschemas.android.com/apk/res/your_app_package_name, bukan namespaceandroid.
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.
- Buka file class
DialView.kt. - Di dalam
DialView, deklarasikan variabel untuk menyimpan nilai atribut dalam cache.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0- Di blok
init, tambahkan kode berikut menggunakan fungsi ekstensiwithStyledAttributes. Anda menyediakan atribut dan tampilan, serta menetapkan variabel lokal. MengimporwithStyledAttributesjuga akan mengimpor fungsigetColor()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)
}- 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.GRAYelseColor.GREEN) dengan kode di bawah.
paint.color = when (fanSpeed) {
FanSpeed.OFF -> Color.GRAY
FanSpeed.LOW -> fanSpeedLowColor
FanSpeed.MEDIUM -> fanSpeedMediumColor
FanSpeed.HIGH -> fanSeedMaxColor
} as Int- 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.
- Di perangkat atau emulator Android, buka Setelan > Aksesibilitas > TalkBack.
- Ketuk tombol Aktif/Nonaktif untuk mengaktifkan TalkBack.
- Ketuk Oke untuk mengonfirmasi izin.
- Konfirmasi sandi perangkat Anda, jika diminta. Jika ini adalah pertama kalinya Anda menjalankan TalkBack, tutorial akan diluncurkan. (Tutorial mungkin tidak tersedia di perangkat lama.)
- Sebaiknya buka tutorial dengan mata tertutup. Untuk membuka kembali tutorial pada lain waktu, buka Setelan > Aksesibilitas > TalkBack > Setelan > Luncurkan tutorial TalkBack.
- 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 labelTextView("Fan Control"). Namun, jika Anda mengetuk tampilanDialViewitu 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.
- Di bagian bawah class
DialView, deklarasikan fungsiupdateContentDescription()tanpa argumen atau jenis nilai yang ditampilkan.
fun updateContentDescription() {
}- Di dalam
updateContentDescription(), ubah properticontentDescriptionuntuk tampilan kustom ke resource string yang terkait dengan kecepatan kipas saat ini (mati, 1, 2, atau 3). Label ini sama dengan yang digunakan dionDraw()saat tombol diputar di layar.
fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}- Scroll ke atas ke blok
init(), dan di akhir blok tersebut, tambahkan panggilan keupdateContentDescription(). Tindakan ini menginisialisasi deskripsi konten saat tampilan diinisialisasi.
init {
isClickable = true
// ...
updateContentDescription()
}- Tambahkan panggilan lain ke
updateContentDescription()dalam metodeperformClick(), tepat sebeluminvalidate().
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}- 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.
- Di
DialView.kt, dalam blokinit, tetapkan delegasi aksesibilitas pada tampilan sebagai objekAccessibilityDelegateCompatbaru. Imporandroidx.core.view.ViewCompatdanandroidx.core.view.AccessibilityDelegateCompatbila diminta. Strategi ini memungkinkan kompatibilitas mundur terbesar dalam aplikasi Anda.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
})- Di dalam objek
AccessibilityDelegateCompat, ganti fungsionInitializeAccessibilityNodeInfo()dengan objekAccessibilityNodeInfoCompat, dan panggil metode super. Imporandroidx.core.view.accessibility.AccessibilityNodeInfoCompatbila 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.
- Di dalam
onInitializeAccessibilityNodeInfo(), buat objekAccessibilityNodeInfoCompat.AccessibilityActionCompatbaru, dan tetapkan ke variabelcustomClick. Teruskan konstantaAccessibilityNodeInfo.ACTION_CLICKdan string placeholder ke konstruktor. ImporAccessibilityNodeInfobila 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.
- Ganti string
"placeholder"dengan panggilan kecontext.getString()untuk mengambil resource string. Untuk resource tertentu, uji kecepatan kipas saat ini. Jika kecepatan saat ini adalahFanSpeed.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)
)
}
})- Setelah tanda kurung tutup untuk definisi
customClick, gunakan metodeaddAction()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)
}
})- Di
res/values/strings.xml, tambahkan resource string untuk "Ubah" dan "Reset".
<string name="change">Change</string>
<string name="reset">Reset</string>- 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.
- Untuk membuat tampilan kustom yang mewarisi tampilan dan perilaku subclass
ViewsepertiEditText, 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
ViewsepertionDraw()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(), bukanOnClickListener(), ke tampilan kustom untuk memberikan perilaku interaktif tampilan. Hal ini memungkinkan Anda atau developer Android lain yang mungkin menggunakan class tampilan kustom Anda untuk menggunakanonClickListener()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.xmldi foldervaluesuntuk menentukan atribut kustom. Kemudian, Anda dapat menggunakan atribut kustom untuk tampilan kustom dalam file tata letak XML.
Kursus Udacity:
Dokumentasi developer Android:
- Membuat Tampilan Kustom
@JvmOverloads- Komponen Kustom
- Cara Android Menggambar Tampilan
onMeasure()onSizeChanged()onDraw()CanvasPaintdrawText()setTypeface()setColor()drawRect()drawOval()drawArc()drawBitmap()setStyle()invalidate()- View
- Peristiwa Input
- Paint
- Library ekstensi Kotlin android-ktx
withStyledAttributes- Dokumentasi Android KTX
- Blog pengumuman asli Android KTX
- Membuat tampilan kustom lebih mudah diakses
AccessibilityDelegateCompatAccessibilityNodeInfoCompatAccessibilityNodeInfoCompat.AccessibilityActionCompat
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.



