onDraw() — Android SDK-da View sinfinin əsas metodu olub, görünüşün məzmununun çəkilməsinə cavabdehdir. Sistem onDraw-ı View-in görünüşü hər yeniləndikdə çağırır — ilkin render zamanı, invalidate()-dan sonra və ya ölçülər dəyişdikdə. Android Developers Documentation (2026)-yə görə, Canvas API istifadə edərək onDraw-ı override etmək Android tətbiqlərində xüsusi qrafika yaratmağın əsas üsuludur.
Əsas məqamlar
onDraw(Canvas canvas) — android.view.View sinfinin qorunan metodu olub, View render edilməli olduqda Android sistemi tərəfindən çağırılır. Tərtibatçı bu metodu xüsusi çəkməni həyata keçirmək üçün override edir: fiqurlar, mətn, şəkillər, gradientlər, animasiya. Canvas parametri bütün rəsm əməliyyatlarının yerinə yetirildiyi qrafik səthdir.
Metodun imzası sadədir: override fun onDraw(canvas: Canvas). Bu metodun daxilində yeni obyektlər yaratmaq (GC yükünün qarşısını almaq üçün), uzun əməliyyatlar yerinə yetirmək və ya şəbəkə ilə işləmək olmaz. View həmçinin öz həyat dövründə onLayout (mövqelərin hesablanması) və dispatchDraw (uşaq elementlərin çəkilməsi) arasında onDraw metodunu ehtiva edir.
View-dən vərəsəlik aldıqda hər hansı məzmunun göstərilməsi üçün onDraw-ı override etmək məcburidir — əks halda View boş olacaq. Mövcud alt siniflərdən (TextView, ImageView, Button) vərəsəlik aldıqda onDraw-ı override etmək isteğe bağlıdır və standart məzmun üzərində əlavə qrafika əlavə etmək üçün istifadə olunur.
Android View-in çəkilməsini ciddi şəkildə müəyyən edilmiş ardıcıllıqla yerinə yetirir, bu ardıcıllıq əsas axından (UI thread) çağırılır. Tam dövr üç fazadan ibarətdir: measure (ölçülərin ölçülməsi), layout (mövqelərin hesablanması) və draw (çəkmə). onDraw draw fazasının bir hissəsidir və View-in ölçüləri və mövqeyi müəyyən edildikdən sonra çağırılır.
draw(Canvas canvas) — onDraw və dispatchDraw-ı çağıran View-in public metodudur. draw metodu override üçün nəzərdə tutulmayıb — onun əvəzinə onDraw override edilir. Cari View üçün onDraw icra edildikdən sonra dispatchDraw çağırılır, o da bütün uşaq View-lər üçün draw-ı rekursiv şəkildə çağırır (əgər cari View ViewGroup konteyneridirsə).
onDraw aşağıdakı şərtlər daxilində çağırılır: 1) View ilk dəfə görünən olur (setVisibility(VISIBLE)-dan sonra); 2) tətbiq bu View və ya onun valideyni üçün invalidate() çağırır; 3) View-in ölçüsü dəyişir (yeni layout); 4) View ilə əlaqəli animasiya dəyişir (ValueAnimator və ya ObjectAnimator vasitəsilə); 5) View-in vəziyyəti dəyişir (pressed, focused, enabled).
Android 3.0 (API 11)-dan etibarən Canvas əməliyyatlarının əksəriyyəti GPU vasitəsilə aparat sürətləndirilməsi ilə yerinə yetirilir. Tərtibatçı sürətləndirmənin aktiv olub-olmadığını canvas.isHardwareAccelerated() vasitəsilə yoxlaya bilər. Apparat sürətləndirilməsi zamanı bəzi Canvas əməliyyatları dəstəklənmir (məsələn, düzbucaqlı olmayan sahə ilə clipPath) və nəzərə alınmaya və ya xətalara səbəb ola bilər.
Canvas rəsm çəkmək üçün zəngin metodlar dəsti təqdim edir. Bütün metodlar cari koordinat sistemində işləyir, bu sistem transformasiyalar (translate, rotate, scale, skew) vasitəsilə dəyişdirilə bilər. Hər bir metod rəng, üslub, xətt qalınlığı, effektlər və hamarlaşdırmanı təyin edən Paint obyektini qəbul edir.
| Canvas metodu | Məqsəd | Paint-in əsas parametrləri |
|---|---|---|
| drawCircle | Dairə çəkmək | centerX, centerY, radius, paint |
| drawRect | Düzbucaqlı çəkmək | left, top, right, bottom, paint |
| drawLine | Xətt çəkmək | startX, startY, endX, endY, paint |
| drawPath | İxtiyari kontur çəkmək | path, paint |
| drawBitmap | Şəkil çəkmək | bitmap, srcRect, dstRect, paint |
| drawText | Mətn çəkmək | text, x, y, paint |
| drawArc | Qövs və ya sektor çəkmək | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Oval çəkmək | ovalRect, paint |
Paint çəkmə üslubunu təyin edir: rəng (setColor), xətt qalınlığı (setStrokeWidth), doldurma üslubu (setStyle — FILL, STROKE, FILL_AND_STROKE), hamarlaşdırma (setAntiAlias), effektlər (setShadowLayer, setMaskFilter) və gradientlər (setShader). Paint-in View konstruktorunda bir dəfə yaradılması və konfiqurasiya edilməsi tövsiyə olunur, onDraw daxilində deyil — bu, çəkmə dövründə obyekt yaradılmasının qarşısını alır.
Canvas koordinat sistemindən istifadə edir, burada X oxu sağa, Y oxu isə aşağıya yönəlmişdir. Koordinatların başlanğıcı (0, 0) View-in yuxarı sol küncüdür. draw metodlarını çağırmazdan əvvəl tərtibatçı koordinat sistemini canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py) vasitəsilə dəyişə bilər. Bütün sonrakı rəsm əməliyyatları transformasiya edilmiş sistemdə yerinə yetiriləcək.
onDraw əsas UI axınında icra olunur və onun müddəti birbaşa saniyədə kadr sayına (fps) təsir edir. 60 fps qorumaq üçün onDraw metodu 16 millisaniyədən çox olmayaraq tamamlanmalıdır. Hər hansı yavaşlama kadrların buraxılmasına (jank) gətirib çıxarır ki, bu da vizual olaraq animasiyanın ləngiməsi və ya sürüşdürmə zamanı gecikmə kimi qəbul edilir.
Əsas qaydalar onDraw optimallaşdırılmasına daxildir: 1) onDraw daxilində obyekt yaratmayın — bütün Paint, Path, Rect sinif sahələri olmalıdır; 2) yaddaş ayırmayın (new) — bu, zibil yığıcını (GC) işə salır və kadrların buraxılmasına səbəb olur; 3) mürəkkəb əməliyyatları Bitmap.createBitmap vasitəsilə Bitmap-da keşləyin və hazır Bitmap-ı çəkin; 4) giriş-çıxış əməliyyatları və ya resursların yüklənməsini yerinə yetirməyin.
clipRect çəkmə sahəsini müəyyən düzbucaqlı ilə məhdudlaşdırır. Canvas-ın görünən sahədən kənar piksellərin renderinə vaxt sərf etməməsi üçün istifadə olunur. Böyük siyahıların sürüşdürülməsi və ya miqyaslama zamanı xüsusilə effektivdir: clipRect-i görünən sahəyə (dirty rect) təyin edin və Canvas yalnız görünəni çəkəcək.
canvas.save() Canvas-ın cari vəziyyətini (transformasiya və clip) yığında saxlayır. canvas.restore() son saxlanmış vəziyyəti bərpa edir. Bu, müvəqqəti transformasiyalar üçün lazımdır: vəziyyəti saxlayırsınız, translate/rotate tətbiq edirsiniz, çəkirsiniz, sonra bərpa edirsiniz. save və restore yüngüldür və ağıllı istifadə zamanı (10–15 səviyyədən az iç-içəlik) performansa təsir etmir.
invalidate() — View-in məzmununun köhnəldiyini və yenidən çəkilməsi lazım olduğunu sistemə bildirən View metodudur. invalidate çağırışı onDraw-ın çağırılacağı yeni draw dövrünü başladır. invalidate yalnız UI axınından (main thread) çağırılmalıdır. Fon axınından çağırış üçün postInvalidate istifadə olunur.
postInvalidate() — invalidate-in thread-təhlükəsiz versiyasıdır, yenidən çəkmə sorğusunu Handler vasitəsilə əsas axının növbəsinə göndərir. Məlumat yükləməsi və ya hesablamalar tamamlandıqdan sonra View-i yeniləmək lazım olduqda fon tapşırıqlarında istifadə olunur. UI ilə sinxronizasiya üçün runOnUiThread ilə birləşdirilir.
Qismən yenidən çəkmə üçün invalidate(Rect dirty) və ya invalidate(int l, int t, int r, int b) istifadə olunur. Bu, sistemə yalnız müəyyən sahənin dəyişdiyini, bütün View-in deyil, göstərir. Android yalnız dirty rect-i yenidən çəkərək renderi optimallaşdırır ki, bu da qismən yeniləmələrlə animasiyalarda performansı əhəmiyyətli dərəcədə artırır.
Praktik nümunələri nəzərdən keçirək — Kotlin-də Android üçün xüsusi View-lər yaratmaq üçün onDraw-ın override edilməsi.
Nümunə animasiyalı doldurma ilə dairəvi gedişat göstəricisi yaradır. onDraw iki dairə çəkir: boz fon rəngli və rəngli doldurulan, qövsü cari gedişatla müəyyən edilir. setProgress-da invalidate çağırışı dəyər dəyişdikdə yenidən çəkməni təmin edir.
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)
}
}
Nümunə üzərində mətn olan gradient düzbucaqlı çəkir. Gradient LinearGradient (Shader) vasitəsilə, mətn isə drawText vasitəsilə yaradılır. Hər iki Paint konstruktorda bir dəfə yaradılır, onDraw-da isə yalnız düzbucaqlının ölçüsü cari View enindən asılı olaraq dəyişdirilir.
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("Canvas-dan salam",
xCenter, yCenter - yOffset,
textPaint)
}
}
Nümunə hər 1000 millisaniyədən bir çəkməni yeniləyən animasiyalı saat yaradır. onDraw-da postDelayed bir saniyədən sonra invalidate çağıraraq sonsuz yenidən çəkmə dövrü yaradır. Saat əqrəbləri müxtəlif uzunluq və qalınlıqla drawLine vasitəsilə çəkilir.
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)
}
}
Tez-tez verilən suallar
Xeyr, onDraw-ı birbaşa çağırmaq olmaz. Yenidən çəkmə sorğusu üçün invalidate() (UI axınından) və ya postInvalidate() (fon axınından) istifadə edin. Sistem özü onDraw-ın nə vaxt çağırılacağına qərar verir və çoxsaylı invalidate-ləri bir keçiddə birləşdirərək renderi optimallaşdırır.
Səbəblər: View gizlidir (setVisibility(GONE) və ya INVISIBLE); View Window iyerarxiyasına əlavə edilməyib; View-in ölçüsü 0-dır (width və ya height = 0); setWillNotDraw(true) çağırılır (ViewGroup üçün susmaya görə true). Debug zamanı bu maddələrin hər birini yoxlayın.
60 fps-də onDraw saniyədə 60 dəfəyə qədər çağırıla bilər. 120 fps-də (120 Hz displeyli cihazlarda) — 120 dəfəyə qədər. Hər çağırış ~8–16 millisaniyəyə tamamlanmalıdır. ObjectAnimator və ya ValueAnimator vasitəsilə aktiv animasiyalar zamanı tezlik animasiya kadrlarının sayı ilə müəyyən edilir.
onDraw View-in öz məzmununun çəkilməsinə cavabdehdir. dispatchDraw uşaq View-lərin çəkilməsinə cavabdehdir (ViewGroup-da). dispatchDraw onDraw-dan sonra çağırılır və bütün uşaq elementləri rekursiv şəkildə gəzir. Adi View (ViewGroup deyil) üçün dispatchDraw boşdur.
Bəli, Canvas Canvas(bitmap) vasitəsilə Bitmap üçün yaradıla bilər və istənilən axında çəkmək olar. Bu, mürəkkəb qrafikanın fonda əvvəlcədən hesablanması üçün istifadə olunur. Lakin onDraw-da ötürülən Canvas sistemə məxsusdur və yalnız UI axınında onDraw metodu daxilində istifadə edilə bilər.
Nəticə
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun