Canvas في Android: ما هو، فئة Canvas وطرق الرسم

المؤلف: IT Sectr نُشر: 2026-07-21 وقت القراءة: 7 دق

Canvas في Android — هي فئة من حزمة android.graphics توفر API للتصيير ثنائي الأبعاد على View. يعمل Canvas كلوحة رسم يقوم المطور برسم الأشكال الهندسية والنصوص والصور والمسارات عليها باستخدام طرق draw* المختلفة. وفقًا لوثائق مطوري Android (2025)، فإن Canvas هو الأداة الرئيسية للتصيير المخصص في Android ويُستخدم مع فئة Paint لتعريف الأنماط والألوان. كل استدعاء لـ onDraw() يتلقى مثيل Canvas جاهزًا مرتبطًا بمخزن bitmap الخاص بالعرض.

النقاط الرئيسية

  • Canvas — فئة للتصيير ثنائي الأبعاد في Android، توفر طرق drawRect و drawCircle و drawText و drawBitmap وغيرها
  • Paint — فئة تحدد نمط التصيير: اللون، السمك، التعبئة، التأثيرات (الظل، التدرج)
  • onDraw(Canvas) — طريقة View حيث يحدث كل التصيير عبر Canvas المستلم
  • Canvas.save() و restore() — إدارة مكدس التحويلات للتحويلات المتداخلة لنظام الإحداثيات
  • Path — فئة لبناء الخطوط والمنحنيات التعسفية، تُمرر إلى drawPath(Path, Paint)

ما هو Canvas في Android

Canvas — هي الفئة المركزية للرسومات ثنائية الأبعاد في Android التي توفر طرقًا للرسم على سطح. كل Canvas مرتبط بـ Bitmap — منطقة ذاكرة تُسجل فيها نتائج عمليات الرسم. عندما يُعاد رسم View، يمرر النظام إلى onDraw() Canvas مرتبطًا بالفعل بمخزن تلك View.

يعمل Canvas في نظام إحداثيات حيث (0,0) هي الزاوية العليا اليسرى، المحور X يتجه لليمين، المحور Y يتجه للأسفل. جميع عمليات الرسم تستخدم الحالة الحالية لـ Canvas، بما في ذلك مصفوفة التحويل، منطقة القص ونمط Paint. وفقًا لوثائق Android (2025)، تتضمن API Canvas أكثر من 30 طريقة draw* لأنواع مختلفة من البدائيات الرسومية.

Canvas وتسريع الأجهزة

منذ Android 3.0 (API 11) يدعم Canvas تسريع الأجهزة عبر DisplayList. في الوضع المسرّع، تُسجل عمليات Canvas في DisplayList ثم تُنفذ على GPU. هذا يعطي زيادة كبيرة في الأداء لكنه يفرض قيودًا: بعض عمليات Canvas (clipPath مع قص غير Path) غير مدعومة في الوضع المسرّع.

طرق Canvas الأساسية للتصيير

يوفر Canvas مجموعة من الطرق لرسم البدائيات الأساسية. كل طريقة تقبل معلمات هندسية وكائن Paint يحدد النمط البصري.

الطريقةالغرضمثال
drawRect()رسم مستطيلcanvas.drawRect(10f, 10f, 100f, 100f, paint)
drawCircle()رسم دائرةcanvas.drawCircle(50f, 50f, 30f, paint)
drawLine()رسم خطcanvas.drawLine(0f, 0f, 100f, 100f, paint)
drawText()رسم نصcanvas.drawText(“Hello”, 10f, 20f, paint)
drawBitmap()رسم صورةcanvas.drawBitmap(bitmap, 0f, 0f, paint)
drawPath()رسم مسار تعسفيcanvas.drawPath(path, paint)
drawOval()رسم بيضاويcanvas.drawOval(rectF, paint)
drawArc()رسم قوس أو قطاعcanvas.drawArc(rectF, 0f, 90f, true, paint)

يمكن استدعاء كل طريقة بأي ترتيب — يرسم Canvas بترتيب الاستدعاءات، مكدسًا العناصر الجديدة فوق العناصر السابقة. للتكوينات المعقدة، تُستخدم تقنية التصيير متعدد الطبقات مع حفظ واستعادة حالة Canvas.

فئة Paint: تكوين الأنماط والتأثيرات

Paint — هي الفئة التي تحدد كيف سيرسم Canvas. بدون Paint، طرق Canvas عديمة الفائدة — فهي تحدد اللون، سمك الخط، نمط التعبئة، حجم النص، التأثيرات (الظل، التدرج، التمويه) ووضع المزج.

لـ Paint خصائص رئيسية: color (اللون)، style (FILL أو STROKE أو FILL_AND_STROKE)، strokeWidth (سمك الخط)، textSize (حجم النص)، isAntiAlias (تنعيم الحواف)، shader (التدرج أو الصورة). إنشاء كائن Paint عملية مكلفة، لذا يجب إنشاؤه مرة واحدة وإعادة استخدامه.

kotlin
private val fillPaint = Paint().apply {
    color = Color.parseColor("#FF6200EE")
    style = Paint.Style.FILL
    isAntiAlias = true
}

private val strokePaint = Paint().apply {
    color = Color.WHITE
    style = Paint.Style.STROKE
    strokeWidth = 4f
    isAntiAlias = true
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    canvas.drawCircle(100f, 100f, 80f, fillPaint)
    canvas.drawCircle(100f, 100f, 80f, strokePaint)
}

في هذا المثال، يُستخدم كائنان Paint: واحد لتعبئة الدائرة باللون الأرجواني، والثاني للحد الأبيض. فصل التعبئة والحد هو ممارسة قياسية تسمح بالتحكم المرن في المظهر دون إعادة إنشاء الكائنات.

Shader والتدرجات

للتعبئات المتدرجة، يقبل Paint كائن Shader — الفئات الفرعية LinearGradient و RadialGradient و SweepGradient. يُربط Shader بـ Paint عبر الطريقة setShader() ويحدد كيف يتغير اللون عبر منطقة الرسم.

تحويلات Canvas: translate، rotate، scale

يدعم Canvas تحويلات المصفوفة لنظام الإحداثيات: translate() (الإزاحة)، rotate() (التدوير)، scale() (التحجيم) و skew() (الإمالة). تتراكم التحويلات: كل استدعاء لاحق يُطبق فوق السابق.

تتم إدارة مكدس التحويلات عبر save() و restore(). تحفظ save() الحالة الحالية لـ Canvas (المصفوفة، القص) في المكدس، وتستعيد restore() آخر حالة محفوظة. بين save() و restore() يمكن تطبيق تحويلات متداخلة.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.save() // Save initial state
    canvas.translate(100f, 100f) // Translate origin

    // First square
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // Save after translate
    canvas.rotate(45f, 25f, 25f) // Rotate around square center

    // Rotated square
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // Restore after rotate

    canvas.restore() // Restore initial state
}

استخدم دائمًا save() قبل بدء التحويلات و restore() بعد اكتمالها. تؤدي أزواج save/restore غير المتوازنة إلى تراكم المصفوفة وتصيير غير صحيح للعناصر اللاحقة.

Canvas مع Bitmap و Path للرسومات المعقدة

للرسومات المعقدة، يُدمج Canvas مع Bitmap و Path. يُستخدم Bitmap لتحميل الصور وتخزين الإطارات المُصيّرة مؤقتًا. يُستخدم Path لبناء منحنيات بيزير التعسفية والمضلعات والخطوط المعقدة.

مخزن Bitmap خارج الشاشة

تقنية التصيير خارج الشاشة: يتم إنشاء Bitmap، ورسم مشهد معقد عليه عبر Canvas منفصل، ثم إخراج Bitmap النهائي في onDraw() عبر drawBitmap(). هذا يسمح بتحديث الرسومات المعقدة دون إعادة رسم كاملة في كل إطار.

kotlin
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null

fun buildOffscreenScene(width: Int, height: Int) {
    offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    offscreenCanvas = Canvas(offscreenBitmap)

    // Render scene once
    offscreenCanvas!!.drawColor(Color.WHITE)
    drawGrid(offscreenCanvas!!)
    drawComplexShape(offscreenCanvas!!)
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}

Path يُستخدم لبناء خطوط معقدة: منحنيات بيزير (quadTo، cubicTo)، الأقواس (arcTo)، الخطوط (lineTo). يمكن إغلاق Path (close()) للأشكال المملوءة أو فتحه للخطوط. الطريقة الرئيسية drawPath() تدمج Path و Paint للتصيير النهائي.

تحسين أداء تصيير Canvas

أداء تصيير Canvas يؤثر مباشرة على FPS للتطبيق. المشكلات الرئيسية: التخصيصات المفرطة في onDraw()، استدعاءات invalidate() المتكررة والاستخدام غير الأمثل لتسريع الأجهزة.

أنشئ الكائنات خارج onDraw()

يجب إنشاء Paint و Path و Rect و RectF والكائنات الأخرى مرة واحدة في المُهيئ أو طريقة setup(). كل تخصيص في onDraw() يخلق ضغطًا على جامع القمامة، مما يؤدي إلى تأخيرات دقيقة. الاستثناء هو static أو companion object للكائنات غير القابلة للتغيير.

استخدم clipRect() لتحديد المنطقة

إذا كان معظم View مخفيًا خارج المنطقة المرئية (مثلاً في ScrollView)، استخدم canvas.clipRect() لتحديد التصيير. تسريع الأجهزة في هذه الحالة يحسن additionally قص البكسلات غير المرئية.

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // Clip to visible area
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // Render large scene
    renderScene(canvas)
    canvas.restore()
}

استخدم View.setLayerType() مع LAYER_TYPE_HARDWARE لـ Views ذات الرسومات Canvas المعقدة التي تُحدّث نادرًا. للرسوم المتحركة التي تُحدّث بشكل متكرر، استخدم LAYER_TYPE_NONE لتجنب التحديث المكلف للطبقة المسرّعة. قم بالتحليل باستخدام GPU Profile Rendering و Android Studio Profiler.

الأسئلة الشائعة

هل يمكن إنشاء Canvas بدون View؟

نعم، يمكن إنشاء Canvas(Bitmap) — مُنشئ يقبل Bitmap. يُستخدم هذا للتصيير خارج الشاشة: ترسم على Bitmap ثم تخرجه عبر drawBitmap(). يمكن أيضًا إنشاء Canvas من Surface (لـ SurfaceView) أو عبر Picture.

ما الفرق بين Canvas و SurfaceView؟

Canvas — هو API للرسم على View في سلسلة واجهة المستخدم. SurfaceView — هو Surface منفصل يمكن تحديثه من سلسلة خلفية عبر Canvas الخاص به (lockCanvas() / unlockCanvasAndPost()). SurfaceView مفضل للألعاب والفيديو.

لماذا لا يرسم Canvas بعد التحويل؟

السبب الأكثر شيوعًا هو أزواج save() و restore() غير المتوازنة. إذا لم يتم استدعاء restore() بعد التحويل (translate، rotate)، يبقى Canvas في نظام إحداثيات معدّل، وتُرسَم العناصر اللاحقة مع إزاحة. تحقق من أزواج save()/restore().

كيف أرسم نصًا في وسط Canvas؟

استخدم Paint.setTextAlign(Paint.Align.CENTER) للمحاذاة الأفقية و Paint.getTextBounds() أو Paint.descent() / ascent() للعمودية. التوسيط: canvas.drawText(نص, centerX, centerY - (ascent + descent) / 2, paint).

هل يدعم Canvas الرسوم المتحركة؟

Canvas بحد ذاته لا يقوم بالتحريك — فهو يرسم الحالة الحالية فقط. يتم تحقيق الحركة من خلال استدعاءات متتابعة لـ invalidate() (أو postInvalidateOnAnimation())، مع رسم إطار جديد في onDraw() في كل مرة. للرسوم المتحركة السلسة استخدم ValueAnimator أو Choreographer للمزامنة مع Vsync.

الخلاصة

  • Canvas — الفئة المركزية للتصيير ثنائي الأبعاد في Android، توفر طرق draw* لرسم البدائيات والنصوص والصور
  • Paint — يحدد النمط البصري (اللون، السمك، التعبئة، shaders)؛ يُنشأ مرة واحدة ويعاد استخدامه
  • save() / restore() — إدارة مكدس التحويلات؛ كل تحويل يجب أن يكون في زوج save/restore
  • Path — بناء خطوط ومنحنيات بيزير تعسفية للرسومات المتجهة المعقدة
  • Offscreen Bitmap — تقنية تخزين المشاهد المعقدة مؤقتًا عبر Canvas(Bitmap) لتقليل الحمل على onDraw()
  • تسريع الأجهزة — تسريع GPU لـ Canvas منذ Android 3.0، يفرض قيودًا على بعض العمليات (clipPath)
  • التحليل — GPU Profile Rendering و Android Studio Profiler ضروريان لإيجاد الاختناقات في onDraw()

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا