dispatchDraw(): ano ito, mekanismo ng pagtawag at pagkakaiba sa onDraw

May-akda: IT Sectr Nai-publish: 2026-07-22 Oras ng pagbabasa: 9 min

dispatchDraw() — ay isang paraan ng klase ng ViewGroup na responsable para sa recursive na pagguhit ng lahat ng child View sa hierarchy ng Android. Ayon sa Android Developers Documentation (2026), ang dispatchDraw ay awtomatikong tinatawag pagkatapos ng onDraw sa draw() na paraan ng pangunahing View at dumadaan sa lahat ng child element, na tinatawag ang kanilang sariling draw(). Ang mga developer ay nag-o-override ng dispatchDraw sa custom na ViewGroup para magdagdag ng mga epekto, magpatong ng graphics sa ibabaw ng mga bata, o baguhin ang pagkakasunod-sunod ng pagguhit.

Pangunahin

  • dispatchDraw(Canvas canvas) — protected na paraan ng ViewGroup na tinatawag pagkatapos ng onDraw para sa pagguhit ng mga child View
  • draw(Canvas canvas) — public final na paraan ng View na sunod-sunod na tumatawag ng onDraw, dispatchDraw at onDrawForeground
  • onDraw ay gumuhit ng nilalaman ng View mismo, dispatchDraw — nilalaman ng lahat ng child element
  • setWillNotDraw(true) — optimisasyon para sa ViewGroup na hindi gumuhit ng sariling nilalaman, mga child element lamang
  • Ang pag-o-override ng dispatchDraw ay ginagamit para sa paglalapat ng mga epekto (blur, overlay) o animasyon ng pagguhit ng mga child element

Ano ang dispatchDraw()?

dispatchDraw(Canvas canvas) — ay isang protected na paraan ng klase ng ViewGroup na tinatawag ng Android system para sa pagguhit ng lahat ng child View ng kasalukuyang container. Ang dispatchDraw ay bahagi ng standard na pipeline ng draw: una ay isinasagawa ang onDraw (pagguhit ng nilalaman ng View mismo), pagkatapos ay ang dispatchDraw (recursive na pagguhit ng mga bata), pagkatapos ay ang onDrawForeground (pagguhit ng foreground at scrollbars). Ang paraan ay hindi para sa direktang pagtawag mula sa code ng application.

Ang standard na implementasyon ng dispatchDraw sa ViewGroup ay umiikot sa lahat ng child View, sinusuri ang kanilang visibility at tinatawag para sa bawat isa ang draw(Canvas). Ang pagkakasunod-sunod ng pagdaan ay tumutugma sa mga index ng mga bata (mula 0 hanggang childCount - 1). Kung ang mga child View ay nagkakapatong, ang mga huli sa listahan ay iginuguhit sa ibabaw ng mga nauna. Sa pamamagitan ng pagbabago ng pagkakasunod-sunod sa dispatchDraw, maaaring baguhin ang Z-order ng pagguhit.

Para sa View (hindi ViewGroup) ang dispatchDraw na paraan ay walang laman — ang ordinaryong View ay walang child element, kaya walang dapat iguhit. Ang paraan ay umiiral sa base class na View, ngunit tinatawag lamang para sa mga instance ng ViewGroup. Maaaring suriin ng developer kung ang View ay may child element sa pamamagitan ng default na pag-uugali ng dispatchDraw, ngunit kadalasan ay mas simple na suriin ang instanceof ViewGroup.

Pipeline ng pagguhit: onDraw, dispatchDraw, onDrawForeground

Ang paraan draw(Canvas) ng klase ng View ay nag-o-organisa ng tatlong-yugtong pipeline ng pagguhit. Unang yugto — pagtawag ng onDraw(canvas), kung saan iginuguhit ng View ang nilalaman nito. Ikalawang yugto — pagtawag ng dispatchDraw(canvas), na nagsisimula sa pagguhit ng mga child View. Ikatlong yugto — onDrawForeground(canvas), responsable para sa foreground layer, scrollbars at ripple effects. Ang pagkakasunod-sunod na ito ay ginagarantiyahan ang tamang pagkakapatong ng mga graphic layer.

onDraw ay palaging isinasagawa bago ang dispatchDraw. Ito ay nangangahulugan na ang nilalaman ng parent View ay ipinapakita sa ilalim ng mga child View. Kung kailangan gumuhit ng isang bagay sa ibabaw ng mga bata, ito ay ginagawa sa onDrawForeground o sa naka-override na dispatchDraw na may pagtawag ng super.dispatchDraw(canvas) at kasunod na pagguhit sa ibabaw. Ang background ay iginuguhit bago pa ang onDraw — sa drawBackground(canvas) sa loob ng paraan draw().

