این کد لبه بخشی از دوره Advanced Android in Kotlin است. اگر از طریق کدها به ترتیب کار کنید، بیشترین ارزش را از این دوره خواهید گرفت، اما اجباری نیست. همه کدهای دوره در صفحه فرود Android Advanced in Kotlin Codelabs فهرست شده اند.
مقدمه
برای هدف این نرم افزار کد، برش روشی برای تعریف مناطقی از یک تصویر، بوم، یا بیت مپ است که به طور انتخابی روی صفحه کشیده شده یا کشیده نمی شوند. یکی از اهداف کلیپینگ کاهش اضافه برداشت است. Overdraw زمانی است که یک پیکسل روی صفحه نمایش بیش از یک بار برای نمایش تصویر نهایی کشیده می شود. وقتی اضافه برداشت را کاهش می دهید، تعداد دفعاتی که یک پیکسل یا ناحیه نمایشگر کشیده می شود را به حداقل می رسانید تا عملکرد طراحی را به حداکثر برسانید. همچنین می توانید از clipping برای ایجاد افکت های جالب در طراحی رابط کاربری و انیمیشن استفاده کنید.
به عنوان مثال، هنگامی که یک پشته از کارتهای روی هم را مطابق شکل زیر میکشید، به جای کشیدن کامل هر کارت از پایین به بالا، معمولاً کشیدن فقط بخشهای قابل مشاهده کارآمدتر است. "معمولا"، زیرا عملیات برش هزینه نیز دارد و در کل سیستم اندروید بهینه سازی ترسیم زیادی انجام می دهد.

برای اینکه فقط قسمتهای قابل مشاهده کارتها را بکشید، برای هر کارت یک ناحیه برش مشخص میکنید. به عنوان مثال در نمودار زیر، هنگامی که یک مستطیل برش بر روی یک تصویر اعمال می شود، تنها بخشی از داخل آن مستطیل نمایش داده می شود.

منطقه برش معمولاً مستطیل است، اما می تواند هر شکل یا ترکیبی از اشکال، حتی متن باشد. همچنین میتوانید تعیین کنید که آیا میخواهید منطقه داخل منطقه برش گنجانده شود یا حذف شود. به عنوان مثال، می توانید یک منطقه برش دایره ای ایجاد کنید و فقط آنچه را که خارج از دایره است نمایش دهید.
در این کد لبه، شما می خواهید روش های مختلف برش را آزمایش کنید.
آنچه از قبل باید بدانید
باید با:
- نحوه ایجاد یک برنامه با
Activityو اجرای آن با استفاده از Android Studio. - نحوه ایجاد و طراحی روی
Canvas. - چگونه یک
Viewسفارشی ایجاد کنیم، وonDraw()وonSizeChanged()لغو کنیم.
چیزی که یاد خواهید گرفت
- نحوه برش دادن اشیا برای کشیدن روی
Canvas. - نحوه ذخیره و بازیابی حالت های طراحی یک بوم.
- نحوه اعمال تغییر شکل روی بوم و متن
کاری که خواهی کرد
- برنامه ای ایجاد کنید که اشکال بریده شده را روی صفحه بکشد و روش های مختلف برش و نتیجه آن را بر روی دید آن اشکال نشان دهد.
- مقداری متن ترجمه شده و اریب را نیز ترسیم خواهید کرد.
برنامه ClippingExample نشان می دهد که چگونه می توانید از اشکال استفاده و ترکیب کنید تا مشخص کنید کدام بخش از بوم در یک نما نمایش داده می شود. برنامه نهایی شما مانند تصویر زیر خواهد بود.

شما میخواهید این برنامه را از ابتدا بسازید، بنابراین باید یک پروژه راهاندازی کنید، ابعاد و رشتهها را تعریف کنید و برخی از متغیرها را اعلام کنید.
مرحله 1: پروژه ClippingExample را ایجاد کنید
- با قالب Empty Activity یک پروژه Kotlin به نام
ClippingExampleایجاد کنید. برای پیشوند نام بسته ازcom.example.androidاستفاده کنید. -
MainActivity.ktرا باز کنید. - در متد
onCreate()نمای پیش فرض محتوای را جایگزین کنید و نمای محتوا را روی نمونه جدیدی ازClippedViewتنظیم کنید. این نمای سفارشی شما برای نمونه های بریده ای است که در ادامه ایجاد خواهید کرد.
setContentView(ClippedView(this))- در همان سطح
MainActivity.kt، یک فایل Kotlin و کلاس جدید برای یک نمای سفارشی به نامClippedViewایجاد کنید کهViewگسترش می دهد. امضای زیر را به آن بدهید. بقیه کارهای شما همه داخل اینClippedViewخواهد بود. حاشیه نویسی@JvmOverloadsبه کامپایلر Kotlin دستور می دهد تا برای این تابع اضافه بارهایی ایجاد کند که جایگزین مقادیر پارامترهای پیش فرض می شود.
class ClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
}مرحله 2: ابعاد و منابع رشته را اضافه کنید
- ابعادی را که برای نماهای بریده شده استفاده می کنید در فایل منابع جدید در
res/values/dimens.xmlتعریف کنید. این ابعاد پیشفرض کدگذاری شده و اندازهبندی شدهاند تا روی یک صفحه نمایش بسیار کوچک قرار بگیرند.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">90dp</dimen>
<dimen name="clipRectBottom">90dp</dimen>
<dimen name="clipRectTop">0dp</dimen>
<dimen name="clipRectLeft">0dp</dimen>
<dimen name="rectInset">8dp</dimen>
<dimen name="smallRectOffset">40dp</dimen>
<dimen name="circleRadius">30dp</dimen>
<dimen name="textOffset">20dp</dimen>
<dimen name="strokeWidth">4dp</dimen>
<dimen name="textSize">18sp</dimen>
</resources> برای اینکه برنامه در یک صفحه نمایش بزرگتر خوب به نظر برسد (و جزئیات را راحت تر ببینید)، می توانید یک فایل dimens با مقادیر بزرگتر ایجاد کنید که فقط برای صفحات بزرگتر اعمال می شود.
- در Android Studio، روی پوشه مقادیر کلیک راست کرده و New > Values resource file را انتخاب کنید.
- در گفتگوی فایل منبع جدید ، فایل را
dimensفراخوانی کنید. در واجد شرایط , Smallest Screen Width را انتخاب کنید و روی دکمه >> کلیک کنید تا آن را به واجد شرایط انتخاب شده اضافه کنید . 480 را در کادر Smallest screen width وارد کرده و روی OK کلیک کنید.

- فایل باید مطابق شکل زیر در پوشه مقادیر شما نمایش داده شود.

- اگر نمیتوانید فایل را ببینید، به نمای Project Files برنامه بروید. مسیر کامل فایل جدید مطابق شکل زیر است:
ClippingExample/app/src/main/res/values-sw480dp/dimens.xml.

- محتوای پیش فرض فایل
values-sw480dp/dimens.xmlرا با ابعاد زیر جایگزین کنید.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="clipRectRight">120dp</dimen>
<dimen name="clipRectBottom">120dp</dimen>
<dimen name="rectInset">10dp</dimen>
<dimen name="smallRectOffset">50dp</dimen>
<dimen name="circleRadius">40dp</dimen>
<dimen name="textOffset">25dp</dimen>
<dimen name="strokeWidth">6dp</dimen>
</resources>- در
strings.xml، رشته های زیر را اضافه کنید. اینها برای نمایش متن روی بوم استفاده خواهند شد.
<string name="clipping">Clipping</string>
<string name="translated">translated text</string>
<string name="skewed">"Skewed and "</string>مرحله 3: یک شی Paint و Path ایجاد و مقداردهی اولیه کنید
- به نمای Android پروژه خود برگردید.
- در
ClippedViewیک متغیرPaintبرای طراحی تعریف کنید. Anti-aliasing را فعال کنید و از عرض خط و اندازه متن تعریف شده در ابعاد، مانند شکل زیر استفاده کنید.
private val paint = Paint().apply {
// Smooth out edges of what is drawn without affecting shape.
isAntiAlias = true
strokeWidth = resources.getDimension(R.dimen.strokeWidth)
textSize = resources.getDimension(R.dimen.textSize)
}- در
ClippedView، یکPathایجاد و مقداردهی اولیه کنید تا مسیر آنچه ترسیم شده است را به صورت محلی ذخیره کنید.android.graphics.Pathرا وارد کنید.
private val path = Path()مرحله 4: شکل ها را تنظیم کنید
در این برنامه، چندین ردیف و دو ستون از اشکال را به روش های مختلف برش داده شده نمایش می دهید.
همه آنها مشترک هستند:
- یک مستطیل بزرگ (مربع) که نقش یک ظرف را دارد
- یک خط مورب در سراسر مستطیل بزرگ
- یک دایره
- یک رشته کوتاه از متن

در این مرحله شما ابعاد آن اشکال را از منابع تنظیم می کنید، به طوری که وقتی بعداً از آنها استفاده می کنید فقط یک بار باید ابعاد را بدست آورید.
- در
ClippedView، در زیرpath، متغیرهایی را برای ابعاد برای یک مستطیل برش در اطراف کل مجموعه اشکال اضافه کنید.
private val clipRectRight = resources.getDimension(R.dimen.clipRectRight)
private val clipRectBottom = resources.getDimension(R.dimen.clipRectBottom)
private val clipRectTop = resources.getDimension(R.dimen.clipRectTop)
private val clipRectLeft = resources.getDimension(R.dimen.clipRectLeft)- متغیرهایی را برای داخل یک مستطیل و افست یک مستطیل کوچک اضافه کنید.
private val rectInset = resources.getDimension(R.dimen.rectInset)
private val smallRectOffset = resources.getDimension(R.dimen.smallRectOffset)- یک متغیر برای شعاع دایره اضافه کنید. این شعاع دایره ای است که در داخل مستطیل کشیده شده است.
private val circleRadius = resources.getDimension(R.dimen.circleRadius)- برای متنی که در داخل مستطیل کشیده شده است، یک افست و اندازه متن اضافه کنید.
private val textOffset = resources.getDimension(R.dimen.textOffset)
private val textSize = resources.getDimension(R.dimen.textSize)مرحله 4: مکان سطر و ستون را تنظیم کنید
اشکال این برنامه در دو ستون و چهار ردیف نمایش داده می شوند که با مقادیر ابعاد تنظیم شده در بالا تعیین می شوند. ریاضیات مربوط به این قسمت از این نرم افزار کد نیست، اما همانطور که در کد داده شده در این مرحله کپی می کنید، به آن نگاه کنید.
- مختصات را برای دو ستون تنظیم کنید.
private val columnOne = rectInset
private val columnTwo = columnOne + rectInset + clipRectRight- مختصات هر سطر را اضافه کنید، از جمله سطر نهایی متن تبدیل شده.
private val rowOne = rectInset
private val rowTwo = rowOne + rectInset + clipRectBottom
private val rowThree = rowTwo + rectInset + clipRectBottom
private val rowFour = rowThree + rectInset + clipRectBottom
private val textRow = rowFour + (1.5f * clipRectBottom)- برنامه خود را اجرا کنید برنامه باید با یک صفحه سفید خالی در زیر نام برنامه باز شود.

در onDraw() شما متدهایی را فراخوانی می کنید تا هفت مستطیل برش خورده مختلف را همانطور که در تصویر برنامه زیر نشان داده شده است بکشید. مستطیل ها همه به یک شکل کشیده شده اند. تنها تفاوت آنها در مناطق برش تعریف شده و مکان روی صفحه است.

الگوریتمی که برای رسم مستطیل ها استفاده می شود، مطابق نمودار و توضیحات زیر عمل می کند. به طور خلاصه، با حرکت دادن مبدا Canvas یک سری مستطیل میکشید. از نظر مفهومی، این شامل مراحل زیر است:

(1) ابتدا Canvas به جایی که می خواهید مستطیل کشیده شود ترجمه می کنید. یعنی به جای محاسبه جایی که مستطیل بعدی و همه اشکال دیگر باید رسم شوند، مبدا Canvas ، یعنی سیستم مختصات آن را جابه جا می کنید.
(2) سپس، مستطیل را در مبدا جدید بوم می کشید. یعنی اشکال را در همان مکان در سیستم مختصات ترجمه شده رسم می کنید. این بسیار ساده تر و کمی کارآمدتر است.
(3) در نهایت، Canvas به Origin اولیه آن باز میگردانید.
در اینجا الگوریتمی است که شما آن را پیاده سازی خواهید کرد:
- در
onDraw()یک تابع را فراخوانی کنید تاCanvasبا رنگ پس زمینه خاکستری پر کرده و اشکال اصلی را بکشید. - برای هر مستطیل بریده شده و متنی که باید ترسیم شود، یک تابع فراخوانی کنید.
برای هر مستطیل یا متن:
- وضعیت فعلی
Canvasرا ذخیره کنید تا بتوانید به حالت اولیه بازنشانی کنید. -
Originبوم را به مکانی که می خواهید نقاشی کنید ترجمه کنید. - اشکال و مسیرهای برش را اعمال کنید.
- مستطیل یا متن را بکشید.
- وضعیت
Canvasرا بازیابی کنید.
مرحله: نادیده گرفتن onDraw()
- همانطور که در کد زیر نشان داده شده است،
onDraw()لغو کنید. برای هر شکلی که میکشید یک تابع فراخوانی میکنید که بعداً آن را پیادهسازی خواهید کرد.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawBackAndUnclippedRectangle(canvas)
drawDifferenceClippingExample(canvas)
drawCircularClippingExample(canvas)
drawIntersectionClippingExample(canvas)
drawCombinedClippingExample(canvas)
drawRoundedRectangleClippingExample(canvas)
drawOutsideClippingExample(canvas)
drawSkewedTextExample(canvas)
drawTranslatedTextExample(canvas)
// drawQuickRejectExample(canvas)
}- برای هر یک از توابع ترسیمی، خرد ایجاد کنید تا کد به کامپایل شدن ادامه دهد. می توانید کد زیر را کپی کنید.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
}
private fun drawDifferenceClippingExample(canvas: Canvas){
}
private fun drawCircularClippingExample(canvas: Canvas){
}
private fun drawIntersectionClippingExample(canvas: Canvas){
}
private fun drawCombinedClippingExample(canvas: Canvas){
}
private fun drawRoundedRectangleClippingExample(canvas: Canvas){
}
private fun drawOutsideClippingExample(canvas: Canvas){
}
private fun drawTranslatedTextExample(canvas: Canvas){
}
private fun drawSkewedTextExample(canvas: Canvas){
}
private fun drawQuickRejectExample(canvas: Canvas){
} این برنامه هفت بار همان مستطیل را ترسیم می کند و شکل می دهد، ابتدا بدون برش، سپس شش بار با استفاده از مسیرهای برش مختلف. متد drawClippedRectangle() کد ترسیم یک مستطیل را فاکتور می کند، همانطور که در زیر نشان داده شده است.

مرحله 1: متد drawClippedRectangle() را ایجاد کنید
- یک متد
drawClippedRectangle()ایجاد کنید که یکcanvasاز نوعCanvasرا می گیرد.
private fun drawClippedRectangle(canvas: Canvas) {
}- در متد
drawClippedRectangle()، مرزهای مستطیل برش را برای کل شکل تعیین کنید. یک مستطیل برش را اعمال کنید که محدود به کشیدن مربع است.
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)متد Canvas.clipRect(...) ناحیه ای از صفحه را که عملیات ترسیم آینده می توانند روی آن بنویسند، کاهش می دهد. مرزهای برش را به عنوان تقاطع فضایی مستطیل برش فعلی و مستطیل منتقل شده به clipRect() تعیین می کند. انواع زیادی از متد clipRect() وجود دارد که اشکال مختلفی را برای مناطق می پذیرد و عملیات متفاوتی را بر روی مستطیل برش می دهد.
-
canvasبا رنگ سفید پر کنید. بله! کل بوم، چون مستطیل نمی کشی، داری بریده می کنی! به دلیل وجود مستطیل برش، تنها ناحیه تعریف شده توسط مستطیل برش پر می شود و یک مستطیل سفید ایجاد می کند. بقیه سطح خاکستری باقی می ماند.
canvas.drawColor(Color.WHITE)- رنگ را به قرمز تغییر دهید و یک خط مورب در داخل مستطیل برش بکشید.
paint.color = Color.RED
canvas.drawLine(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom,paint
)- رنگ را سبز کنید و یک دایره داخل مستطیل برش بکشید.
paint.color = Color.GREEN
canvas.drawCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,paint
)- رنگ را روی آبی تنظیم کنید و متن را با لبه سمت راست مستطیل برش تراز کنید. برای ترسیم متن
canvas.drawText()استفاده کنید.
paint.color = Color.BLUE
// Align the RIGHT side of the text with the origin.
paint.textSize = textSize
paint.textAlign = Paint.Align.RIGHT
canvas.drawText(
context.getString(R.string.clipping),
clipRectRight,textOffset,paint
)مرحله 2: متد drawBackAndUnclippedRectangle() را پیاده سازی کنید
- برای مشاهده عملکرد متد
drawClippedRectangle()، اولین مستطیل بدون برش را با اجرای متدdrawBackAndUnclippedRectangle()مطابق شکل زیر بکشید.canvasرا ذخیره کنید، به اولین سطر و موقعیت ستون ترجمه کنید، با فراخوانیdrawClippedRectangle()رسم کنید و سپسcanvasبه حالت قبلی بازگردانید.
private fun drawBackAndUnclippedRectangle(canvas: Canvas){
canvas.drawColor(Color.GRAY)
canvas.save()
canvas.translate(columnOne,rowOne)
drawClippedRectangle(canvas)
canvas.restore()
}- برنامه خود را اجرا کنید شما باید اولین مستطیل سفید را با دایره، خط قرمز و متن آن در پس زمینه خاکستری ببینید.

در روشهای نمونه برش زیر، ترکیبهای مختلفی از مناطق برش را برای دستیابی به جلوههای گرافیکی اعمال میکنید و یاد میگیرید که چگونه میتوانید مناطق برش را برای ایجاد هر شکلی که نیاز دارید ترکیب کنید.
هر یک از این روش ها از یک الگو پیروی می کنند.
- ذخیره وضعیت فعلی بوم:
canvas.save()
زمینه فعالیت پشته ای از حالت های ترسیم را حفظ می کند. حالت های ترسیمی شامل ماتریس تبدیل جریان و منطقه برش فعلی است. میتوانید وضعیت فعلی را ذخیره کنید، اقداماتی را انجام دهید که وضعیت طراحی را تغییر میدهد (مانند ترجمه یا چرخاندن بوم)، و سپس حالت طراحی ذخیره شده را بازیابی کنید. (توجه: این مانند دستور "stash" در git است!).
وقتی طراحی شما شامل تبدیل است، زنجیر کردن و بازگرداندن تبدیل ها با معکوس کردن آنها مستعد خطا است. به عنوان مثال، اگر ترجمه کنید، کشش دهید، و سپس بچرخانید، به سرعت پیچیده می شود. در عوض، وضعیت بوم را ذخیره کنید، تبدیل های خود را اعمال کنید، ترسیم کنید و سپس حالت قبلی را بازیابی کنید.
به عنوان مثال، می توانید یک منطقه برش را تعریف کنید و آن حالت را ذخیره کنید. سپس بوم را ترجمه کنید، یک ناحیه برش اضافه کنید و بچرخانید. پس از انجام برخی طراحی ها، می توانید حالت برش اولیه را بازیابی کنید و می توانید مانند نمودار نشان داده شده، به انجام ترجمه های مختلف و تبدیل انحرافی ادامه دهید.

- مبدا بوم را به مختصات سطر/ستون ترجمه کنید:
canvas.translate()
جابجایی مبدأ بوم و کشیدن همان چیز در یک سیستم مختصات جدید بسیار ساده تر از جابجایی همه عناصر برای ترسیم است. (نکته: می توانید از همین تکنیک برای چرخاندن عناصر استفاده کنید.)
- در صورت وجود، تغییرات را در
pathاعمال کنید. - اعمال برش:
canvas.clipPath(path) - شکل ها را بکشید:
drawClippedRectangle() or drawText() - حالت بوم قبلی را بازیابی کنید:
canvas.restore()
مرحله 1: اجرای drawDifferenceClippingExample(canvas)
برای رسم مستطیل دوم کد اضافه کنید، که از تفاوت بین دو مستطیل برش برای ایجاد افکت قاب عکس استفاده می کند.

از کد زیر استفاده کنید که کارهای زیر را انجام می دهد:
- بوم را ذخیره کنید.
- مبدأ بوم را به فضای باز به ردیف اول، ستون دوم، سمت راست مستطیل اول ترجمه کنید.
- دو مستطیل برش را اعمال کنید. عملگر
DIFFERENCEمستطیل دوم را از مستطیل اول کم می کند.
- برای ترسیم بوم اصلاح شده، متد
drawClippedRectangle()را فراخوانی کنید. - حالت بوم را بازیابی کنید.
private fun drawDifferenceClippingExample(canvas: Canvas) {
canvas.save()
// Move the origin to the right for the next rectangle.
canvas.translate(columnTwo,rowOne)
// Use the subtraction of two clipping rectangles to create a frame.
canvas.clipRect(
2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset
)
// The method clipRect(float, float, float, float, Region.Op
// .DIFFERENCE) was deprecated in API level 26. The recommended
// alternative method is clipOutRect(float, float, float, float),
// which is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O){
canvas.clipRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset,
Region.Op.DIFFERENCE
)
} else {
canvas.clipOutRect(
4 * rectInset,4 * rectInset,
clipRectRight - 4 * rectInset,
clipRectBottom - 4 * rectInset
)
}
drawClippedRectangle(canvas)
canvas.restore()
}- برنامه خود را اجرا کنید و باید شبیه این باشد.

مرحله 2: اجرای drawCircularClippingExample(canvas)
در مرحله بعد، کدی را برای رسم مستطیل اضافه کنید که از یک منطقه برش دایره ای ایجاد شده از یک مسیر دایره ای استفاده می کند، که اساسا دایره را حذف می کند (نه رسم می کند) و بنابراین به جای آن پس زمینه خاکستری را نشان می دهد.

private fun drawCircularClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowTwo)
// Clears any lines and curves from the path but unlike reset(),
// keeps the internal data structure for faster reuse.
path.rewind()
path.addCircle(
circleRadius,clipRectBottom - circleRadius,
circleRadius,Path.Direction.CCW
)
// The method clipPath(path, Region.Op.DIFFERENCE) was deprecated in
// API level 26. The recommended alternative method is
// clipOutPath(Path), which is currently available in
// API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipPath(path, Region.Op.DIFFERENCE)
} else {
canvas.clipOutPath(path)
}
drawClippedRectangle(canvas)
canvas.restore()
}مرحله 3: اجرای drawIntersectionClippingExample(canvas)
در مرحله بعد، کدی را اضافه کنید تا محل تقاطع دو مستطیل برش را در سطر و ستون دوم بکشید.


توجه داشته باشید که بسته به وضوح صفحه نمایش شما، ظاهر این منطقه متفاوت خواهد بود. برای تغییر اندازه ناحیه قابل مشاهده، بعد smallRectOffset را آزمایش کنید. یک smallRectOffset کوچکتر باعث ایجاد ناحیه بزرگتر روی صفحه می شود.
private fun drawIntersectionClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowTwo)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight - smallRectOffset,
clipRectBottom - smallRectOffset
)
// The method clipRect(float, float, float, float, Region.Op
// .INTERSECT) was deprecated in API level 26. The recommended
// alternative method is clipRect(float, float, float, float), which
// is currently available in API level 26 and higher.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom,
Region.Op.INTERSECT
)
} else {
canvas.clipRect(
clipRectLeft + smallRectOffset,
clipRectTop + smallRectOffset,
clipRectRight,clipRectBottom
)
}
drawClippedRectangle(canvas)
canvas.restore()
}
مرحله 4: اجرای drawCombinedClippingExample(canvas)
در مرحله بعد، اشکال، یک دایره و یک مستطیل را با هم ترکیب کنید و هر مسیری را برای تعریف یک منطقه برش ترسیم کنید.

private fun drawCombinedClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne, rowThree)
path.rewind()
path.addCircle(
clipRectLeft + rectInset + circleRadius,
clipRectTop + circleRadius + rectInset,
circleRadius,Path.Direction.CCW
)
path.addRect(
clipRectRight / 2 - circleRadius,
clipRectTop + circleRadius + rectInset,
clipRectRight / 2 + circleRadius,
clipRectBottom - rectInset,Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
مرحله 5: اجرای drawRoundedRectangleClippingExample(canvas)
در مرحله بعد، یک مستطیل گرد که یک شکل معمولی برش است، اضافه کنید.

- در سطح بالا، یک متغیر مستطیل ایجاد و مقداردهی اولیه کنید.
RectFکلاسی است که مختصات مستطیل را در ممیز شناور نگه می دارد.
private var rectF = RectF(
rectInset,
rectInset,
clipRectRight - rectInset,
clipRectBottom - rectInset
)- تابع
drawRoundedRectangleClippingExample()را پیاده سازی کنید. تابعaddRoundRect()یک مستطیل، مقادیر x و y شعاع گوشه و جهت پیچیدن کانتور مستطیل گرد را می گیرد.Path.Directionمشخص میکند که شکلهای بسته (مثلاً راست، بیضی) چگونه به یک مسیر اضافه میشوند.CCWمخفف خلاف جهت عقربه های ساعت است.
private fun drawRoundedRectangleClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnTwo,rowThree)
path.rewind()
path.addRoundRect(
rectF,clipRectRight / 4,
clipRectRight / 4, Path.Direction.CCW
)
canvas.clipPath(path)
drawClippedRectangle(canvas)
canvas.restore()
}
مرحله 6: اجرای drawOutsideClippingExample(canvas)
قسمت بیرونی مستطیل را با دوبرابر کردن ورودی های مستطیل برش گیر کنید.

private fun drawOutsideClippingExample(canvas: Canvas) {
canvas.save()
canvas.translate(columnOne,rowFour)
canvas.clipRect(2 * rectInset,2 * rectInset,
clipRectRight - 2 * rectInset,
clipRectBottom - 2 * rectInset)
drawClippedRectangle(canvas)
canvas.restore()
}
مرحله 7: پیاده سازی drawTranslatedTextExample(canvas)
رسم متن واقعاً با هیچ شکل دیگری تفاوتی ندارد و می توانید تبدیل به متن اعمال کنید. به عنوان مثال، می توانید متن را با ترجمه بوم و کشیدن متن ترجمه کنید.

- تابع زیر را پیاده سازی کنید.
private fun drawTranslatedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.GREEN
// Align the RIGHT side of the text with the origin.
paint.textAlign = Paint.Align.LEFT
// Apply transformation to canvas.
canvas.translate(columnTwo,textRow)
// Draw text.
canvas.drawText(context.getString(R.string.translated),
clipRectLeft,clipRectTop,paint)
canvas.restore()
}- برنامه خود را اجرا کنید تا متن ترجمه شده را ببینید.

مرحله 8: اجرای drawSkewedTextExample(canvas)
همچنین می توانید متن را کج کنید. یعنی به طرق مختلف آن را تحریف کنید.

- تابع زیر را در
ClippedViewایجاد کنید.
private fun drawSkewedTextExample(canvas: Canvas) {
canvas.save()
paint.color = Color.YELLOW
paint.textAlign = Paint.Align.RIGHT
// Position text.
canvas.translate(columnTwo, textRow)
// Apply skew transformation.
canvas.skew(0.2f, 0.3f)
canvas.drawText(context.getString(R.string.skewed),
clipRectLeft, clipRectTop, paint)
canvas.restore()
}- برنامه خود را اجرا کنید تا متن اریب را قبل از متن ترجمه شده ببینید.

متد quickReject() Canvas به شما امکان می دهد پس از اعمال همه تبدیل ها، بررسی کنید که آیا یک مستطیل یا مسیر مشخص شده کاملاً خارج از مناطق قابل مشاهده فعلی قرار دارد یا خیر.
متد quickReject() زمانی بسیار مفید است که شما در حال ساختن نقشه های پیچیده تری هستید و باید این کار را با بیشترین سرعت ممکن انجام دهید. با quickReject() می توانید به طور موثر تصمیم بگیرید که کدام اشیاء را اصلاً نباید بکشید و نیازی به نوشتن منطق تقاطع خودتان نیست.
- اگر مستطیل یا مسیر اصلاً روی صفحه قابل مشاهده نباشد، متد
quickReject()trueرا برمیگرداند. برای همپوشانی های جزئی، هنوز باید خودتان بررسی کنید. -
EdgeTypeیاAA( Antialiased : درمان لبهها با گرد کردن، زیرا ممکن است ضد نام مستعار باشند) یاBW(سیاه-سفید: لبهها را فقط با گرد کردن به نزدیکترین مرز پیکسل درمان کنید) فقط برای گرد کردن به نزدیکترین پیکسل.
چندین نسخه از quickReject() وجود دارد و شما همچنین می توانید آنها را در اسناد پیدا کنید.
| quickReject |
| quickReject |
| quickReject |
در این تمرین میخواهید مانند قبل در یک ردیف جدید، زیر متن و داخل clipRect رسم کنید.
- شما ابتدا
quickReject()با یک مستطیلinClipRectangleفراخوانی می کنید که باclipRectهمپوشانی دارد. بنابراینquickReject()false را برمی گرداند،clipRectباBLACKپر می شود و مستطیلinClipRectangleرسم می شود.

- سپس کد را تغییر دهید و
quickReject()را باnotInClipRectangleفراخوانی کنید.quickReject()اکنون true را برمی گرداند وclipRectباWHITEپر می شود وnotInClipRectangleترسیم نمی شود.

وقتی نقشه های پیچیده ای دارید، این می تواند به سرعت به شما بگوید که کدام اشکال کاملاً خارج از منطقه برش هستند و ممکن است مجبور شوید محاسبات و ترسیم اضافی برای آنها انجام دهید، زیرا آنها به طور جزئی یا کامل در منطقه برش قرار دارند.
مرحله: با quickReject() آزمایش کنید
- در سطح بالا، یک متغیر برای مختصات y یک ردیف اضافی ایجاد کنید.
private val rejectRow = rowFour + rectInset + 2*clipRectBottom- تابع
drawQuickRejectExample()زیر را بهClippedViewاضافه کنید. کد را بخوانید، زیرا حاوی همه چیزهایی است که برای استفاده ازquickReject()نیاز دارید.
private fun drawQuickRejectExample(canvas: Canvas) {
val inClipRectangle = RectF(clipRectRight / 2,
clipRectBottom / 2,
clipRectRight * 2,
clipRectBottom * 2)
val notInClipRectangle = RectF(RectF(clipRectRight+1,
clipRectBottom+1,
clipRectRight * 2,
clipRectBottom * 2))
canvas.save()
canvas.translate(columnOne, rejectRow)
canvas.clipRect(
clipRectLeft,clipRectTop,
clipRectRight,clipRectBottom
)
if (canvas.quickReject(
inClipRectangle, Canvas.EdgeType.AA)) {
canvas.drawColor(Color.WHITE)
}
else {
canvas.drawColor(Color.BLACK)
canvas.drawRect(inClipRectangle, paint
)
}
canvas.restore()
}- در
onDraw()فراخوانی ازdrawQuickRejectExample()را لغو نظر کنید. - برنامه خود را اجرا کنید و یک مستطیل مشکی خواهید دید که ناحیه برش پر شده و بخش هایی از
inClipRectangleاست، زیرا دو مستطیل با هم همپوشانی دارند، بنابراینquickReject()falseرا برمی گرداند وinClipRectangleرسم می شود.

- در
drawQuickRejectExample()کد را برای اجرایquickReject()در مقابلnotInClipRectangle.اکنونquickReject()trueرا برمی گرداند و ناحیه برش با سفید پر می شود.

دانلود کد برای کد لبه تمام شده..
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-clipping
یا میتوانید مخزن را بهعنوان یک فایل Zip دانلود کنید، آن را از حالت فشرده خارج کنید و در Android Studio باز کنید.
-
Contextیک فعالیت حالتی را حفظ می کند که تبدیل ها و مناطق بریده شده را برایCanvasحفظ می کند. - از
canvas.save()وcanvas.restore()برای ترسیم و بازگشت به حالت اولیه بوم خود استفاده کنید. - برای ترسیم چندین شکل بر روی بوم، می توانید مکان آنها را محاسبه کنید یا می توانید مبدا سطح طراحی خود را جابجا کنید (ترجمه کنید). دومی میتواند ایجاد روشهای کاربردی برای دنبالههای ترسیم مکرر را آسانتر کند.
- مناطق برش می تواند هر شکل، ترکیبی از اشکال یا مسیر باشد.
- می توانید مناطق برش را اضافه، تفریق و قطع کنید تا دقیقاً منطقه مورد نیاز خود را بدست آورید.
- می توانید با تبدیل بوم، تبدیل به متن اعمال کنید.
- متد
quickReject()Canvasبه شما امکان می دهد بررسی کنید که آیا یک مستطیل یا مسیر مشخص شده کاملاً خارج از مناطق قابل مشاهده فعلی قرار دارد یا خیر.
دوره بی ادبی:
مستندات توسعه دهنده اندروید:
- کلاس
Canvas - کلاس
Bitmap -
Viewکلاس - کلاس
Paint - تنظیمات
Bitmap.config - اپراتورهای
Region.Op - کلاس
Path - کلاس
Canvas - کلاس
Bitmap -
Viewکلاس - کلاس
Paint - تنظیمات
Bitmap.config - اپراتورهای
Region.Op - کلاس
Path - ابزارهای گرافیکی
android.graphics -
Bitmap.ConfigتنظیماتCanvas - بوم و نقاشی
- ()canvas.translate چه کاری انجام می دهد
- درک save() و restore() برای زمینه Canvas
- بریدن
- اضافه برداشت .
-
@JvmOverloads
همچنین سری مقالات معماری گرافیک را برای توضیح عمیق در مورد چگونگی ترسیم فریمورک اندروید روی صفحه ببینید.
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، کار میکنند، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است، محول کنند.
اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
به این سوالات پاسخ دهید
سوال 1
چه روشی را برای حذف کارآمد اشکال از ترسیم میخواهید؟
▢ excludeFromDrawing()
▢ quickReject()
▢ onDraw()
▢ clipRect()
سوال 2
Canvas.save() و Canvas.restore() کدام اطلاعات را ذخیره و بازیابی می کنند؟
▢ رنگ، عرض خط و غیره
▢ فقط تحولات فعلی
▢ تحولات فعلی و منطقه برش
▢ فقط منطقه برش فعلی
سوال 3
Paint.Align مشخص می کند:
▢ نحوه تراز کردن اشکال طراحی زیر
▢ متن از کدام طرف مبدأ گرفته شده است
▢ جایی که در ناحیه برش تراز شده است
▢ کدام سمت متن را با مبدا تراز کنید
برای پیوند به دیگر کدلب ها در این دوره، صفحه فرود Advanced Android in Kotlin Codelabs را ببینید.