Canvas در Android — کلاسی از بسته android.graphics است که API برای رسم 2D روی View فراهم میکند. Canvas مانند یک بوم کار میکند که توسعهدهنده روی آن اشکال هندسی، متن، تصاویر و مسیرها را با استفاده از متدهای مختلف draw* رسم میکند. طبق Android Developers Documentation (2025)، Canvas ابزار اصلی برای رسم سفارشی در Android است و همراه با کلاس Paint برای تنظیم سبکها و رنگها استفاده میشود. هر فراخوانی onDraw() یک نمونه آماده Canvas دریافت میکند که به بافر بیتمپ نمایش مرتبط است.
نکات اصلی
Canvas — کلاس مرکزی گرافیک 2D در Android است که متدهایی برای رسم روی صفحه فراهم میکند. هر Canvas به Bitmap متصل است — ناحیه حافظهای که نتایج عملیات رسم در آن نوشته میشود. وقتی View دوباره ترسیم میشود، سیستم به onDraw() یک Canvas که از قبل به بافر این View متصل است منتقل میکند.
Canvas در دستگاه مختصاتی کار میکند که (0,0) گوشه بالا سمت چپ است، محور X به راست و محور Y به پایین میرود. تمام عملیات رسم از وضعیت فعلی Canvas شامل ماتریس تبدیل، ناحیه برش (clip) و سبک Paint استفاده میکنند. طبق Android Documentation (2025)، API Canvas شامل بیش از 30 متد draw* برای انواع مختلف اولیههای گرافیکی است.
از Android 3.0 (API 11) Canvas از تسریع سختافزاری از طریق DisplayList پشتیبانی میکند. در حالت تسریعشده، عملیات Canvas در DisplayList ذخیره شده و سپس روی GPU اجرا میشوند. این افزایش قابل توجهی در عملکرد ایجاد میکند اما محدودیتهایی اعمال میکند: برخی عملیات Canvas (clipPath با کلیپ غیر Path) در حالت تسریعشده پشتیبانی نمیشوند.
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 — کلاسی است که تعیین میکند Canvas چگونه رسم کند. بدون Paint متدهای Canvas بیفایده هستند — آن رنگ، ضخامت خط، سبک پر کردن، اندازه متن، افکتها (سایه، گرادینت، تارشدن) و حالت ترکیب را تعیین میکند.
Paint ویژگیهای کلیدی دارد: color (رنگ)، style (FILL، STROKE یا FILL_AND_STROKE)، strokeWidth (ضخامت خط)، textSize (اندازه متن)، isAntiAlias (صافسازی)، shader (گرادینت یا تصویر). ایجاد شی Paint یک عملیات پرهزینه است، بنابراین باید یک بار ایجاد و مجدداً استفاده شود.
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 یک روش استاندارد است که امکان مدیریت انعطافپذیر ظاهر را بدون بازآفرینی اشیا فراهم میکند.
برای پر کردن گرادینت، Paint یک Shader دریافت میکند — زیرکلاسهای LinearGradient، RadialGradient، SweepGradient. Shader از طریق متد setShader() به Paint متصل میشود و تعیین میکند که رنگ چگونه در ناحیه رسم تغییر کند.
Canvas از تبدیلات ماتریسی دستگاه مختصات پشتیبانی میکند: translate() (انتقال)، rotate() (چرخش)، scale() (مقیاس) و skew() (کج کردن). تبدیلات انباشته میشوند: هر فراخوانی بعدی روی قبلی اعمال میشود.
مدیریت پشته تبدیلات از طریق save() و restore() انجام میشود. save() وضعیت فعلی Canvas (ماتریس، clip) را در پشته ذخیره میکند، restore() آخرین وضعیت ذخیرهشده را بازیابی میکند. بین save() و restore() میتوان تبدیلات تو در تو داشت.
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 ترکیب میشود. Bitmap برای بارگذاری تصاویر و کش کردن فریمهای رندر شده استفاده میشود. Path — برای ساخت منحنیهای دلخواه Bezier، چندضلعیها و خطوط پیچیده.
تکنیک رندرینگ خارج از صفحه: یک Bitmap ایجاد میشود، یک صحنه پیچیده روی آن از طریق Canvas جداگانه رسم میشود و سپس Bitmap آماده در onDraw() از طریق drawBitmap() نمایش داده میشود. این امکان بهروزرسانی گرافیک پیچیده را بدون بازترسیم کامل هر فریم فراهم میکند.
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 مستقیماً بر FPS برنامه تأثیر میگذارد. مشکلات اصلی: تخصیصهای اضافی در onDraw()، invalidate() مکرر و استفاده غیربهینه از تسریع سختافزاری.
Paint، Path، Rect، RectF و سایر اشیا باید یک بار در مقداردهنده یا متد setup() ایجاد شوند. هر تخصیص در onDraw() بار garbage collector را افزایش میدهد که منجر به میکرو لگ میشود. استثنا — static یا companion object برای اشیای immutable.
اگر بخش بزرگی از View در خارج از ناحیه قابل مشاهده پنهان است (مثلاً در ScrollView)، از canvas.clipRect() برای محدود کردن رسم استفاده کنید. تسریع سختافزاری در این حالت برش پیکسلهای نامرئی را بهینه میکند.
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(Bitmap) — سازندهای که Bitmap میگیرد ایجاد کرد. این برای رندرینگ خارج از صفحه استفاده میشود: روی Bitmap رسم میکنید و سپس آن را از طریق drawBitmap() نمایش میدهید. همچنین Canvas میتواند از Surface (برای SurfaceView) یا از طریق Picture ایجاد شود.
Canvas — API برای رسم روی View در رشته UI است. SurfaceView — یک Surface جداگانه است که میتواند از رشته پسزمینه از طریق Canvas خود (lockCanvas() / unlockCanvasAndPost()) بهروزرسانی شود. SurfaceView برای بازیها و ویدیو ترجیح داده میشود.
رایجترین دلیل — save() و restore() نامتعادل. اگر بعد از تبدیل (translate، rotate) restore() فراخوانی نشود، Canvas در دستگاه مختصات تغییر یافته باقی میماند و عناصر بعدی با جابجایی رسم میشوند. جفتهای save()/restore() را بررسی کنید.
برای تراز افقی از Paint.setTextAlign(Paint.Align.CENTER) و برای عمودی از Paint.getTextBounds() یا Paint.descent() / ascent() استفاده کنید. مرکز: canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint).
Canvas خودش انیمیشن نمیسازد — فقط وضعیت فعلی را رسم میکند. انیمیشن از طریق فراخوانیهای متوالی invalidate() (یا postInvalidateOnAnimation()) به دست میآید و هر بار یک فریم جدید در onDraw() رسم میکند. برای انیمیشنهای روان از ValueAnimator یا Choreographer برای هماهنگسازی با Vsync استفاده کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید