onDraw() — ay ang pangunahing metodo ng klase View sa Android SDK, na responsable sa pag-render ng nilalaman ng view. Tinatawag ng sistema ang onDraw sa bawat pag-update ng itsura ng View — sa paunang rendering, pagkatapos ng invalidate() o kapag nagbago ang mga sukat. Ayon sa Android Developers Documentation (2026), ang pag-override ng onDraw gamit ang Canvas API ay ang pangunahing paraan ng paggawa ng custom na graphics sa Android applications.
Mga pangunahing punto
onDraw(Canvas canvas) — ay isang protected metodo ng klase android.view.View, na tinatawag ng Android system kapag ang View ay kailangang i-render. Ino-override ng developer ang metodong ito upang ipatupad ang custom na rendering: mga hugis, teksto, larawan, gradient, animation. Ang parameter na Canvas ay ang graphical surface kung saan isinasagawa ang lahat ng operasyon sa pagguhit.
Ang signature ng metodo ay simple: override fun onDraw(canvas: Canvas). Sa loob ng metodong ito ay hindi maaaring gumawa ng bagong object (upang maiwasan ang GC load), magsagawa ng mahabang operasyon o makipag-ugnayan sa network. Ang View ay naglalaman din ng metodo onDraw sa lifecycle nito sa pagitan ng onLayout (pagkalkula ng posisyon) at dispatchDraw (rendering ng mga child element).
Kapag nagmana mula sa View, ang pag-override ng onDraw ay kinakailangan upang magpakita ng anumang nilalaman — kung hindi, ang View ay magiging walang laman. Kapag nagmana mula sa umiiral na mga subklase (TextView, ImageView, Button), ang pag-override ng onDraw ay opsyonal at ginagamit upang magdagdag ng karagdagang graphics sa ibabaw ng karaniwang nilalaman.
Android ay nagsasagawa ng rendering ng View sa isang mahigpit na tinukoy na pagkakasunod-sunod, na tinatawag mula sa pangunahing thread (UI thread). Ang buong cycle ay binubuo ng tatlong phase: measure (pagsukat ng mga sukat), layout (pagkalkula ng mga posisyon) at draw (rendering). Ang onDraw ay bahagi ng draw phase at tinatawag pagkatapos matukoy ang mga sukat at posisyon ng View.
draw(Canvas canvas) — ay ang public metodo ng View na tumatawag ng onDraw at dispatchDraw. Ang metodo draw ay hindi nilayon na i-override — sa halip, ang onDraw ang ino-override. Pagkatapos ng execution ng onDraw para sa kasalukuyang View, tinatawag ang dispatchDraw, na recursively tumatawag ng draw para sa lahat ng child View (kung ang kasalukuyang View ay isang ViewGroup container).
onDraw ay tinatawag sa mga sumusunod na kondisyon: 1) View ay nagiging visible sa unang pagkakataon (pagkatapos ng setVisibility(VISIBLE)); 2) ang application ay tumatawag ng invalidate() para sa View na ito o sa magulang nito; 3) nagbabago ang laki ng View (bagong layout); 4) nagbabago ang animation na nauugnay sa View (sa pamamagitan ng ValueAnimator o ObjectAnimator); 5) nagbabago ang estado ng View (pressed, focused, enabled).
Mula sa Android 3.0 (API 11), karamihan sa mga Canvas operations ay isinasagawa gamit ang hardware acceleration sa pamamagitan ng GPU. Maaaring suriin ng developer kung naka-enable ang acceleration sa pamamagitan ng canvas.isHardwareAccelerated(). Sa hardware acceleration, ang ilang Canvas operations ay hindi suportado (hal., clipPath na may non-rectangular na lugar) at maaaring balewalain o magdulot ng error.
Canvas ay nagbibigay ng mayamang set ng mga metodo para sa pagguhit. Lahat ng metodo ay gumagana sa kasalukuyang coordinate system, na maaaring baguhin sa pamamagitan ng mga transformasyon (translate, rotate, scale, skew). Bawat metodo ay tumatanggap ng Paint object na tumutukoy sa kulay, style, kapal ng linya, mga epekto at anti-aliasing.
| Canvas method | Layunin | Mga pangunahing parameter ng Paint |
|---|---|---|
| drawCircle | Pagguhit ng bilog | centerX, centerY, radius, paint |
| drawRect | Pagguhit ng parihaba | left, top, right, bottom, paint |
| drawLine | Pagguhit ng linya | startX, startY, endX, endY, paint |
| drawPath | Pagguhit ng arbitraryong contour | path, paint |
| drawBitmap | Pagguhit ng larawan | bitmap, srcRect, dstRect, paint |
| drawText | Pagguhit ng teksto | text, x, y, paint |
| drawArc | Pagguhit ng arko o sektor | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Pagguhit ng obalo | ovalRect, paint |
Paint ay tumutukoy sa style ng rendering: kulay (setColor), kapal ng linya (setStrokeWidth), style ng fill (setStyle — FILL, STROKE, FILL_AND_STROKE), anti-aliasing (setAntiAlias), mga epekto (setShadowLayer, setMaskFilter) at mga gradient (setShader). Inirerekomenda na gawin at i-configure ang Paint nang isang beses sa constructor ng View, hindi sa loob ng onDraw, upang maiwasan ang paggawa ng object sa rendering cycle.
Canvas ay gumagamit ng coordinate system kung saan ang X axis ay nakaturo sa kanan, at ang Y axis ay pababa. Ang pinagmulan (0, 0) ay ang kaliwang itaas na sulok ng View. Bago tumawag ng draw methods, maaaring baguhin ng developer ang coordinate system sa pamamagitan ng canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Lahat ng kasunod na operasyon sa pagguhit ay isasagawa sa transformed system.
onDraw ay isinasagawa sa pangunahing UI thread, at ang tagal nito ay direktang nakakaapekto sa frame rate (fps). Upang mapanatili ang 60 fps, ang metodo onDraw ay dapat matapos sa loob ng hindi hihigit sa 16 millisecond. Anumang pagbagal ay nagdudulot ng paglaktaw ng frame (jank), na biswal na nakikita bilang pag-stutter ng animation o pagkaantala sa scrolling.
Mga pangunahing tuntunin ng pag-optimize ng onDraw ay kinabibilangan ng: 1) huwag gumawa ng object sa loob ng onDraw — lahat ng Paint, Path, Rect ay dapat na field ng klase; 2) huwag mag-allocate ng memory (new) — ito ay nagti-trigger ng garbage collection (GC) at paglaktaw ng frame; 3) i-cache ang mga kumplikadong operasyon sa Bitmap sa pamamagitan ng Bitmap.createBitmap at iguhit ang handa na Bitmap; 4) huwag magsagawa ng input-output operations o pag-load ng resources.
clipRect ay naglilimita sa lugar ng pagguhit sa isang tinukoy na parihaba. Ginagamit upang hindi mag-aksaya ng oras ang Canvas sa pag-render ng pixels sa labas ng nakikitang lugar. Lalo na epektibo sa scrolling ng malalaking listahan o scaling: itakda ang clipRect sa nakikitang lugar (dirty rect), at ang Canvas ay iguguhit lamang ang nakikita.
canvas.save() ay nagse-save ng kasalukuyang estado ng Canvas (transformasyon at clip) sa stack. canvas.restore() ay nagre-restore ng huling na-save na estado. Ito ay kinakailangan para sa pansamantalang transformasyon: i-save ang estado, ilapat ang translate/rotate, iguhit, pagkatapos ay i-restore. Ang save at restore ay magaan at hindi nakakaapekto sa pagganap kung gagamitin nang makatwiran (mas mababa sa 10–15 antas ng nesting).
invalidate() — ay ang metodo ng View na nagpapaalam sa sistema na ang nilalaman ng View ay lipas na at kailangang i-redraw. Ang pagtawag ng invalidate ay nagpapasimula ng bagong draw cycle kung saan tatawagin ang onDraw. Ang invalidate ay dapat tawagin lamang mula sa UI thread (main thread). Para sa pagtawag mula sa background thread, ginagamit ang postInvalidate.
postInvalidate() — ang thread-safe na bersyon ng invalidate, na nagpapadala ng kahilingan para i-redraw sa queue ng pangunahing thread sa pamamagitan ng Handler. Ginagamit sa mga background task kapag kailangang i-update ang View pagkatapos ng pagkarga ng data o pagkalkula. Pinagsasama sa runOnUiThread para sa pag-sync sa UI.
Para sa partial na pag-redraw ginagamit ang invalidate(Rect dirty) o invalidate(int l, int t, int r, int b). Ito ay nagpapahiwatig sa sistema na ang tinukoy na lugar lamang ang nagbago, hindi ang buong View. Ino-optimize ng Android ang rendering sa pamamagitan ng pag-redraw lamang ng dirty rect, na makabuluhang nagpapabuti ng pagganap sa mga animation na may bahagyang pag-update.
Tingnan natin ang mga praktikal na halimbawa ng pag-override ng onDraw para gumawa ng custom na Views sa Android gamit ang Kotlin.
Ang halimbawa ay gumagawa ng circular na indicator ng progreso na may animated na pagpuno. Ang onDraw ay gumuhit ng dalawang bilog: isang gray na background at isang may kulay na puno, na ang arko ay tinutukoy ng kasalukuyang progreso. Ang pagtawag ng invalidate sa setProgress ay tinitiyak ang pag-redraw kapag nagbago ang halaga.
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)
}
}
Ang halimbawa ay gumuhit ng parihaba na may gradient at teksto sa ibabaw nito. Ang gradient ay ginagawa sa pamamagitan ng LinearGradient (Shader), at ang teksto sa pamamagitan ng drawText. Ang parehong Paint ay ginawa nang isang beses sa constructor, at sa onDraw ay binabago lamang ang laki ng parihaba batay sa kasalukuyang lapad ng View.
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("Kumusta mula sa Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Ang halimbawa ay gumagawa ng animated na orasan na nag-a-update ng rendering bawat 1000 millisecond. Ang postDelayed sa onDraw ay tumatawag ng invalidate pagkatapos ng isang segundo, na lumilikha ng walang katapusang cycle ng pag-redraw. Ang mga kamay ng orasan ay iginuguhit sa pamamagitan ng drawLine na may iba't ibang haba at kapal.
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)
}
}
Mga madalas itanong
Hindi, hindi dapat direktang tawagin ang onDraw. Para sa kahilingan ng pag-redraw, gamitin ang invalidate() (mula sa UI thread) o postInvalidate() (mula sa background thread). Ang sistema mismo ang nagpapasya kung kailan tatawagin ang onDraw at ino-optimize ang rendering sa pamamagitan ng pagsasama-sama ng maraming invalidate sa isang pass.
Mga dahilan: View ay nakatago (setVisibility(GONE) o INVISIBLE); View ay hindi naidagdag sa hierarchy ng Window; laki ng View ay 0 (width o height = 0); tinatawag ang setWillNotDraw(true) (default true para sa ViewGroup). Suriin ang bawat isa sa mga puntong ito habang nagde-debug.
Sa 60 fps, ang onDraw ay maaaring tawagin hanggang 60 beses bawat segundo. Sa 120 fps (sa mga device na may 120 Hz display) — hanggang 120 beses. Bawat pagtawag ay dapat matapos sa ~8–16 millisecond. Sa mga aktibong animation sa pamamagitan ng ObjectAnimator o ValueAnimator, ang frequency ay tinutukoy ng bilang ng animation frames.
onDraw ay responsable para sa pag-render ng nilalaman ng View mismo. dispatchDraw ay responsable para sa pag-render ng mga child View (sa ViewGroup). Ang dispatchDraw ay tinatawag pagkatapos ng onDraw at recursively dumadaan sa lahat ng child elements. Para sa isang ordinaryong View (hindi ViewGroup), ang dispatchDraw ay walang laman.
Oo, ang Canvas ay maaaring gawin para sa Bitmap sa pamamagitan ng Canvas(bitmap) at gumuhit dito sa anumang thread. Ito ay ginagamit para sa pre-computation ng kumplikadong graphics sa background. Gayunpaman, ang Canvas na ipinapasa sa onDraw ay pag-aari ng sistema at maaari lamang gamitin sa UI thread sa loob ng metodo onDraw.
Buod
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.
Basahin din