Paint — کلاس اصلی برای استایلدهی گرافیک در Android Canvas است. این کلاس رنگ، ضخامت خط، استایل پر کردن، افکتهای محو و فونتها را برای تمام عملیات نقاشی در رابط کاربری تعیین میکند. بدون Paint، Canvas نمیتواند ویژگیهای بصری را به عناصر نمایش داده شده اعمال کند. به گفته Google Developers، 2026، Paint تا 42 پارامتر استایل از جمله گرادینتها و بافتها را پشتیبانی میکند.
نکات اصلی
Paint — کلاسی از بسته android.graphics است که تمام تنظیمات استایل را برای عملیات نقاشی روی Canvas ذخیره میکند. این کلاس خود پیکسلها را شامل نمیشود، بلکه تعیین میکند خطوط، اشکال و متن چگونه به نظر برسند. هر فراخوانی متد Canvas — drawCircle، drawLine، drawText — از Paint برای تفسیر نمایش بصری استفاده میکند.
Paint بر اساس اصل شی پیکربندی کار میکند. شما یک نمونه ایجاد میکنید، پارامترها را یک بار تنظیم میکنید و آن را برای عملیات متعدد استفاده مجدد میکنید. این کار تعداد تخصیصها در حلقه رسم را به حداقل میرساند و عملکرد را تا 30٪ در مقایسه با ایجاد یک شی جدید در هر فریم افزایش میدهد. به گفته Google I/O 2024، بهینهسازی Paint یکی از روشهای کلیدی برای دستیابی به 60 FPS در گرافیک سفارشی است.
تمام تنظیمات Paint به چند دسته تقسیم میشوند: رنگ و شفافیت، استایل خط و پر کردن، کار با متن و فونتها، افکتهای محو و ماسکگذاری. هر دسته توسط متدهای setter جداگانهای مدیریت میشود که میتوان آنها را در هر ترکیبی با هم به کار برد. این باعث میشود API Paint انعطافپذیر باشد، اما نیاز به درک تعامل پارامترها دارد — برای مثال، Style تأثیر میگذارد که Shader روی خط دور یا پر کردن اعمال شود.
setColor — روش اصلی تنظیم رنگ برای تمام عملیات نقاشی بعدی است. رنگ در قالب ARGB (عدد صحیح 32 بیتی) منتقل میشود، که در آن 8 بیت بالایی کانال آلفا و بقیه کانالهای قرمز، سبز و آبی هستند. مقدار 0xFF0000FF یک رنگ آبی غیرشفاف تنظیم میکند.
متد setAlpha امکان تنظیم مستقل شفافیت را در محدوده 0 (کاملاً شفاف) تا 255 (کاملاً غیرشفاف) فراهم میکند. برخلاف مشخص کردن آلفا در setColor، این متد بر روی رنگ قبلاً تنظیم شده تأثیر نمیگذارد — فقط شفافیت کلی را تعدیل میکند. این برای انیمیشنهای ظاهر و ناپدید شدن عناصر بدون تغییر رنگ مفید است.
ColorFilter — مکانیزم تبدیل رنگ هر پیکسل قبل از رسم است. سه حالت پشتیبانی میشود: LightingColorFilter برای ضرب و جمع کانالها، PorterDuffColorFilter برای روی همگذاری با استفاده از حالتهای ترکیب و ColorMatrixColorFilter برای تبدیل ماتریسی کامل. ColorFilter بعد از Shader اما قبل از خروجی به Canvas اعمال میشود.
برای بیشتر کارها، setColor یا setAlpha استاندارد کافی است. ColorFilter عمدتاً برای افکتهای بیرنگ کردن، سپیا یا معکوس کردن استفاده میشود. استفاده از ColorFilter بار GPU را افزایش میدهد، بنابراین Google توصیه میکند استفاده از آن را در فراخوانیهای چرخهای onDraw به حداقل برسانید.
| متد | توضیح | عملکرد |
|---|---|---|
| setColor | تنظیم رنگ در ARGB | O(1) — بدون سربار |
| setAlpha | تنظیم شفافیت (0-255) | O(1) — عملیات سبک |
| setColorFilter | اعمال تصحیح رنگ | O(n) — تأثیر بر FPS در فراخوانیهای مکرر |
متد setStyle یکی از سه مقدار Paint.Style را میپذیرد: FILL (پر کردن)، STROKE (خط دور) یا FILL_AND_STROKE (همزمان). بسته به انتخاب استایل، شکل به صورت سیلوئت، اسکلت یا خط دور با پر کردن داخلی نمایش داده میشود. Paint به طور پیشفرض با استایل FILL ایجاد میشود.
setStrokeWidth ضخامت خط را بر حسب پیکسل برای حالت STROKE تنظیم میکند. برای اشکال گرد و گوشههای گرد از setStrokeCap (Cap.ROUND، BUTT، SQUARE) و setStrokeJoin (Join.MITER، ROUND، BEVEL) استفاده میشود. این پارامترها تأثیر مهمی بر ظاهر نمودارها، مسیرها و قابها در Viewهای سفارشی دارند.
در ضخامت کمتر از 1 پیکسل، خط با دقت زیرپیکسلی رسم میشود، اما ممکن است در صفحههای با تراکم پایین تار به نظر برسد. برای نمایشگرهای Retina Android از TypedValue.applyDimension برای تبدیل dp به پیکسل استفاده کنید. ضخامت 2 dp دید واضحی را در بیشتر دستگاهها فراهم میکند.
DashPathEffect خطوط نقطهچین و خطچین ایجاد میکند. سازنده آرایهای از طولهای خطچینها و فاصلههای متناوب و شیفت فاز اولیه را میپذیرد. به عنوان مثال، آرایه [10, 5, 3, 5] یک خط با خطچین بلند، فاصله کوتاه، خطچین کوتاه و فاصله ایجاد میکند. این افکت در ویرایشگرهای گرافیکی و ابزارهای حاشیهنویسی استفاده میشود.
ترکیب استایلها از طریق setPathEffect انجام میشود. با ترکیب CornerPathEffect، DashPathEffect و DiscretePathEffect میتوان خطوط بصری پیچیدهای برای نمودارها، زیرنویسها و عناصر تزئینی ایجاد کرد. هر PathEffect روی CPU محاسبه میشود، بنابراین در انیمیشنهای با تعداد زیادی خط توصیه میشود افکت را از قبل در Bitmap rasterize کنید.
val paint = Paint().apply {
setStyle(Paint.Style.STROKE)
setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2f, resources.displayMetrics))
setPathEffect(DashPathEffect(floatArrayOf(10f, 5f, 3f, 5f), 0f))
}
Typeface — شیئی که قلم متن را برای تمام عملیات متنی Paint تعیین میکند. Android از فونتهای سیستمی (Roboto، Noto Sans)، فونتهای سفارشی از منابع assets و فونتهای قابل بارگیری از طریق Downloadable Fonts API پشتیبانی میکند. تنظیم Typeface از طریق setTypeface روی نمونه Paint انجام میشود.
متد setTextSize اندازه قلم را بر حسب پیکسل تنظیم میکند. برای نمایش صحیح در صفحههای مختلف توصیه میشود اندازه را با استفاده از TypedValue.applyDimension بر حسب sp مشخص کنید. فاصله بین خطوط با setTextScaleX برای کشش افقی تنظیم میشود و در پشتیبانی داخلی متن MultiLine از طریق Canvas.drawText گنجانده شده است.
برای تراز کردن متن از setTextAlign با مقادیر LEFT، CENTER و RIGHT استفاده میشود. در CENTER، مختصات x ارسال شده به drawText به نقطه مرکزی متن تبدیل میشود — این به ویژه برای زیرنویسهای زیر آیکونها و عنوانهای داخل اشکال مفید است. برای موقعیتیابی دقیق از متدهای measureText و getTextBounds استفاده میشود که عرض و مستطیل مرزهای متن را برمیگردانند.
setUnderlineText و setStrikeThruText به ترتیب زیرخط و خط خورده را فعال میکنند. این پارامترها در سطح قلم کار میکنند و به کل متنی که توسط Paint رسم میشود اعمال میگردند. برای قالببندی منعطف بخشهای مختلف یک خط، از اشیاء Paint جداگانه با تنظیمات مختلف استفاده کنید یا پارامترها را بین فراخوانیهای drawText تغییر دهید.
| متد | هدف | واحد اندازهگیری |
|---|---|---|
| setTextSize | اندازه قلم | px (پیکسل) |
| setTextAlign | تراز افقی | LEFT, CENTER, RIGHT |
| setTypeface | قلم | Typeface object |
| setUnderlineText | زیرخط | Boolean |
Shader — کلاس پایه برای پر کردن اشکال با گرادینتها، بافتهای بیتمپ و ترکیبها. از طریق متد setShader میتوان هر یک از زیرکلاسها را تعیین کرد: LinearGradient، RadialGradient، SweepGradient، BitmapShader یا ComposeShader. Shader جایگزین رنگ تعیین شده توسط setColor میشود و به تمام عناصر رسم شده اعمال میگردد.
LinearGradient یک انتقال صاف بین دو یا چند رنگ در امتداد یک خط مستقیم ایجاد میکند. سازنده نقاط شروع و پایان، آرایه رنگها و موقعیتهای آنها (حالت کاشی) را میپذیرد. حالت کاشی رفتار خارج از گرادینت را تعیین میکند: CLAMP (تکرار آخرین رنگ)، MIRROR (بازتاب) یا REPEAT (تکرار). LinearGradient برای پرکنندههای پسزمینه و نوارهای پیشرفت استفاده میشود.
RadialGradient یک گرادینت دایرهای از مرکز به لبهها ایجاد میکند. مرکز با مختصات تعیین میشود، شعاع ناحیه انتقال رنگ را مشخص میکند. ظاهر گرادینت شعاعی شبیه نورپردازی از یک منبع نوری نقطهای است — این اغلب برای شبیهسازی درخشش دکمهها و نور پسزمینه عناصر فعال استفاده میشود.
BlurMaskFilter افکت محو را در اطراف عناصر رسم شده اضافه میکند. سازنده شعاع محو و سبک را میپذیرد: NORMAL (محو در همه جهات)، INNER (فقط داخل شکل)، OUTER (فقط خارج) و SOLID (شکل + محو خارجی). BlurMaskFilter با هر نوع Paint کار میکند و اغلب برای ایجاد سایهها و افکتهای درخشش استفاده میشود.
val paint = Paint().apply {
setShader(LinearGradient(
0f, 0f, width, height,
intArrayOf(Color.RED, Color.BLUE),
null,
Shader.TileMode.CLAMP
))
setMaskFilter(BlurMaskFilter(20f, BlurMaskFilter.Blur.NORMAL))
}
بیایید یک مثال کامل از View سفارشی را بررسی کنیم که یک دایره با پرکننده گرادینت و زیرنویس متنی رسم میکند. ترکیب Paint و Canvas امکان ایجاد گرافیک پیچیده با حداقل کد را فراهم میکند. تمام تنظیمات برای عملکرد به initialization منتقل شدهاند.
class GradientCircleView@JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
private val fillPaint = Paint().apply {
setShader(RadialGradient(
0f, 0f, 200f,
intArrayOf(Color.CYAN, Color.DKGRAY),
null, Shader.TileMode.CLAMP
))
}
private val textPaint = Paint().apply {
setColor(Color.WHITE)
setTextSize(TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 24f, resources.displayMetrics
))
setTextAlign(Paint.Align.CENTER)
setMaskFilter(BlurMaskFilter(5f, BlurMaskFilter.Blur.NORMAL))
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(200f, 200f, 150f, fillPaint)
canvas.drawText("گرادینت", 200f, 210f, textPaint)
}
}
در کد از دو شی Paint جداگانه استفاده شده است: یکی برای پر کردن دایره با گرادینت شعاعی، دومی برای متن با سایه از طریق BlurMaskFilter. این جداسازی امکان پیکربندی مستقل پارامترها و جلوگیری از بازنشانی وضعیت Paint بین رسم انواع مختلف عناصر را فراهم میکند. برای افزایش عملکرد، هر دو Paint در سازنده ایجاد میشوند، نه در onDraw.
در مقیاسبندی روی دستگاههایی با تراکم پیکسل متفاوت، مقادیر اندازه باید از طریق TypedValue.applyDimension دوباره محاسبه شوند. بدون این، متن و گرادینت در صفحههای mdpi و xxxhdpi متفاوت به نظر میرسند. در مثال، اندازه متن بر حسب sp تنظیم شده است که به طور خودکار با تنظیمات اندازه قلم سیستم کاربر تطبیق مییابد.
سوالات متداول
Canvas عملیات نقاشی (drawCircle، drawLine) را انجام میدهد و Paint استایل — رنگ، ضخامت، فونت را تعیین میکند. Canvas شامل تنظیمات استایل نیست، بنابراین بدون Paint هیچ عملیات نقاشی تأثیر بصری ندارد.
بله، یک شی Paint را میتوان برای چندین شکل استفاده مجدد کرد. توصیه میشود Paintهای جداگانه برای انواع مختلف عناصر (پسزمینه، متن، خط دور) ایجاد کنید، اما نه برای هر شکل — این کار بار روی garbage collector را کاهش میدهد.
متد reset Paint را به حالتی معادل یک نمونه جدید بازمیگرداند. به طور جایگزین میتوان از set با flag استفاده کرد، اما reset برای خوانایی کód ترجیح داده میشود. پس از بازنشانی، تمام پارامترهای استایل مقادیر پیشفرض میگیرند.
ایجاد Paint در داخل onDraw باعث تخصیص در هر فریم میشود که منجر به توقف GC میگردد. تمام اشیاء Paint باید در سازنده View ایجاد شوند. تغییر پارامترها در onDraw از طریق setterها ایمن است و اشیاء جدید ایجاد نمیکند.
متن در Android همیشه با پسزمینه شفاف رسم میشود. متد drawText فقط گلیفها را بدون مستطیل پسزمینه نمایش میدهد. اگر زیر متن نیاز به پسزمینه دارید، ابتدا یک مستطیل از طریق drawRoundRect با Paint جداگانه رسم کنید، سپس متن را رسم کنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید