dispatchDraw(): apa itu, mekanisme panggilan dan perbedaan dari onDraw

Penulis: IT Sectr Diterbitkan: 2026-07-22 Waktu membaca: 9 mnt

dispatchDraw() — adalah metode kelas ViewGroup yang bertanggung jawab untuk menggambar secara rekursif semua View anak dalam hierarki Android. Menurut Android Developers Documentation (2026), dispatchDraw dipanggil secara otomatis setelah onDraw dalam metode draw() dari View utama dan menelusuri semua elemen anak, memanggil draw() mereka sendiri. Pengembang menimpa dispatchDraw di ViewGroup kustom untuk menambahkan efek, melapiskan grafik di atas anak, atau mengubah urutan penggambaran.

Utama

  • dispatchDraw(Canvas canvas) — metode protected ViewGroup dipanggil setelah onDraw untuk menggambar View anak
  • draw(Canvas canvas) — metode public final View yang secara berurutan memanggil onDraw, dispatchDraw, dan onDrawForeground
  • onDraw menggambar konten View itu sendiri, dispatchDraw — konten semua elemen anak
  • setWillNotDraw(true) — optimasi untuk ViewGroup yang tidak menggambar konten sendiri, hanya elemen anak
  • Penimpaan dispatchDraw digunakan untuk menerapkan efek (blur, overlay) atau animasi penggambaran elemen anak

Apa itu dispatchDraw()?

dispatchDraw(Canvas canvas) — adalah metode protected dari kelas ViewGroup yang dipanggil oleh sistem Android untuk menggambar semua View anak dari kontainer saat ini. dispatchDraw adalah bagian dari pipeline draw standar: pertama onDraw dieksekusi (menggambar konten View itu sendiri), kemudian dispatchDraw (penggambaran rekursif anak), kemudian onDrawForeground (menggambar foreground dan scrollbars). Metode ini tidak dimaksudkan untuk dipanggil langsung dari kode aplikasi.

Implementasi standar dispatchDraw di ViewGroup mengiterasi semua View anak, memeriksa visibilitasnya, dan memanggil draw(Canvas) untuk masing-masing. Urutan penelusuran sesuai dengan indeks anak (dari 0 hingga childCount - 1). Jika View anak saling tumpang tindih, yang terakhir dalam daftar digambar di atas yang pertama. Dengan mengubah urutan di dispatchDraw, Z-order penggambaran dapat diubah.

Untuk View (bukan ViewGroup) metode dispatchDraw kosong — View biasa tidak memiliki elemen anak, jadi tidak ada yang perlu digambar. Metode ini ada di kelas dasar View, tetapi hanya dipanggil untuk instance ViewGroup. Pengembang dapat memeriksa apakah View memiliki elemen anak melalui perilaku default dispatchDraw, tetapi seringkali lebih sederhana untuk memeriksa instanceof ViewGroup.

Pipeline penggambaran: onDraw, dispatchDraw, onDrawForeground

Metode draw(Canvas) dari kelas View mengatur pipeline penggambaran tiga tahap. Tahap pertama — pemanggilan onDraw(canvas), di mana View menggambar kontennya. Tahap kedua — pemanggilan dispatchDraw(canvas), yang memulai penggambaran View anak. Tahap ketiga — onDrawForeground(canvas), bertanggung jawab untuk lapisan foreground, scrollbars, dan efek ripple. Urutan ini menjamin tumpukan lapisan grafis yang benar.

onDraw selalu dieksekusi sebelum dispatchDraw. Ini berarti konten View induk ditampilkan di bawah View anak. Jika perlu menggambar sesuatu di atas anak, ini dilakukan di onDrawForeground atau di dispatchDraw yang ditimpa dengan pemanggilan super.dispatchDraw(canvas) dan penggambaran selanjutnya di atasnya. Latar belakang (background) digambar bahkan sebelum onDraw — di drawBackground(canvas) di dalam metode draw().

Pada percepatan perangkat keras (hardware acceleration) pipeline draw bekerja melalui GPU. View digambar dalam DisplayList — daftar perintah gambar yang di-cache dan digunakan kembali. dispatchDraw dalam mode percepatan perangkat keras menambahkan DisplayList View anak ke DisplayList umum adegan. Mengubah dispatchDraw dapat menyebabkan invalidasi DisplayList dan pembangunan ulang cache, yang memengaruhi kinerja.

MetodeUrutan panggilanTujuan
drawBackground1Menggambar latar belakang (background drawable)
onDraw2Menggambar konten View itu sendiri
dispatchDraw3Menggambar semua View anak (hanya untuk ViewGroup)
onDrawForeground4Menggambar foreground, scrollbar, dan efek ripple

Perbedaan dispatchDraw dan onDraw

onDraw bertanggung jawab untuk menggambar konten View itu sendiri — bentuk, teks, gambar yang menjadi milik elemen spesifik ini. dispatchDraw bertanggung jawab untuk menggambar View anak — semua elemen yang berada di dalam ViewGroup. Jika ViewGroup tidak menimpa dispatchDraw, implementasi dari ViewGroup digunakan, yang hanya secara rekursif menelusuri childCount dan memanggil draw untuk setiap anak.

Untuk ViewGroup yang tidak menggambar konten sendiri (misalnya, FrameLayout, LinearLayout), onDraw dapat dioptimalkan dengan mengatur setWillNotDraw(true). Dalam kasus ini, onDraw tidak pernah dipanggil, yang menghemat sumber daya. dispatchDraw tetap berfungsi dan memanggil draw untuk elemen anak. Semua ViewGroup standar (LinearLayout, RelativeLayout, ConstraintLayout) menggunakan setWillNotDraw(true).

Jika dispatchDraw ditimpa tanpa memanggil super.dispatchDraw(canvas), View anak tidak akan digambar. Ini dapat berguna untuk menyembunyikan sementara semua anak, tetapi dalam banyak kasus menyebabkan kesalahan. Praktik yang direkomendasikan: panggil super.dispatchDraw(canvas) di awal metode yang ditimpa, lalu tambahkan grafik Anda sendiri (misalnya, overlay dengan saluran alfa) di atas anak.

Contoh penimpaan dispatchDraw di Kotlin

Mari buat OverlayViewGroup — ViewGroup kustom yang menambahkan lapisan semi-transparan dengan label teks di atas semua View anak. dispatchDraw pertama memanggil super (menggambar semua anak), kemudian menggambar persegi panjang overlay dan teks. Paint dibuat di konstruktor untuk menghindari alokasi dalam loop draw.

kotlin
class OverlayViewGroup(context: Context)
    : ViewGroup(context) {

    private val overlayPaint = Paint().apply {
        color = Color.parseColor("#66000000")
        style = Paint.Style.FILL
    }

    private val textPaint = Paint().apply {
        color = Color.WHITE
        textSize = 36f
        isAntiAlias = true
        textAlign = Paint.Align.CENTER
    }

    init {
        setWillNotDraw(false)
    }

    override fun dispatchDraw(canvas: Canvas) {
        super.dispatchDraw(canvas)
        canvas.drawRect(0f, 0f,
                        width.toFloat(),
                        height.toFloat(), overlayPaint)
        canvas.drawText("MODE PRATINJAU",
            width / 2f,
            height / 2f, textPaint)
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var top = t
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            val ch = child.measuredHeight
            child.layout(l, top, l + cw, top + ch)
            top += ch
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        val maxWidth = resolveSize(
            getChildAt(0).measuredWidth,
            widthMeasureSpec)
        var totalHeight = 0
        for (i in 0 until childCount) {
            totalHeight += getChildAt(i).measuredHeight
        }
        setMeasuredDimension(maxWidth,
            resolveSize(totalHeight, heightMeasureSpec))
    }
}

Z-order animasi melalui dispatchDraw

Contoh berikut mendemonstrasikan perubahan urutan penggambaran elemen anak. CircularRevealLayout menimpa dispatchDraw dan menggambar anak dalam urutan terbalik, menciptakan efek Z-order terbalik. Untuk animasi, pergeseran siklik dari indeks penggambaran ditambahkan berdasarkan nilai offset yang dianimasikan.

kotlin
class ReverseOrderLayout(context: Context)
    : ViewGroup(context) {

    private var reverse = false

    override fun dispatchDraw(canvas: Canvas) {
        if (!reverse) {
            super.dispatchDraw(canvas)
            return
        }
        for (i in childCount - 1 downTo 0) {
            val child = getChildAt(i)
            if (child.visibility == GONE) continue
            drawChild(canvas, child, getDrawingTime())
        }
    }

    fun toggleReverse() {
        reverse = !reverse
        invalidate()
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var left = l
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            child.layout(left, t,
                left + cw, t + child.measuredHeight)
            left += cw
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        var totalWidth = 0
        for (i in 0 until childCount) {
            totalWidth += getChildAt(i).measuredWidth
        }
        setMeasuredDimension(
            resolveSize(totalWidth, widthMeasureSpec),
            resolveSize(getChildAt(0).measuredHeight,
                heightMeasureSpec))
    }
}

Kapan menggunakan dispatchDraw di Android

dispatchDraw digunakan dalam skenario di mana Anda perlu memengaruhi proses penggambaran View anak tanpa memengaruhi konten ViewGroup itu sendiri. Skenario utama: menerapkan efek bersama (overlay, shadow, gradient) di atas semua anak; mengubah urutan Z-order untuk menciptakan efek kedalaman; menganimasikan kemunculan atau penghilangan elemen anak melalui transformasi Canvas.

Jika diperlukan pelapisan grafik di bawah elemen anak (efek latar belakang), gunakan onDraw — dispatchDraw dipanggil setelahnya. Jika grafik harus di atas anak — gunakan dispatchDraw dengan panggilan super, lalu Canvas.draw. Jika diperlukan perubahan warna atau transparansi global, lebih mudah menggunakan canvas.saveLayerAlpha() di dispatchDraw yang membungkus penggambaran anak.

Tidak direkomendasikan menggunakan dispatchDraw untuk: 1) menggambar animasi kompleks secara real-time (gunakan invalidate dan onDraw); 2) membuat screenshot ViewGroup (gunakan buildDrawingCache() atau View.draw(Canvas)); 3) memodifikasi View anak (koordinat, ukuran — ini tugas onLayout, bukan dispatchDraw). dispatchDraw — hanya untuk menerapkan efek visual.

Pertanyaan yang Sering Diajukan

Bisakah dispatchDraw dipanggil langsung tanpa sistem?

Tidak, dispatchDraw — metode protected yang dipanggil oleh sistem Android di dalam metode public draw(). Pemanggilan langsung dispatchDraw tidak berguna, karena tidak melakukan tindakan persiapan (menyimpan Canvas, menggambar latar belakang dan foreground). Sebagai gantinya, gunakan View.draw(Canvas) untuk penggambaran terprogram ke Canvas sembarang.

Apa yang terjadi saat memanggil super.dispatchDraw setelah penggambaran kustom?

Jika Anda memanggil super.dispatchDraw(canvas) setelah operasi penggambaran Anda sendiri, View anak akan digambar di atas grafik kustom. Ini mengubah tumpukan lapisan: pertama lapisan kustom digambar, kemudian anak. Jika situasi sebaliknya diperlukan (grafik di atas anak), panggil super.dispatchDraw terlebih dahulu, lalu operasi Anda sendiri.

Bagaimana dispatchDraw memengaruhi kinerja?

dispatchDraw memanggil draw() untuk setiap View anak, waktu total sebanding dengan jumlah anak. Pada hardware acceleration, penambahan operasi Canvas di dispatchDraw dapat menyebabkan invalidasi DisplayList dan pembangunan ulang cache. Untuk 5–10 anak dampaknya minimal, untuk 50+ anak disarankan untuk men-cache overlay yang kompleks.

Mengapa dispatchDraw tidak dipanggil untuk View?

View (bukan ViewGroup) tidak memiliki elemen anak, oleh karena itu dispatchDraw tidak melakukan pekerjaan yang berguna. Namun, metode ini ada di kelas dasar View untuk polimorfisme: kode di draw() memanggil dispatchDraw untuk View apa pun, tetapi implementasi View tidak berisi logika penggambaran anak. Hanya ViewGroup yang menimpa dispatchDraw.

Bisakah Canvas diubah di dispatchDraw?

Ya, dispatchDraw menerima Canvas dan dapat ditransformasi sebelum memanggil super.dispatchDraw (translate, rotate, scale). Ini diterapkan untuk menganimasikan seluruh set anak sebagai satu kesatuan. Setelah super.dispatchDraw selesai, disarankan untuk mengembalikan Canvas ke keadaan semula.

Ringkasan

  • dispatchDraw(Canvas) — metode ViewGroup untuk menggambar secara rekursif semua View anak setelah onDraw
  • Pipeline draw: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw menggambar konten View itu sendiri, dispatchDraw — konten elemen anak
  • setWillNotDraw(true) mengoptimalkan ViewGroup, melewatkan onDraw, tetapi dispatchDraw terus berfungsi
  • Penimpaan dispatchDraw digunakan untuk menerapkan efek, mengubah Z-order, dan transformasi Canvas anak
  • Urutan super.dispatchDraw dan penggambaran kustom menentukan apa yang akan berada di atas anak
  • Untuk View biasa (bukan ViewGroup) dispatchDraw kosong — tidak memiliki elemen anak

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.

Diskusikan proyek

Baca juga