Sa hardware acceleration ang pipeline ng draw ay gumagana sa pamamagitan ng GPU. Ang View ay iginuguhit sa DisplayList — isang listahan ng mga utos ng pagguhit na ini-cache at ginagamit muli. Ang dispatchDraw sa hardware-accelerated mode ay nagdaragdag ng DisplayList ng mga child View sa pangkalahatang DisplayList ng eksena. Ang pagbabago ng dispatchDraw ay maaaring humantong sa invalidation ng DisplayList at muling pagbuo ng cache, na nakakaapekto sa pagganap.

ParaanPagkakasunod-sunod ng tawagLayunin
drawBackground1Pagguhit ng background (background drawable)
onDraw2Pagguhit ng nilalaman ng View mismo
dispatchDraw3Pagguhit ng lahat ng child View (para lamang sa ViewGroup)
onDrawForeground4Pagguhit ng foreground, scrollbar at ripple effect

Pagkakaiba ng dispatchDraw at onDraw

onDraw ay responsable para sa pagguhit ng nilalaman ng View mismo — mga hugis, teksto, larawan na pag-aari ng partikular na elementong ito. dispatchDraw ay responsable para sa pagguhit ng mga child View — lahat ng elemento na nasa loob ng ViewGroup. Kung ang ViewGroup ay hindi nag-o-override ng dispatchDraw, ang implementasyon mula sa ViewGroup ang ginagamit, na simpleng recursive na dumadaan sa childCount at tumatawag ng draw para sa bawat bata.

Para sa ViewGroup na hindi gumuhit ng sariling nilalaman (halimbawa, FrameLayout, LinearLayout), ang onDraw ay maaaring i-optimize sa pamamagitan ng pagtatakda ng setWillNotDraw(true). Sa kasong ito, ang onDraw ay hindi kailanman tinatawag, na nakakatipid ng mga resources. Ang dispatchDraw ay patuloy na gumagana at tumatawag ng draw para sa mga child element. Lahat ng standard na ViewGroup (LinearLayout, RelativeLayout, ConstraintLayout) ay gumagamit ng setWillNotDraw(true).

Kung ang dispatchDraw ay na-override nang hindi tinatawag ang super.dispatchDraw(canvas), ang mga child View ay hindi iguguhit. Ito ay maaaring maging kapaki-pakinabang para sa pansamantalang pagtatago ng lahat ng bata, ngunit sa karamihan ng mga kaso ay nagdudulot ng mga error. Inirerekomendang praktika: tawagan ang super.dispatchDraw(canvas) sa simula ng na-override na paraan, pagkatapos ay idagdag ang iyong sariling graphics (halimbawa, overlay na may alpha channel) sa ibabaw ng mga bata.

Halimbawa ng pag-override ng dispatchDraw sa Kotlin

Gumawa tayo ng OverlayViewGroup — isang custom na ViewGroup na nagdaragdag ng semi-transparent na layer na may text label sa ibabaw ng lahat ng child View. Ang dispatchDraw ay unang tumatawag ng super (pagguhit ng lahat ng bata), pagkatapos ay gumuhit ng overlay rectangle at text. Ang Paint ay ginagawa sa constructor upang maiwasan ang mga alokasyon sa draw loop.

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("PREVIEW MODE",
            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))
    }
}

Animated na Z-order sa pamamagitan ng dispatchDraw

Ang sumusunod na halimbawa ay nagpapakita ng pagbabago ng pagkakasunod-sunod ng pagguhit ng mga child element. Ang CircularRevealLayout ay nag-o-override ng dispatchDraw at gumuhit ng mga bata sa reverse na pagkakasunod-sunod, na lumilikha ng epekto ng baligtad na Z-order. Para sa animasyon, isang cyclic na paglipat ng index ng pagguhit ay idinagdag batay sa animated na halaga ng offset.

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))
    }
}

Kailan gagamitin ang dispatchDraw sa Android

dispatchDraw ay ginagamit sa mga sitwasyon kung saan kailangan mong impluwensyahan ang proseso ng pagguhit ng mga child View nang hindi naaapektuhan ang sariling nilalaman ng ViewGroup. Pangunahing sitwasyon: paglalapat ng isang karaniwang epekto (overlay, shadow, gradient) sa ibabaw ng lahat ng bata; pagbabago ng pagkakasunod-sunod ng Z-order para sa paglikha ng epekto ng lalim; pag-animate ng paglitaw o pagkawala ng mga child element sa pamamagitan ng Canvas transformations.

Kung kinakailangan ang pagpapatong ng graphics sa ilalim ng mga child element (epekto ng background), gamitin ang onDraw — ang dispatchDraw ay tinatawag pagkatapos nito. Kung ang graphics ay dapat nasa ibabaw ng mga bata — gamitin ang dispatchDraw na may tawag na super, pagkatapos ay Canvas.draw. Kung kinakailangan ang pandaigdigang pagbabago ng kulay o transparency, mas maginhawang gamitin ang canvas.saveLayerAlpha() sa dispatchDraw, na bumabalot sa pagguhit ng mga bata.

Hindi inirerekomenda ang paggamit ng dispatchDraw para sa: 1) pagguhit ng kumplikadong animasyon sa real-time (gamitin ang invalidate at onDraw); 2) paggawa ng mga screenshot ng ViewGroup (gamitin ang buildDrawingCache() o View.draw(Canvas)); 3) pagbabago ng mga child View (coordinates, sizes — ito ay gawain ng onLayout, hindi ng dispatchDraw). dispatchDraw — para lamang sa paglalapat ng mga visual effect.

Mga Madalas Itanong

Maaari bang tawagan nang direkta ang dispatchDraw nang walang system?

Hindi, ang dispatchDraw — protected na paraan na tinatawag ng Android system sa loob ng public na paraan draw(). Ang direktang pagtawag ng dispatchDraw ay walang saysay, dahil hindi ito nagsasagawa ng mga paghahandang aksyon (pag-save ng Canvas, pagguhit ng background at foreground). Sa halip, gamitin ang View.draw(Canvas) para sa programmatic na pagguhit sa anumang Canvas.

Ano ang mangyayari kapag tinawag ang super.dispatchDraw pagkatapos ng custom na pagguhit?

Kung tatawagin mo ang super.dispatchDraw(canvas) pagkatapos ng iyong sariling mga operasyon sa pagguhit, ang mga child View ay iguguhit sa ibabaw ng custom na graphics. Binabago nito ang pagkakapatong ng layer: unang iginuguhit ang custom na layer, pagkatapos ang mga bata. Kung kinakailangan ang kabaligtaran na sitwasyon (graphics sa ibabaw ng mga bata), tawagan muna ang super.dispatchDraw, pagkatapos ang iyong sariling mga operasyon.

Paano naaapektuhan ng dispatchDraw ang pagganap?

dispatchDraw ay tumatawag ng draw() para sa bawat child View, ang kabuuang oras ay proporsyonal sa bilang ng mga bata. Sa hardware acceleration, ang pagdaragdag ng mga Canvas operation sa dispatchDraw ay maaaring humantong sa invalidation ng DisplayList at muling pagbuo ng cache. Para sa 5–10 bata ang epekto ay minimal, para sa 50+ bata inirerekomenda ang pag-cache ng mga kumplikadong overlay.

Bakit hindi tinatawag ang dispatchDraw para sa View?

View (hindi ViewGroup) ay walang child element, kaya ang dispatchDraw ay hindi gumagawa ng kapaki-pakinabang na trabaho. Gayunpaman, ang paraan ay umiiral sa base class na View para sa polymorphism: ang code sa draw() ay tumatawag ng dispatchDraw para sa anumang View, ngunit ang implementasyon ng View ay hindi naglalaman ng lohika ng pagguhit ng mga bata. Tanging ang ViewGroup ang nag-o-override ng dispatchDraw.

Maaari bang baguhin ang Canvas sa dispatchDraw?

Oo, ang dispatchDraw ay tumatanggap ng Canvas at maaaring i-transform bago tawagan ang super.dispatchDraw (translate, rotate, scale). Ito ay inilalapat para sa pag-animate ng buong set ng mga bata bilang isang solong kabuuan. Pagkatapos makumpleto ang super.dispatchDraw, inirerekomenda na ibalik ang Canvas sa orihinal na estado.

Buod

  • dispatchDraw(Canvas) — paraan ng ViewGroup para sa recursive na pagguhit ng lahat ng child View pagkatapos ng onDraw
  • Pipeline ng draw: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw ay gumuhit ng nilalaman ng View mismo, dispatchDraw — nilalaman ng mga child element
  • setWillNotDraw(true) ay nag-o-optimize ng ViewGroup, nilalaktawan ang onDraw, ngunit ang dispatchDraw ay patuloy na gumagana
  • Ang pag-o-override ng dispatchDraw ay ginagamit para sa paglalapat ng mga epekto, pagbabago ng Z-order at Canvas transformations ng mga bata
  • Ang pagkakasunod-sunod ng super.dispatchDraw at custom na pagguhit ay tumutukoy kung ano ang nasa ibabaw ng mga bata
  • Para sa ordinaryong View (hindi ViewGroup) ang dispatchDraw ay walang laman — walang child element

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din