onDraw() — adalah metode kunci dari kelas View di Android SDK, yang bertanggung jawab untuk merender konten tampilan. Sistem memanggil onDraw setiap kali tampilan View diperbarui — saat rendering awal, setelah invalidate() atau saat ukuran berubah. Menurut Android Developers Documentation (2026), override onDraw menggunakan Canvas API adalah cara utama untuk membuat grafis kustom di aplikasi Android.
Poin utama
onDraw(Canvas canvas) — adalah metode protected dari kelas android.view.View, yang dipanggil oleh sistem Android ketika View perlu di-render. Pengembang meng-override metode ini untuk mengimplementasikan rendering kustom: bentuk, teks, gambar, gradien, animasi. Parameter Canvas adalah permukaan grafis di mana semua operasi menggambar dilakukan.
Sidik jari metode ini sederhana: override fun onDraw(canvas: Canvas). Di dalam metode ini tidak boleh membuat objek baru (untuk menghindari beban GC), melakukan operasi panjang atau bekerja dengan jaringan. View juga berisi metode onDraw dalam siklus hidupnya antara onLayout (perhitungan posisi) dan dispatchDraw (rendering elemen anak).
Saat mewarisi dari View, override onDraw wajib dilakukan untuk menampilkan konten apa pun — jika tidak, View akan kosong. Saat mewarisi dari subkelas yang ada (TextView, ImageView, Button), override onDraw bersifat opsional dan digunakan untuk menambahkan grafis tambahan di atas konten standar.
Android menjalankan rendering View dalam urutan yang ditentukan secara ketat, dipanggil dari thread utama (UI thread). Siklus lengkap terdiri dari tiga fase: measure (pengukuran ukuran), layout (perhitungan posisi) dan draw (rendering). onDraw adalah bagian dari fase draw dan dipanggil setelah ukuran dan posisi View ditentukan.
draw(Canvas canvas) — adalah metode publik dari View yang memanggil onDraw dan dispatchDraw. Metode draw tidak dimaksudkan untuk di-override — sebagai gantinya, onDraw di-override. Setelah eksekusi onDraw untuk View saat ini, dispatchDraw dipanggil, yang secara rekursif memanggil draw untuk semua View anak (jika View saat ini adalah container ViewGroup).
onDraw dipanggil dalam kondisi berikut: 1) View menjadi terlihat untuk pertama kalinya (setelah setVisibility(VISIBLE)); 2) aplikasi memanggil invalidate() untuk View ini atau induknya; 3) ukuran View berubah (layout baru); 4) animasi yang terkait dengan View berubah (melalui ValueAnimator atau ObjectAnimator); 5) status View berubah (pressed, focused, enabled).
Mulai Android 3.0 (API 11), sebagian besar operasi Canvas dijalankan dengan percepatan hardware melalui GPU. Pengembang dapat memeriksa apakah percepatan diaktifkan melalui canvas.isHardwareAccelerated(). Dengan percepatan hardware, beberapa operasi Canvas tidak didukung (misalnya, clipPath dengan area non-persegi panjang) dan dapat diabaikan atau menyebabkan kesalahan.
Canvas menyediakan kumpulan metode yang kaya untuk menggambar. Semua metode bekerja dalam sistem koordinat saat ini, yang dapat diubah melalui transformasi (translate, rotate, scale, skew). Setiap metode menerima objek Paint yang menentukan warna, gaya, ketebalan garis, efek, dan anti-aliasing.
| Metode Canvas | Tujuan | Parameter kunci Paint |
|---|---|---|
| drawCircle | Menggambar lingkaran | centerX, centerY, radius, paint |
| drawRect | Menggambar persegi panjang | left, top, right, bottom, paint |
| drawLine | Menggambar garis | startX, startY, endX, endY, paint |
| drawPath | Menggambar kontur sembarang | path, paint |
| drawBitmap | Menggambar gambar | bitmap, srcRect, dstRect, paint |
| drawText | Menggambar teks | text, x, y, paint |
| drawArc | Menggambar busur atau sektor | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Menggambar oval | ovalRect, paint |
Paint menentukan gaya rendering: warna (setColor), ketebalan garis (setStrokeWidth), gaya isian (setStyle — FILL, STROKE, FILL_AND_STROKE), anti-aliasing (setAntiAlias), efek (setShadowLayer, setMaskFilter) dan gradien (setShader). Disarankan untuk membuat dan mengonfigurasi Paint satu kali di konstruktor View, bukan di dalam onDraw, untuk menghindari pembuatan objek dalam siklus rendering.
Canvas menggunakan sistem koordinat di mana sumbu X mengarah ke kanan dan sumbu Y ke bawah. Titik asal (0, 0) adalah sudut kiri atas View. Sebelum memanggil metode draw, pengembang dapat mengubah sistem koordinat melalui canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Semua operasi menggambar selanjutnya akan dijalankan dalam sistem yang telah ditransformasi.
onDraw dijalankan di thread UI utama, dan durasinya secara langsung memengaruhi frame rate (fps). Untuk mempertahankan 60 fps, metode onDraw harus selesai dalam waktu tidak lebih dari 16 milidetik. Perlambatan apa pun menyebabkan frame terlewat (jank), yang secara visual terlihat sebagai animasi tersendat atau keterlambatan saat scrolling.
Aturan utama optimasi onDraw meliputi: 1) jangan membuat objek di dalam onDraw — semua Paint, Path, Rect harus menjadi bidang kelas; 2) jangan mengalokasikan memori (new) — ini memicu garbage collection (GC) dan frame terlewat; 3) cache operasi kompleks di Bitmap melalui Bitmap.createBitmap dan gambar Bitmap yang sudah siap; 4) jangan melakukan operasi input-output atau memuat sumber daya.
clipRect membatasi area menggambar ke persegi panjang yang ditentukan. Digunakan agar Canvas tidak membuang waktu merender piksel di luar area yang terlihat. Sangat efektif saat scrolling daftar besar atau scaling: atur clipRect ke area yang terlihat (dirty rect), dan Canvas hanya akan menggambar apa yang terlihat.
canvas.save() menyimpan status Canvas saat ini (transformasi dan clip) ke dalam stack. canvas.restore() memulihkan status terakhir yang disimpan. Ini diperlukan untuk transformasi sementara: simpan status, terapkan translate/rotate, gambar, lalu pulihkan. save dan restore ringan dan tidak memengaruhi kinerja jika digunakan secara wajar (kurang dari 10–15 level nesting).
invalidate() — adalah metode View yang memberi tahu sistem bahwa konten View sudah usang dan perlu digambar ulang. Pemanggilan invalidate memulai siklus draw baru di mana onDraw akan dipanggil. invalidate hanya boleh dipanggil dari thread UI (main thread). Untuk pemanggilan dari thread latar belakang, digunakan postInvalidate.
postInvalidate() — versi invalidate yang thread-safe, yang mengirim permintaan penggambaran ulang ke antrian thread utama melalui Handler. Digunakan dalam tugas latar belakang ketika View perlu diperbarui setelah selesai memuat data atau melakukan perhitungan. Dikombinasikan dengan runOnUiThread untuk sinkronisasi dengan UI.
Untuk penggambaran ulang sebagian digunakan invalidate(Rect dirty) atau invalidate(int l, int t, int r, int b). Ini memberi tahu sistem bahwa hanya area yang ditentukan yang berubah, bukan seluruh View. Android mengoptimalkan rendering dengan menggambar ulang hanya dirty rect, yang secara signifikan meningkatkan kinerja pada animasi dengan pembaruan parsial.
Mari kita lihat contoh praktis override onDraw untuk membuat View kustom di Android dengan Kotlin.
Contoh membuat indikator progres melingkar dengan pengisian animasi. onDraw menggambar dua lingkaran: latar belakang abu-abu dan isian berwarna, yang busurnya ditentukan oleh progres saat ini. Pemanggilan invalidate di setProgress memastikan penggambaran ulang saat nilai berubah.
class CircularProgressView
@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val bgPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.parseColor("#E0E0E0")
isAntiAlias = true
}
private val progressPaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 12f
color = Color.BLUE
isAntiAlias = true
strokeCap = Paint.Cap.ROUND
}
var progress: Float = 0f
set(value) {
field = value.coerceIn(0f, 1f)
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val size = minOf(width, height) - 24f
val oval = RectF(12f, 12f,
12f + size, 12f + size)
canvas.drawArc(oval, 270f, 360f, false, bgPaint)
canvas.drawArc(oval, 270f,
progress * 360f,
false, progressPaint)
}
}
Contoh menggambar persegi panjang dengan gradien dan teks di atasnya. Gradien dibuat melalui LinearGradient (Shader), dan teks melalui drawText. Kedua Paint dibuat satu kali di konstruktor, dan di onDraw hanya ukuran persegi panjang yang berubah tergantung pada lebar View saat ini.
class GradientTextView(context: Context)
: View(context) {
private val shaderPaint = Paint().apply {
isAntiAlias = true
}
private val textPaint = Paint().apply {
color = Color.WHITE
textSize = 48f
isAntiAlias = true
textAlign = Paint.Align.CENTER
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val gradient = LinearGradient(
0f, 0f, width.toFloat(), 0f,
Color.parseColor("#FF6B35"),
Color.parseColor("#FF0040"),
Shader.TileMode.CLAMP
)
shaderPaint.shader = gradient
canvas.drawRect(0f, 0f,
width.toFloat(),
height.toFloat(), shaderPaint)
val xCenter = width / 2f
val yCenter = height / 2f
val yOffset = (textPaint.descent() +
textPaint.ascent()) / 2f
canvas.drawText("Halo dari Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Contoh membuat jam animasi yang memperbarui rendering setiap 1000 milidetik. postDelayed di onDraw memanggil invalidate setelah satu detik, menciptakan siklus penggambaran ulang tak terbatas. Jarum jam digambar melalui drawLine dengan panjang dan ketebalan yang berbeda.
class AnalogClockView(context: Context)
: View(context) {
private val handPaint = Paint().apply {
style = Paint.Style.STROKE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 20f
val now = Calendar.getInstance()
val hours = now.get(Calendar.HOUR) % 12
val minutes = now.get(Calendar.MINUTE)
val seconds = now.get(Calendar.SECOND)
val secondAngle = Math.toRadians(
seconds * 6f - 90f)
handPaint.strokeWidth = 2f
handPaint.color = Color.RED
canvas.drawLine(cx, cy,
cx + cos(secondAngle) * radius * 0.8f,
cy + sin(secondAngle) * radius * 0.8f,
handPaint)
postDelayed({ invalidate() }, 1000)
}
}
Pertanyaan yang sering diajukan
Tidak, onDraw tidak boleh dipanggil secara langsung. Untuk meminta penggambaran ulang, gunakan invalidate() (dari thread UI) atau postInvalidate() (dari thread latar belakang). Sistem sendiri yang memutuskan kapan akan memanggil onDraw dan mengoptimalkan rendering dengan menggabungkan beberapa invalidate menjadi satu langkah.
Penyebab: View tersembunyi (setVisibility(GONE) atau INVISIBLE); View belum ditambahkan ke hierarki Window; ukuran View adalah 0 (width atau height = 0); setWillNotDraw(true) dipanggil (default true untuk ViewGroup). Periksa masing-masing poin ini saat debugging.
Pada 60 fps, onDraw dapat dipanggil hingga 60 kali per detik. Pada 120 fps (di perangkat dengan layar 120 Hz) — hingga 120 kali. Setiap pemanggilan harus selesai dalam ~8–16 milidetik. Pada animasi aktif melalui ObjectAnimator atau ValueAnimator, frekuensi ditentukan oleh jumlah frame animasi.
onDraw bertanggung jawab untuk merender konten View itu sendiri. dispatchDraw bertanggung jawab untuk merender View anak (di ViewGroup). dispatchDraw dipanggil setelah onDraw dan secara rekursif melintasi semua elemen anak. Untuk View biasa (bukan ViewGroup), dispatchDraw kosong.
Ya, Canvas dapat dibuat untuk Bitmap melalui Canvas(bitmap) dan digambar di thread mana pun. Ini digunakan untuk pra-komputasi grafis kompleks di latar belakang. Namun, Canvas yang diteruskan ke onDraw adalah milik sistem dan hanya dapat digunakan di thread UI di dalam metode onDraw.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga