dispatchDraw() — Android iyerarxiyasında bütün uşaq View-lərin rekursiv çəkilməsinə cavabdeh olan ViewGroup sinifinin metodudur. Android Developers Documentation (2026) məlumatına görə, dispatchDraw əsas View-in draw() metodunda onDraw-dan sonra avtomatik çağırılır və bütün uşaq elementlərindən keçərək onların öz draw() metodunu çağırır. Tərtibatçılar effektlər əlavə etmək, uşaqların üzərinə qrafik qoymaq və ya çəkmə sırasını dəyişdirmək üçün xüsusi ViewGroup-larda dispatchDraw-ı yenidən təyin edirlər.
Əsas məlumatlar
dispatchDraw(Canvas canvas) — Android sisteminin cari konteynerin bütün uşaq View-lərini çəkmək üçün çağırdığı ViewGroup sinifinin protected metodudur. dispatchDraw standart draw konveyerinin bir hissəsidir: əvvəlcə onDraw (View-in öz məzmununun çəkilməsi), sonra dispatchDraw (uşaqların rekursiv çəkilməsi), daha sonra onDrawForeground (foreground və scrollbars-ın çəkilməsi) icra olunur. Metod tətbiq kodundan birbaşa çağırılmaq üçün nəzərdə tutulmayıb.
ViewGroup-da standart dispatchDraw tətbiqi bütün uşaq View-ləri dövr edir, onların görünürlüyünü yoxlayır və hər biri üçün draw(Canvas) çağırır. Dövriyyə sırası uşaqların indekslərinə uyğundur (0-dan childCount - 1-ə qədər). Uşaq View-lər üst-üstə düşərsə, siyahıda sonuncu olanlar birincilərin üzərində çəkilir. dispatchDraw-da sıranı dəyişdirməklə Z-order çəkmə sırasını dəyişmək olar.
View (ViewGroup deyil) üçün dispatchDraw metodu boşdur — adi View-in uşaq elementləri yoxdur, ona görə də çəkməyə heç nə yoxdur. Metod View sinifində mövcuddur, lakin yalnız ViewGroup nümunələrində çağırılır. Tərtibatçı View-in uşaq elementlərinin olub-olmadığını dispatchDraw-ın standart davranışı ilə yoxlaya bilər, lakin çox vaxt instanceof ViewGroup yoxlamaq daha asandır.
draw(Canvas) metodu View sinifinin üç mərhələli çəkmə konveyerini təşkil edir. Birinci mərhələ — View-in öz məzmununu çəkdiyi onDraw(canvas) çağırışı. İkinci mərhələ — uşaq View-lərin çəkilməsini başladan dispatchDraw(canvas) çağırışı. Üçüncü mərhələ — foreground qatı, scrollbars və ripple effektlərinə cavabdeh olan onDrawForeground(canvas). Bu ardıcıllıq qrafik qatların düzgün yerləşmə sırasını təmin edir.
onDraw həmişə dispatchDraw-dan əvvəl icra olunur. Bu o deməkdir ki, valideyn View-in məzmunu uşaq View-lərin altında göstərilir. Uşaqların üzərində nə isə çəkmək lazımdırsa, bu onDrawForeground-da və ya super.dispatchDraw(canvas) çağırışı ilə yenidən təyin edilmiş dispatchDraw-da edilir. Fon (background) onDraw-dan əvvəl — draw() metodunun daxilində drawBackground(canvas) ilə çəkilir.
Avadanlıq sürətləndirilməsi (hardware acceleration) zamanı draw konveyeri GPU vasitəsilə işləyir. View DisplayList-də — keşlənən və təkrar istifadə olunan rəsm əmrləri siyahısında çəkilir. Avadanlıqla sürətləndirilmiş rejimdə dispatchDraw uşaq View-lərin DisplayList-ni səhnənin ümumi DisplayList-nə əlavə edir. dispatchDraw-ın dəyişdirilməsi DisplayList-nin etibarsızlaşmasına və keşin yenidən qurulmasına səbəb ola bilər ki, bu da performansa təsir edir.
| Metod | Çağırış sırası | Təyinat |
|---|---|---|
| drawBackground | 1 | Fonun çəkilməsi (background drawable) |
| onDraw | 2 | View-in öz məzmununun çəkilməsi |
| dispatchDraw | 3 | Bütün uşaq View-lərin çəkilməsi (yalnız ViewGroup üçün) |
| onDrawForeground | 4 | Foreground, scrollbar və ripple effektinin çəkilməsi |
onDraw View-in öz məzmununun — bu konkret elementə aid olan fiqurların, mətnin, şəkillərin çəkilməsinə cavabdehdir. dispatchDraw uşaq View-lərin — ViewGroup daxilində olan bütün elementlərin çəkilməsinə cavabdehdir. ViewGroup dispatchDraw-ı yenidən təyin etmirsə, ViewGroup-dakı standart tətbiq istifadə olunur, o da sadəcə childCount üzrə rekursiv keçir və hər uşaq üçün draw çağırır.
Öz məzmununu çəkməyən ViewGroup (məsələn, FrameLayout, LinearLayout) üçün onDraw setWillNotDraw(true) təyin etməklə optimallaşdırıla bilər. Bu halda onDraw heç vaxt çağırılmır, bu da resurslara qənaət edir. dispatchDraw isə işləməyə davam edir və uşaq elementlər üçün draw çağırır. Bütün standart ViewGroup-lar (LinearLayout, RelativeLayout, ConstraintLayout) setWillNotDraw(true) istifadə edir.
Əgər dispatchDraw super.dispatchDraw(canvas) çağırışı olmadan yenidən təyin edilərsə, uşaq View-lər çəkilməyəcək. Bu, bütün uşaqları müvəqqəti gizlətmək üçün faydalı ola bilər, lakin əksər hallarda səhvlərə səbəb olur. Tövsiyə olunan təcrübə: yenidən təyin edilmiş metodun əvvəlində super.dispatchDraw(canvas) çağırın, sonra uşaqların üzərinə öz qrafikinizi (məsələn, alfa kanalı olan overlay) əlavə edin.
OverlayViewGroup yaradaq — bütün uşaq View-lərin üzərinə mətn etiketi olan yarımşəffaf qat əlavə edən xüsusi ViewGroup. dispatchDraw əvvəlcə super-i çağırır (bütün uşaqların çəkilməsi), sonra overlay düzbucaqlısını və mətni çəkir. Paint draw dövründə ayırmaların qarşısını almaq üçün konstruktorda yaradılır.
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("ÖN Baxış REJIMI",
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))
}
}
Növbəti nümunə uşaq elementlərin çəkilmə sırasının dəyişdirilməsini göstərir. CircularRevealLayout dispatchDraw-ı yenidən təyin edir və uşaqları tərs qaydada çəkir, tərsinə çevrilmiş Z-order effekti yaradır. Animasiya üçün offset-in animasiya dəyərinə əsasən çəkmə indeksinin tsiklik yerdəyişməsi əlavə olunur.
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))
}
}
dispatchDraw ViewGroup-un öz məzmununa toxunmadan uşaq View-lərin çəkilmə prosesinə təsir etmək lazım olan ssenarilərdə istifadə olunur. Əsas ssenarilər: bütün uşaqların üzərinə ümumi effektin (overlay, shadow, gradient) qoyulması; dərinlik effekti yaratmaq üçün Z-order sırasının dəyişdirilməsi; Canvas transformasiyaları vasitəsilə uşaq elementlərin görünmə və ya yox olma animasiyası.
Əgər qrafik uşaq elementlərin altında yerləşdirilməlidirsə (fon effekti), onDraw istifadə edilməlidir — dispatchDraw ondan sonra çağırılır. Qrafik uşaqların üzərində olmalıdırsa — super çağırışı ilə dispatchDraw, sonra isə Canvas.draw istifadə edin. Qlobal rəng və ya şəffaflıq dəyişikliyi tələb olunarsa, uşaqların çəkilməsini əhatə edən dispatchDraw-da canvas.saveLayerAlpha() istifadə etmək rahatdır.
Tövsiyə edilmir dispatchDraw-dan aşağıdakılar üçün istifadə: 1) real-time mürəkkəb animasiya çəkmək (invalidate və onDraw istifadə edin); 2) ViewGroup-un ekran görüntülərini yaratmaq (buildDrawingCache() və ya View.draw(Canvas) istifadə edin); 3) uşaq View-ləri dəyişdirmək (koordinatlar, ölçülər — bu onLayout-un işidir, dispatchDraw-ın deyil). dispatchDraw — yalnız vizual effektlərin qoyulması üçündür.
Tez-tez verilən suallar
Xeyr, dispatchDraw — Android sistemi tərəfindən public draw() metodu daxilində çağırılan protected metoddur. dispatchDraw-ı birbaşa çağırmağın mənası yoxdur, çünki o hazırlıq hərəkətlərini (Canvas-ın saxlanması, fonun və foreground-ın çəkilməsi) yerinə yetirmir. Bunun əvəzinə ixtiyari Canvas-da proqramlı çəkmə üçün View.draw(Canvas) istifadə edin.
Öz çəkmə əməliyyatlarınızdan sonra super.dispatchDraw(canvas) çağırsanız, uşaq View-lər xüsusi qrafikanın üzərində çəkiləcək. Bu qatların yerləşməsini dəyişir: əvvəlcə xüsusi qat, sonra uşaqlar çəkilir. Əks vəziyyət lazımdırsa (qrafik uşaqların üzərində), əvvəlcə super.dispatchDraw, sonra öz əməliyyatlarınızı çağırın.
dispatchDraw hər uşaq View üçün draw() çağırır, ümumi vaxt uşaqların sayına mütənasibdir. Hardware acceleration zamanı dispatchDraw-da Canvas əməliyyatlarının əlavə edilməsi DisplayList-in etibarsızlaşmasına və keşin yenidən qurulmasına səbəb ola bilər. 5–10 uşaq üçün təsir minimaldır, 50+ uşaq üçün mürəkkəb qatlamaların keşlənməsi tövsiyə olunur.
View (ViewGroup deyil) uşaq elementlərə malik deyil, ona görə də dispatchDraw faydalı iş görmür. Lakin metod View sinfində polimorfizm üçün mövcuddur: draw() kodu istənilən View üçün dispatchDraw çağırır, lakin View tətbiqində uşaqların çəkilmə məntiqi yoxdur. Yalnız ViewGroup dispatchDraw-ı yenidən təyin edir.
Bəli, dispatchDraw Canvas qəbul edir və super.dispatchDraw çağırmazdan əvvəl onu transformasiya etmək olar (translate, rotate, scale). Bu, bütün uşaq dəstini vahid bir bütöv olaraq animasiya etmək üçün tətbiq edilir. super.dispatchDraw tamamlandıqdan sonra Canvas-ı ilkin vəziyyətinə qaytarmaq tövsiyə olunur.
Xülasə
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