onDraw(), Android SDK’daki View sınıfının, bir görünümün içeriğini çizmekten sorumlu anahtar bir yöntemidir. Sistem, View’in görünümünün her güncellenmesinde onDraw’ı çağırır — ilk oluşturma sırasında, invalidate() sonrasında veya boyutlar değiştiğinde. Android Developers Documentation (2026)’ya göre, Canvas API kullanarak onDraw’ı geçersiz kılmak, Android uygulamalarında özel grafikler oluşturmanın birincil yoludur.
Ana Noktalar
onDraw(Canvas canvas), android.view.View sınıfının protected bir metodu olup, bir View’in çizilmesi gerektiğinde Android sistemi tarafından çağrılır. Geliştirici, özel çizim (şekiller, metin, görüntüler, gradyanlar, animasyonlar) uygulamak için bu metodu geçersiz kılar. Canvas parametresi, tüm çizim işlemlerinin gerçekleştirildiği grafiksel bir yüzeydir.
Metot imzası basittir: override fun onDraw(canvas: Canvas). Bu metodun içinde yeni nesneler oluşturulmamalı (GC yükünden kaçınmak için), uzun işlemler yapılmamalı veya ağ ile çalışılmamalıdır. View, yaşam döngüsünde onLayout (konum hesaplama) ve dispatchDraw (alt öğeleri çizme) arasında onDraw metodunu da içerir.
View’den miras alındığında, herhangi bir içeriği görüntülemek için onDraw’ı geçersiz kılmak zorunludur — aksi takdirde View boş olacaktır. Mevcut alt sınıflardan (TextView, ImageView, Button) miras alındığında, onDraw’ı geçersiz kılmak isteğe bağlıdır ve standart içeriğin üstüne ek grafikler eklemek için kullanılır.
Android, View çizimini ana iş parçacığından (UI thread) çağrılan sıkı bir şekilde tanımlanmış bir sırayla gerçekleştirir. Tam döngü üç aşamadan oluşur: measure (boyut ölçme), layout (konum hesaplama) ve draw (oluşturma). onDraw, draw aşamasının bir parçasıdır ve View’in boyutları ve konumu belirlendikten sonra çağrılır.
draw(Canvas canvas), onDraw ve dispatchDraw’ı çağıran View’in genel bir metodudur. draw metodu geçersiz kılınmak için tasarlanmamıştır — bunun yerine onDraw geçersiz kılınır. Geçerli View için onDraw çalıştırıldıktan sonra, dispatchDraw çağrılır ve bu da tüm alt View’ler için (geçerli View bir ViewGroup kapsayıcısıysa) draw’ı yinelemeli olarak çağırır.
onDraw şu koşullarda çağrılır: 1) View ilk kez görünür hale gelir (setVisibility(VISIBLE) sonrası); 2) uygulama bu View veya üst öğesi üzerinde invalidate() çağırır; 3) View’in boyutu değişir (yeni layout); 4) View ile ilişkili bir animasyon değişir (ValueAnimator veya ObjectAnimator aracılığıyla); 5) View’in durumu değişir (pressed, focused, enabled).
Android 3.0 (API 11)’den itibaren çoğu Canvas işlemi GPU aracılığıyla donanım hızlandırmalı olarak gerçekleştirilir. Geliştirici, canvas.isHardwareAccelerated() ile hızlandırmanın etkin olup olmadığını kontrol edebilir. Donanım hızlandırmasıyla bazı Canvas işlemleri desteklenmez (örn. dikdörtgen olmayan bir bölgeyle clipPath) ve görmezden gelinebilir veya hatalara neden olabilir.
Canvas, zengin bir çizim metotları kümesi sağlar. Tüm metotlar, dönüştürmeler (translate, rotate, scale, skew) aracılığıyla değiştirilebilen geçerli koordinat sisteminde çalışır. Her metot, renk, stil, çizgi kalınlığı, efektler ve kenar yumuşatmayı tanımlayan bir Paint nesnesini kabul eder.
| Canvas Metodu | Amaç | Anahtar Paint Parametreleri |
|---|---|---|
| drawCircle | Daire çizmek | centerX, centerY, radius, paint |
| drawRect | Dikdörtgen çizmek | left, top, right, bottom, paint |
| drawLine | Çizgi çizmek | startX, startY, endX, endY, paint |
| drawPath | Rastgele yol çizmek | path, paint |
| drawBitmap | Bitmap görüntüsü çizmek | bitmap, srcRect, dstRect, paint |
| drawText | Metin çizmek | text, x, y, paint |
| drawArc | Yay veya sektör çizmek | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Oval çizmek | ovalRect, paint |
Paint, çizim stilini tanımlar: renk (setColor), çizgi kalınlığı (setStrokeWidth), dolum stili (setStyle — FILL, STROKE, FILL_AND_STROKE), kenar yumuşatma (setAntiAlias), efektler (setShadowLayer, setMaskFilter) ve gradyanlar (setShader). Çizim döngüsünde nesne oluşturmayı önlemek için Paint’in onDraw içinde değil, View yapıcısında bir kez oluşturulması ve yapılandırılması önerilir.
Canvas, X ekseninin sağa ve Y ekseninin aşağıyı gösterdiği bir koordinat sistemi kullanır. Orijin (0, 0), View’in sol üst köşesidir. Çizim metotlarını çağırmadan önce, geliştirici canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py) aracılığıyla koordinat sistemini değiştirebilir. Sonraki tüm çizim işlemleri dönüştürülmüş sistemde gerçekleştirilecektir.
onDraw, ana UI iş parçacığında yürütülür ve süresi doğrudan kare hızını (fps) etkiler. 60 fps’i korumak için onDraw metodunun 16 milisaniye içinde tamamlanması gerekir. Herhangi bir yavaşlama, kare düşmelerine (jank) yol açar ve bu görsel olarak animasyon takılması veya kaydırma gecikmesi olarak algılanır.
Ana kurallar onDraw optimizasyonu için şunları içerir: 1) onDraw içinde nesne oluşturmayın — tüm Paint, Path, Rect nesneleri sınıf alanları olmalıdır; 2) bellek ayırmayın (new) — bu, çöp toplamayı (GC) ve kare düşmelerini tetikler; 3) karmaşık işlemleri Bitmap.createBitmap aracılığıyla Bitmap’te önbelleğe alın ve hazırlanan Bitmap’i çizin; 4) G/Ç işlemleri veya kaynak yükleme yapmayın.
clipRect, çizim alanını belirtilen bir dikdörtgenle sınırlar. Canvas’ın görünür alanın dışındaki pikselleri çizerek zaman kaybetmesini önlemek için kullanılır. Özellikle büyük listeleri kaydırırken veya yakınlaştırma yaparken etkilidir: clipRect’i görünür alana (dirty rect) ayarlayın; Canvas yalnızca görünür olanı çizecektir.
canvas.save(), geçerli Canvas durumunu (dönüştürme ve clip) bir yığına kaydeder. canvas.restore(), son kaydedilen durumu geri yükler. Geçici dönüştürmeler için gereklidir: durumu kaydedin, translate/rotate uygulayın, çizin, ardından geri yükleyin. save ve restore hafiftir ve makul kullanımda (10–15 iç içe geçme seviyesinden az) performansı etkilemez.
invalidate(), sisteme View içeriğinin güncel olmadığını ve yeniden çizilmesi gerektiğini bildiren bir View metodudur. invalidate çağrısı, onDraw’ın çağrılacağı yeni bir draw döngüsü başlatır. invalidate yalnızca UI iş parçacığından (ana iş parçacığı) çağrılmalıdır. Bir arka plan iş parçacığından çağırmak için postInvalidate kullanılır.
postInvalidate(), invalidate’in iş parçacığı güvenli bir sürümüdür ve Handler aracılığıyla ana iş parçacığının kuyruğuna yeniden çizim isteği gönderir. Veri yükleme veya hesaplamalar tamamlandıktan sonra View’in güncellenmesi gerektiğinde arka plan görevlerinde kullanılır. UI senkronizasyonu için runOnUiThread ile birleştirilebilir.
Kısmi yeniden çizim için invalidate(Rect dirty) veya invalidate(int l, int t, int r, int b) kullanın. Bu, sisteme tüm View’in değil, yalnızca belirtilen alanın değiştiğini bildirir. Android, yalnızca dirty rect’i yeniden çizerek oluşturmayı optimize eder ve kısmi güncellemeleri olan animasyonların performansını önemli ölçüde artırır.
Kotlin kullanarak Android’de özel View’ler oluşturmak için onDraw’ı geçersiz kılmanın pratik örneklerine bakalım.
Bu örnek, animasyonlu dolgulu bir dairesel ilerleme göstergesi oluşturur. onDraw iki daire çizer: gri bir arka plan dairesi ve yayı geçerli ilerlemeye göre belirlenen renkli bir dolum dairesi. setProgress içinde invalidate çağırmak, değer değiştiğinde yeniden çizimi sağlar.
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)
}
}
Bu örnek, bir gradyanlı dikdörtgen ve üstünde metin çizer. Gradyan LinearGradient (Shader) aracılığıyla, metin ise drawText aracılığıyla oluşturulur. Her iki Paint nesnesi de yapıcıda bir kez oluşturulurken, onDraw yalnızca mevcut View genişliğine göre dikdörtgen boyutunu günceller.
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("Hello from Canvas",
xCenter, yCenter - yOffset,
textPaint)
}
}
Bu örnek, çizimi her 1000 milisaniyede bir güncelleyen bir animasyonlu saat oluşturur. onDraw içindeki postDelayed, bir saniye sonra invalidate çağırarak sonsuz bir yeniden çizim döngüsü oluşturur. Saat kolları, farklı uzunluk ve kalınlıklarda drawLine ile çizilir.
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)
}
}
Sıkça Sorulan Sorular
Hayır, onDraw doğrudan çağrılmamalıdır. Yeniden çizim istemek için invalidate() (UI iş parçacığından) veya postInvalidate() (arka plan iş parçacığından) kullanın. Sistem, onDraw’ı ne zaman çağıracağına karar verecek ve birden çok invalidate çağrısını tek bir geçişte birleştirerek çizimi optimize edecektir.
Nedenleri: View gizli (setVisibility(GONE) veya INVISIBLE); View Window hiyerarşisine eklenmemiş; View boyutu 0 (genişlik veya yükseklik = 0); setWillNotDraw(true) çağrılmış (ViewGroup için varsayılan true). Hata ayıklarken bu noktaların her birini kontrol edin.
60 fps’de onDraw saniyede 60 defaya kadar çağrılabilir. 120 fps’de (120 Hz ekranlı cihazlarda) — 120 defaya kadar. Her çağrı yaklaşık 8–16 milisaniye içinde tamamlanmalıdır. ObjectAnimator veya ValueAnimator aracılığıyla aktif animasyonlar sırasında sıklık, animasyon kare sayısına göre belirlenir.
onDraw, View’in kendi içeriğini çizmekten sorumludur. dispatchDraw, alt View’leri (ViewGroup içinde) çizmekten sorumludur. dispatchDraw, onDraw’dan sonra çağrılır ve tüm alt öğeleri yinelemeli olarak dolaşır. Normal bir View (ViewGroup değil) için dispatchDraw boştur.
Evet, Canvas(bitmap) aracılığıyla bir Bitmap için Canvas oluşturulabilir ve herhangi bir iş parçacığında üzerine çizim yapılabilir. Bu, arka planda karmaşık grafikleri önceden oluşturmak için kullanılır. Ancak, onDraw’a iletilen Canvas sisteme aittir ve yalnızca onDraw metodu içinde UI iş parçacığında kullanılabilir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun