Canvas در Android: چیست، کلاس Canvas و متدهای draw

نویسنده: IT Sectr منتشر شده: 2026-07-21 زمان مطالعه: 7 دقیقه

Canvas در Android — کلاسی از بسته android.graphics است که API برای رسم 2D روی View فراهم می‌کند. Canvas مانند یک بوم کار می‌کند که توسعه‌دهنده روی آن اشکال هندسی، متن، تصاویر و مسیرها را با استفاده از متدهای مختلف draw* رسم می‌کند. طبق Android Developers Documentation (2025)، Canvas ابزار اصلی برای رسم سفارشی در Android است و همراه با کلاس Paint برای تنظیم سبک‌ها و رنگ‌ها استفاده می‌شود. هر فراخوانی onDraw() یک نمونه آماده Canvas دریافت می‌کند که به بافر بیت‌مپ نمایش مرتبط است.

نکات اصلی

  • Canvas — کلاس رسم 2D در Android، متدهای drawRect، drawCircle، drawText، drawBitmap و دیگران را فراهم می‌کند
  • Paint — کلاسی که سبک رسم را تعیین می‌کند: رنگ، ضخامت، پر کردن، افکت‌ها (سایه، گرادینت)
  • onDraw(Canvas) — متد View که در آن تمام رسم از طریق Canvas دریافت شده انجام می‌شود
  • Canvas.save() و restore() — مدیریت پشته تبدیلات برای تغییرات تو در توی دستگاه مختصات
  • Path — کلاس ساخت خطوط و منحنی‌های دلخواه، به drawPath(Path, Paint) منتقل می‌شود

Canvas در Android چیست

Canvas — کلاس مرکزی گرافیک 2D در Android است که متدهایی برای رسم روی صفحه فراهم می‌کند. هر Canvas به Bitmap متصل است — ناحیه حافظه‌ای که نتایج عملیات رسم در آن نوشته می‌شود. وقتی View دوباره ترسیم می‌شود، سیستم به onDraw() یک Canvas که از قبل به بافر این View متصل است منتقل می‌کند.

Canvas در دستگاه مختصاتی کار می‌کند که (0,0) گوشه بالا سمت چپ است، محور X به راست و محور Y به پایین می‌رود. تمام عملیات رسم از وضعیت فعلی Canvas شامل ماتریس تبدیل، ناحیه برش (clip) و سبک Paint استفاده می‌کنند. طبق Android Documentation (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 استفاده شده است: یکی برای پر کردن دایره با رنگ بنفش و دیگری برای خط دور سفید. تقسیم به fill و stroke یک روش استاندارد است که امکان مدیریت انعطاف‌پذیر ظاهر را بدون بازآفرینی اشیا فراهم می‌کند.

Shader و گرادینت‌ها

برای پر کردن گرادینت، Paint یک Shader دریافت می‌کند — زیرکلاس‌های LinearGradient، RadialGradient، SweepGradient. Shader از طریق متد setShader() به Paint متصل می‌شود و تعیین می‌کند که رنگ چگونه در ناحیه رسم تغییر کند.

تبدیلات Canvas: translate، rotate، scale

Canvas از تبدیلات ماتریسی دستگاه مختصات پشتیبانی می‌کند: translate() (انتقال)، rotate() (چرخش)، scale() (مقیاس) و skew() (کج کردن). تبدیلات انباشته می‌شوند: هر فراخوانی بعدی روی قبلی اعمال می‌شود.

مدیریت پشته تبدیلات از طریق save() و restore() انجام می‌شود. save() وضعیت فعلی Canvas (ماتریس، clip) را در پشته ذخیره می‌کند، restore() آخرین وضعیت ذخیره‌شده را بازیابی می‌کند. بین save() و restore() می‌توان تبدیلات تو در تو داشت.

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

    canvas.save() // ذخیره وضعیت اولیه
    canvas.translate(100f, 100f) // انتقال مبدأ

    // مربع اول
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // ذخیره پس از انتقال
    canvas.rotate(45f, 25f, 25f) // چرخش حول مرکز مربع

    // مربع چرخیده
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // بازیابی پس از چرخش

    canvas.restore() // بازیابی وضعیت اولیه
}

همیشه قبل از شروع تبدیلات از save() و پس از اتمام آنها از restore() استفاده کنید. save/restore نامتعادل منجر به انباشته شدن ماتریس و رسم نادرست عناصر بعدی می‌شود.

Canvas با Bitmap و Path برای گرافیک پیچیده

برای گرافیک پیچیده، Canvas با Bitmap و Path ترکیب می‌شود. Bitmap برای بارگذاری تصاویر و کش کردن فریم‌های رندر شده استفاده می‌شود. Path — برای ساخت منحنی‌های دلخواه Bezier، چندضلعی‌ها و خطوط پیچیده.

بافر 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)

    // رندر صحنه یک بار
    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 برای ساخت خطوط پیچیده استفاده می‌شود: منحنی‌های Bezier (quadTo، cubicTo)، کمان‌ها (arcTo)، خطوط (lineTo). Path می‌تواند بسته (close()) برای اشکال پر شده یا باز برای خطوط باشد. متدهای اصلی drawPath() Path و Paint را برای رسم نهایی ترکیب می‌کنند.

بهینه‌سازی رسم Canvas

عملکرد رسم Canvas مستقیماً بر FPS برنامه تأثیر می‌گذارد. مشکلات اصلی: تخصیص‌های اضافی در onDraw()، invalidate() مکرر و استفاده غیربهینه از تسریع سخت‌افزاری.

اشیاء را خارج از onDraw() ایجاد کنید

Paint، Path، Rect، RectF و سایر اشیا باید یک بار در مقداردهنده یا متد setup() ایجاد شوند. هر تخصیص در onDraw() بار garbage collector را افزایش می‌دهد که منجر به میکرو لگ می‌شود. استثنا — static یا companion object برای اشیای immutable.

برای محدود کردن ناحیه از clipRect() استفاده کنید

اگر بخش بزرگی از View در خارج از ناحیه قابل مشاهده پنهان است (مثلاً در ScrollView)، از canvas.clipRect() برای محدود کردن رسم استفاده کنید. تسریع سخت‌افزاری در این حالت برش پیکسل‌های نامرئی را بهینه می‌کند.

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

    // برش به ناحیه قابل مشاهده
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // رندر صحنه بزرگ
    renderScene(canvas)
    canvas.restore()
}

برای View با گرافیک Canvas پیچیده که به‌ندرت به‌روزرسانی می‌شود از View.setLayerType() با LAYER_TYPE_HARDWARE استفاده کنید. برای انیمیشن‌های مکرر به‌روزرسانی‌شونده از 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 در رشته UI است. SurfaceView — یک Surface جداگانه است که می‌تواند از رشته پس‌زمینه از طریق Canvas خود (lockCanvas() / unlockCanvasAndPost()) به‌روزرسانی شود. SurfaceView برای بازی‌ها و ویدیو ترجیح داده می‌شود.

چرا Canvas بعد از تبدیل رسم نمی‌کند؟

رایج‌ترین دلیل — save() و restore() نامتعادل. اگر بعد از تبدیل (translate، rotate) restore() فراخوانی نشود، Canvas در دستگاه مختصات تغییر یافته باقی می‌ماند و عناصر بعدی با جابجایی رسم می‌شوند. جفت‌های save()/restore() را بررسی کنید.

چگونه متن را در مرکز Canvas رسم کنیم؟

برای تراز افقی از Paint.setTextAlign(Paint.Align.CENTER) و برای عمودی از Paint.getTextBounds() یا Paint.descent() / ascent() استفاده کنید. مرکز: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).

آیا Canvas از انیمیشن پشتیبانی می‌کند؟

Canvas خودش انیمیشن نمی‌سازد — فقط وضعیت فعلی را رسم می‌کند. انیمیشن از طریق فراخوانی‌های متوالی invalidate() (یا postInvalidateOnAnimation()) به دست می‌آید و هر بار یک فریم جدید در onDraw() رسم می‌کند. برای انیمیشن‌های روان از ValueAnimator یا Choreographer برای هماهنگ‌سازی با Vsync استفاده کنید.

خلاصه

  • Canvas — کلاس مرکزی رسم 2D در Android، متدهای draw* برای رسم اولیه‌ها، متن و تصاویر فراهم می‌کند
  • Paint — سبک بصری را تعیین می‌کند (رنگ، ضخامت، پر کردن، شیدرها)؛ یک بار ایجاد و مجدداً استفاده می‌شود
  • save() / restore() — مدیریت پشته تبدیلات؛ هر تبدیل باید در جفت save/restore باشد
  • Path — ساخت خطوط و منحنی‌های دلخواه Bezier برای گرافیک برداری پیچیده
  • Offscreen Bitmap — تکنیک کش کردن صحنه‌های پیچیده از طریق Canvas(Bitmap) برای کاهش بار onDraw()
  • تسریع سخت‌افزاری — شتاب GPU برای Canvas از Android 3.0، محدودیت‌هایی بر برخی عملیات اعمال می‌کند (clipPath)
  • پروفایلینگ — GPU Profile Rendering و Android Studio Profiler برای یافتن تنگناها در onDraw() ضروری هستند

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید