این کد لبه بخشی از دوره Advanced Android in Kotlin است. اگر از طریق کدها به ترتیب کار کنید، بیشترین ارزش را از این دوره خواهید گرفت، اما اجباری نیست. همه کدهای دوره در صفحه فرود Android Advanced in Kotlin Codelabs فهرست شده اند.
مقدمه
Android مجموعه بزرگی از زیر کلاسهای View ارائه میکند، مانند Button ، TextView ، EditText ، ImageView ، CheckBox یا RadioButton . میتوانید از این زیر کلاسها برای ایجاد رابط کاربری استفاده کنید که تعامل کاربر را فعال میکند و اطلاعات را در برنامه شما نمایش میدهد. اگر هیچ یک از زیر کلاس های View نیازهای شما را برآورده نکرد، می توانید یک زیر کلاس View به نام نمای سفارشی ایجاد کنید.
برای ایجاد یک نمای سفارشی، میتوانید یک زیرکلاس View موجود را گسترش دهید (مانند یک Button یا EditText )، یا زیرکلاس View خود را ایجاد کنید. با گسترش مستقیم View ، میتوانید یک عنصر رابط کاربری تعاملی با هر اندازه و شکلی با نادیده گرفتن متد onDraw() برای View ایجاد کنید تا آن را ترسیم کند.
پس از ایجاد نمای سفارشی، میتوانید آن را به همان روشی که TextView یا Button اضافه میکنید، به طرحبندیهای فعالیت خود اضافه کنید.
این درس به شما نشان میدهد که چگونه با گسترش View یک نمای سفارشی از ابتدا ایجاد کنید.
آنچه از قبل باید بدانید
- نحوه ایجاد یک برنامه با Activity و اجرای آن با استفاده از Android Studio.
چیزی که یاد خواهید گرفت
- نحوه گسترش
Viewبرای ایجاد نمای سفارشی - نحوه ترسیم نمای سفارشی که به شکل دایره است.
- نحوه استفاده از شنوندگان برای مدیریت تعامل کاربر با نمای سفارشی.
- نحوه استفاده از نمای سفارشی در یک طرح.
کاری که خواهی کرد
برنامه CustomFanController نحوه ایجاد یک زیرکلاس نمای سفارشی را با گسترش کلاس View نشان می دهد. زیر کلاس جدید DialView نام دارد.
این برنامه یک عنصر رابط کاربری دایرهای را نشان میدهد که شبیه یک کنترل فیزیکی فن است، با تنظیمات خاموش (0)، کم (1)، متوسط (2) و زیاد (3). وقتی کاربر روی نما ضربه می زند، نشانگر انتخاب به موقعیت بعدی می رود: 0-1-2-3، و به 0 برمی گردد. همچنین، اگر انتخاب 1 یا بالاتر باشد، رنگ پس زمینه قسمت دایره ای نما از خاکستری به سبز تغییر می کند (نشان دهنده روشن بودن برق فن).


نماها بلوکهای اصلی رابط کاربری یک برنامه هستند. کلاس View زیر کلاسهای زیادی را ارائه میکند که به ویجتهای UI گفته میشود، که بسیاری از نیازهای رابط کاربری معمولی یک برنامه Android را پوشش میدهند.
بلوک های سازنده رابط کاربری مانند Button و TextView زیر کلاس هایی هستند که کلاس View را گسترش می دهند. برای صرفه جویی در زمان و تلاش توسعه، می توانید یکی از این زیر کلاس های View گسترش دهید. نمای سفارشی ظاهر و رفتار والد خود را به ارث می برد و می توانید رفتار یا جنبه ظاهری را که می خواهید تغییر دهید نادیده بگیرید. به عنوان مثال، اگر EditText برای ایجاد یک نمای سفارشی گسترش دهید، نما درست مانند نمای EditText عمل می کند، اما همچنین می تواند برای نشان دادن دکمه X که متن را از قسمت ورودی متن پاک می کند، سفارشی شود.

میتوانید هر زیرکلاس View ، مانند EditText را گسترش دهید تا یک نمای سفارشی دریافت کنید— نزدیکترین مورد را به آنچه میخواهید انجام دهید، انتخاب کنید. سپس میتوانید از نمای سفارشی مانند هر زیرکلاس View در یک یا چند طرحبندی به عنوان یک عنصر XML با ویژگیها استفاده کنید.
برای ایجاد نمای سفارشی خود از ابتدا، خود کلاس View گسترش دهید. کد شما روشهای View را لغو میکند تا ظاهر و عملکرد نما را مشخص کند. کلید ایجاد نمای سفارشی خود این است که شما مسئول ترسیم کل عنصر UI با هر اندازه و شکلی روی صفحه هستید. اگر یک نمای موجود مانند Button را زیرکلاس کنید، آن کلاس طراحی را برای شما انجام می دهد. (شما بعداً در این کدنویسی درباره طراحی بیشتر خواهید آموخت.)
برای ایجاد یک نمای سفارشی این مراحل کلی را دنبال کنید:
- یک کلاس نمای سفارشی ایجاد کنید که
Viewرا گسترش دهد یا یک زیرکلاسViewرا گسترش دهد (مانندButtonیاEditText). - اگر یک زیرکلاس
Viewموجود را گسترش دهید، فقط رفتار یا جنبههایی از ظاهری را که میخواهید تغییر دهید لغو کنید. - اگر کلاس
Viewرا گسترش دهید، شکل نمای سفارشی را ترسیم کنید و ظاهر آن را با نادیده گرفتن متدهایViewمانندonDraw()وonMeasure()در کلاس جدید کنترل کنید. - برای پاسخ به تعامل کاربر کد اضافه کنید و در صورت لزوم نمای سفارشی را دوباره ترسیم کنید.
- از کلاس view سفارشی به عنوان ویجت UI در طرح XML فعالیت خود استفاده کنید. همچنین میتوانید ویژگیهای سفارشی را برای نما تعریف کنید تا سفارشیسازی نمای در طرحبندیهای مختلف ارائه شود.
در این کار شما:
- یک برنامه با
ImageViewبه عنوان یک مکان نگهدار موقت برای نمای سفارشی ایجاد کنید. - برای ایجاد نمای سفارشی،
Viewگسترش دهید. - نمای سفارشی را با مقادیر طراحی و نقاشی راهاندازی کنید.
مرحله 1: یک برنامه با نگهدارنده ImageView ایجاد کنید
- با استفاده از قالب Empty Activity یک برنامه Kotlin با عنوان
CustomFanControllerایجاد کنید. مطمئن شوید که نام بستهcom.example.android.customfancontrollerباشد. - برای ویرایش کد XML،
activity_main.xmlدر تب Text باز کنید. -
TextViewموجود را با این کد جایگزین کنید. این متن به عنوان یک برچسب در فعالیت برای نمای سفارشی عمل می کند.
<TextView
android:id="@+id/customViewLabel"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Display3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:textColor="@android:color/black"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="24dp"
android:text="Fan Control"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>- این عنصر
ImageViewبه طرح اضافه کنید. این یک مکان نگهدار برای نمای سفارشی است که در این کد لبه ایجاد خواهید کرد.
<ImageView
android:id="@+id/dialView"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/darker_gray"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"/>- استخراج منابع رشته و ابعاد در هر دو عنصر UI.
- روی تب Design کلیک کنید. طرح باید به شکل زیر باشد:

مرحله 2. کلاس view سفارشی خود را ایجاد کنید
- یک کلاس Kotlin جدید به نام
DialViewایجاد کنید. - تعریف کلاس را برای گسترش
Viewتغییر دهید. وقتی از شما خواسته شدandroid.view.Viewرا وارد کنید. - روی
Viewکلیک کنید و سپس روی لامپ قرمز کلیک کنید. افزودن سازندگان نمای Android با استفاده از «@JvmOverloads» را انتخاب کنید. Android Studio سازنده را از کلاسViewاضافه می کند. حاشیه نویسی@JvmOverloadsبه کامپایلر Kotlin دستور می دهد تا برای این تابع اضافه بارهایی ایجاد کند که جایگزین مقادیر پارامترهای پیش فرض می شود.
class DialView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {- بالای تعریف کلاس
DialView، درست در زیر importها، یکenumسطح بالا برای نمایش سرعت فن موجود اضافه کنید. توجه داشته باشید که اینenumاز نوعIntاست زیرا مقادیر به جای رشته های واقعی منابع رشته ای هستند. Android Studio در هر یک از این مقادیر، خطاهایی را برای منابع رشته از دست رفته نشان می دهد. در مرحله بعد آن را برطرف خواهید کرد.
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
}- در زیر
enum، این ثابت ها را اضافه کنید. شما از اینها به عنوان بخشی از ترسیم نشانگرها و برچسب های شماره گیری استفاده خواهید کرد.
private const val RADIUS_OFFSET_LABEL = 30
private const val RADIUS_OFFSET_INDICATOR = -35- در داخل کلاس
DialView، چندین متغیر مورد نیاز را برای ترسیم نمای سفارشی تعریف کنید. در صورت درخواستandroid.graphics.PointFرا وارد کنید.
private var radius = 0.0f // Radius of the circle.
private var fanSpeed = FanSpeed.OFF // The active selection.
// position variable which will be used to draw label and indicator circle position
private val pointPosition: PointF = PointF(0.0f, 0.0f)- شعاع شعاع فعلی دایره است
radiusاین مقدار زمانی تنظیم می شود که نما روی صفحه نمایش داده می شود. - fanSpeed سرعت فعلی فن است که یکی از مقادیر
fanSpeedشمارشFanSpeedاست. به طور پیش فرض آن مقدارOFFاست. - در نهایت
postPositionیک نقطه X,Y است که برای ترسیم چندین عنصر نمای روی صفحه استفاده می شود.
این مقادیر به جای زمانی که نما واقعاً ترسیم می شود، در اینجا ایجاد و مقداردهی اولیه می شوند تا اطمینان حاصل شود که مرحله طراحی واقعی با بیشترین سرعت ممکن اجرا می شود.
- همچنین در داخل تعریف کلاس
DialView، یک شیPaintبا تعداد انگشت شماری از سبک های اولیه مقداردهی اولیه کنید.android.graphics.Paintوandroid.graphics.Typefaceرا در صورت درخواست وارد کنید. همانطور که قبلا در مورد متغیرها، این سبک ها در اینجا مقداردهی اولیه می شوند تا به سرعت بخشیدن به مرحله ترسیم کمک کنند.
private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
textAlign = Paint.Align.CENTER
textSize = 55.0f
typeface = Typeface.create( "", Typeface.BOLD)
}-
res/values/strings.xmlرا باز کنید و منابع رشته را برای سرعت فن اضافه کنید:
<string name="fan_off">off</string>
<string name="fan_low">1</string>
<string name="fan_medium">2</string>
<string name="fan_high">3</string> هنگامی که یک نمای سفارشی ایجاد کردید، باید بتوانید آن را ترسیم کنید. هنگامی که یک زیر کلاس View مانند EditText را گسترش می دهید، آن زیر کلاس ظاهر و ویژگی های نما را تعریف می کند و خود را روی صفحه می کشد. در نتیجه، برای ترسیم نما نیازی به نوشتن کد ندارید. میتوانید روشهای والد را لغو کنید تا نمای خود را سفارشی کنید.
اگر نمای خود را از ابتدا ایجاد میکنید (با گسترش View )، شما مسئول ترسیم کل نما در هر بار بازخوانی صفحه، و نادیده گرفتن روشهای View هستید که طراحی را انجام میدهند. برای ترسیم درست نمای سفارشی که View را گسترش می دهد، باید:
- اندازه نما را زمانی که برای اولین بار ظاهر می شود، و هر بار که اندازه آن نما تغییر می کند، با نادیده گرفتن متد
onSizeChanged()محاسبه کنید. - برای ترسیم نمای سفارشی، با استفاده از یک شی
Canvasکه توسط یک شیPaintاستایل شده است، متدonDraw()را لغو کنید. - زمانی که به کلیک کاربر پاسخ میدهد، متد
invalidate()را فراخوانی کنید که نحوه ترسیم نما را تغییر میدهد تا کل view را باطل کند، در نتیجه فراخوانی بهonDraw()مجبور میشود تا نمای را دوباره ترسیم کند.
متد onDraw() هر بار که صفحه رفرش می شود فراخوانی می شود که می تواند چندین بار در ثانیه باشد. به دلایل عملکرد و برای جلوگیری از اشکالات بصری، باید تا حد امکان در onDraw() کمتر کار کنید. به ویژه، تخصیص ها را در onDraw() قرار ندهید، زیرا تخصیص ها ممکن است منجر به جمع آوری زباله شود که ممکن است باعث ایجاد لکنت بصری شود.
کلاس های Canvas و Paint تعدادی میانبر طراحی مفید را ارائه می دهند:
- با استفاده از
drawText()متن را ترسیم کنید. فونت را با فراخوانیsetTypeface()و رنگ متن را با فراخوانیsetColor()مشخص کنید. - اشکال ابتدایی را با استفاده از
drawRect()،drawOval()وdrawArc()رسم کنید. با فراخوانیsetStyle()پر شده، مشخص شده یا هر دو شکل را تغییر دهید. - نقشه های بیت را با استفاده از
drawBitmap()بکشید.
در کدهای بعدی درباره Canvas and Paint بیشتر خواهید آموخت. برای کسب اطلاعات بیشتر درباره نحوه ترسیم نماها توسط Android، به نحوه ترسیم نماها توسط Android مراجعه کنید.
در این کار، نمای سفارشی کنترلکننده فن را روی صفحه میکشید - خود شمارهگیر، نشانگر موقعیت فعلی و برچسبهای نشانگر - با روشهای onSizeChanged() و onDraw() . شما همچنین یک روش کمکی، computeXYForSpeed(), برای محاسبه موقعیت X,Y فعلی برچسب نشانگر روی صفحه ایجاد خواهید کرد.
مرحله 1. موقعیت ها را محاسبه کنید و نما را بکشید
- در کلاس
DialView، در زیر مقادیر اولیه، متدonSizeChanged()را از کلاسViewبرای محاسبه اندازه شماره گیری نمای سفارشی نادیده بگیرید. وارداتkotlinmath.minدر صورت درخواست
متدonSizeChanged()هر زمان که اندازه نما تغییر می کند، از جمله اولین باری که در هنگام افزایش طرح بندی ترسیم می شود، نامیده می شود. برای محاسبه موقعیتها، ابعاد و هر مقدار دیگر مربوط به اندازه نمای سفارشیتان، به جای اینکه هر بار که ترسیم میکنید، آنها را دوباره محاسبه کنیدonSizeChanged()لغو کنید. در این حالت شما ازonSizeChanged()برای محاسبه شعاع فعلی عنصر دایره شماره گیری استفاده می کنید.
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
radius = (min(width, height) / 2.0 * 0.8).toFloat()
}- در زیر
onSizeChanged()این کد را اضافه کنید تا یک تابع پسوندcomputeXYForSpeed()برایPointFتعریف کنید. کلاس در صورت درخواستkotlin.math.cosوkotlin.math.sinرا وارد کنید. این تابع افزودنی در کلاسPointFمختصات X، Y را روی صفحه برای برچسب متن و نشانگر جریان (0، 1، 2 یا 3) با توجه به موقعیتFanSpeedو شعاع شماره گیری فعلی محاسبه می کند. شما از این درonDraw().
private fun PointF.computeXYForSpeed(pos: FanSpeed, radius: Float) {
// Angles are in radians.
val startAngle = Math.PI * (9 / 8.0)
val angle = startAngle + pos.ordinal * (Math.PI / 4)
x = (radius * cos(angle)).toFloat() + width / 2
y = (radius * sin(angle)).toFloat() + height / 2
}- متد
onDraw()را لغو کنید تا نمای روی صفحه را با کلاسهایCanvasوPaintارائه کنید. در صورت درخواستandroid.graphics.Canvasرا وارد کنید. این نادیده گرفتن اسکلت است:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}- در داخل
onDraw()، این خط را اضافه کنید تا بسته به اینکه سرعت فنOFFباشد یا هر مقدار دیگری، رنگ رنگ را به خاکستری (Color.GRAY) یا سبز (Color.GREEN) تنظیم کنید. در صورت درخواستandroid.graphics.Colorرا وارد کنید.
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN- با استفاده از متد
drawCircle()این کد را برای رسم دایره برای شماره گیری اضافه کنید. این روش از عرض و ارتفاع نمای فعلی برای یافتن مرکز دایره، شعاع دایره و رنگ فعلی استفاده می کند. ویژگی هایwidthوheightاعضای سوپرکلاسViewهستند و ابعاد فعلی نما را نشان می دهند.
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)- این کد زیر را اضافه کنید تا یک دایره کوچکتر برای علامت نشانگر سرعت فن ترسیم کنید، همچنین با متد
drawCircle()این قسمت ازPointFاستفاده می کند. روش توسعهcomputeXYforSpeed()برای محاسبه مختصات X,Y برای مرکز نشانگر بر اساس سرعت فن فعلی.
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)- در نهایت، برچسب های سرعت فن (0، 1، 2، 3) را در موقعیت های مناسب اطراف صفحه بکشید. این قسمت از متد دوباره
PointF.computeXYForSpeed()را فراخوانی می کند تا موقعیت هر برچسب را بدست آورد و هر بار از شیpointPositionبرای جلوگیری از تخصیص مجدد استفاده می کند. برای رسم برچسب هاdrawText()استفاده کنید.
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}متد تکمیل شده onDraw() به شکل زیر است:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Set dial background color to green if selection not off.
paint.color = if (fanSpeed == FanSpeed.OFF) Color.GRAY else Color.GREEN
// Draw the dial.
canvas.drawCircle((width / 2).toFloat(), (height / 2).toFloat(), radius, paint)
// Draw the indicator circle.
val markerRadius = radius + RADIUS_OFFSET_INDICATOR
pointPosition.computeXYForSpeed(fanSpeed, markerRadius)
paint.color = Color.BLACK
canvas.drawCircle(pointPosition.x, pointPosition.y, radius/12, paint)
// Draw the text labels.
val labelRadius = radius + RADIUS_OFFSET_LABEL
for (i in FanSpeed.values()) {
pointPosition.computeXYForSpeed(i, labelRadius)
val label = resources.getString(i.label)
canvas.drawText(label, pointPosition.x, pointPosition.y, paint)
}
}مرحله 2. نمای را به طرحبندی اضافه کنید
برای افزودن نمای سفارشی به رابط کاربری برنامه، آن را به عنوان عنصری در طرح XML فعالیت مشخص میکنید. ظاهر و رفتار آن را با ویژگی های عنصر XML کنترل کنید، همانطور که برای هر عنصر UI دیگر انجام می دهید.
- در
activity_main.xml، تگImageViewبرایdialViewرا بهcom.example.android.customfancontroller.DialViewتغییر دهید و ویژگیandroid:backgroundحذف کنید. همDialViewو همImageViewاصلی ویژگی های استاندارد را از کلاسViewبه ارث می برند، بنابراین نیازی به تغییر هیچ یک از ویژگی های دیگر نیست. عنصرDialViewجدید به شکل زیر است:
<com.example.android.customfancontroller.DialView
android:id="@+id/dialView"
android:layout_width="@dimen/fan_dimen"
android:layout_height="@dimen/fan_dimen"
app:layout_constraintTop_toBottomOf="@+id/customViewLabel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginLeft="@dimen/default_margin"
android:layout_marginRight="@dimen/default_margin"
android:layout_marginTop="@dimen/default_margin" />- برنامه را اجرا کنید. نمای کنترل فن شما در فعالیت ظاهر می شود.

وظیفه نهایی این است که نمای سفارشی خود را فعال کنید تا زمانی که کاربر روی نما ضربه می زند، عملی را انجام دهد. هر ضربه باید نشانگر انتخاب را به موقعیت بعدی منتقل کند: خاموش-1-2-3 و برگشت به خاموش. همچنین، اگر انتخاب 1 یا بالاتر است، پسزمینه را از خاکستری به سبز تغییر دهید، که نشان میدهد برق فن روشن است.
برای اینکه نمای سفارشی خود را فعال کنید تا قابل کلیک باشد، شما:
- ویژگی
isClickableview را رویtrueتنظیم کنید. این نمای سفارشی شما را قادر میسازد تا به کلیکها پاسخ دهد. - برای اجرای عملیات زمانی که روی view کلیک می شود،
performClick()کلاسViewرا پیاده سازی کنید. - متد
invalidate()را فراخوانی کنید. این به سیستم اندروید میگوید که متدonDraw()را برای ترسیم مجدد view فراخوانی کند.
به طور معمول، با یک نمای استاندارد اندروید، OnClickListener() را پیاده سازی می کنید تا زمانی که کاربر روی آن نمای کلیک می کند یک عمل انجام دهد. برای نمای سفارشی، به جای آن، متد performClick () کلاس View را پیاده سازی کرده و super را فراخوانی می کنید. performClick(). روش پیشفرض performClick() نیز onClickListener() فراخوانی میکند، بنابراین میتوانید اقدامات خود را به performClick() اضافه کنید و onClickListener() برای سفارشیسازی بیشتر توسط شما یا توسعهدهندگان دیگری که ممکن است از نمای سفارشی شما استفاده کنند، در دسترس بگذارید.
- در
DialView.kt، در داخل شمارشFanSpeed، یک تابع افزونهnext()اضافه کنید که سرعت فن فعلی را به سرعت بعدی در لیست تغییر می دهد (ازOFFبهLOW،MEDIUM، وHIGH، و سپس بهOFFبرمی گردد). اکنون شمارش کامل به این صورت است:
private enum class FanSpeed(val label: Int) {
OFF(R.string.fan_off),
LOW(R.string.fan_low),
MEDIUM(R.string.fan_medium),
HIGH(R.string.fan_high);
fun next() = when (this) {
OFF -> LOW
LOW -> MEDIUM
MEDIUM -> HIGH
HIGH -> OFF
}
}- در داخل کلاس
DialView، درست قبل از متدonSizeChanged()یک بلوکinit()اضافه کنید. تنظیم ویژگیisClickableview روی true، این نما را قادر می سازد تا ورودی کاربر را بپذیرد.
init {
isClickable = true
}- در زیر
init(),متدperformClick()با کد زیر لغو کنید.
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
contentDescription = resources.getString(fanSpeed.label)
invalidate()
return true
}فراخوان super . performClick() باید ابتدا اتفاق بیفتد، که رویدادهای دسترسی و همچنین فراخوانی onClickListener() فعال می کند.
دو خط بعدی سرعت فن را با متد next() افزایش می دهد و توضیحات محتوای view را روی منبع رشته ای تنظیم می کند که سرعت فعلی را نشان می دهد (خاموش، 1، 2 یا 3).
در نهایت، متد invalidate() کل view را باطل میکند و یک فراخوانی به onDraw() برای ترسیم مجدد view را مجبور میکند. اگر چیزی در نمای سفارشی شما به هر دلیلی از جمله تعامل کاربر تغییر کرد و نیاز به نمایش آن باشد، invalidate().
- برنامه را اجرا کنید. روی عنصر
DialViewضربه بزنید تا نشانگر از حالت خاموش به 1 منتقل شود. صفحه باید سبز شود. با هر ضربه، نشانگر باید به موقعیت بعدی حرکت کند. وقتی نشانگر خاموش شد، صفحه باید دوباره خاکستری شود.


این مثال مکانیزم های اساسی استفاده از ویژگی های سفارشی با نمای سفارشی شما را نشان می دهد. شما ویژگی های سفارشی را برای کلاس DialView با رنگ های مختلف برای هر موقعیت شماره گیری فن تعریف می کنید.
-
res/values/attrs.xmlرا ایجاد و باز کنید. - در داخل
<resources>، یک عنصر منبع<declare-styleable>اضافه کنید. - در داخل عنصر منبع
<declare-styleable>، سه عنصرattr، یکی برای هر ویژگی، باnameوformatاضافه کنید.formatمانند یک نوع است و در این موردcolorاست.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DialView">
<attr name="fanColor1" format="color" />
<attr name="fanColor2" format="color" />
<attr name="fanColor3" format="color" />
</declare-styleable>
</resources>- فایل layout
activity_main.xmlرا باز کنید. - در
DialView، ویژگیهایی را برایfanColor1،fanColor2وfanColor3اضافه کنید و مقادیر آنها را روی رنگهای نشانداده شده در زیر تنظیم کنید. ازapp:به عنوان پیشگفتار برای ویژگی سفارشی (مانندapp:fanColor1) به جایandroid:زیرا ویژگی های سفارشی شما به جای نامandroidبه فضای نامschemas.android.com/apk/res/your_app_package_nameتعلق دارد.
app:fanColor1="#FFEB3B"
app:fanColor2="#CDDC39"
app:fanColor3="#009688"برای استفاده از ویژگی ها در کلاس DialView ، باید آنها را بازیابی کنید. آنها در یک AttributeSet ذخیره می شوند که در صورت وجود، پس از ایجاد به کلاس شما تحویل داده می شود. شما ویژگیها را در init بازیابی میکنید و مقادیر مشخصهها را برای ذخیرهسازی به متغیرهای محلی اختصاص میدهید.
- فایل کلاس
DialView.ktرا باز کنید. - در داخل
DialView، متغیرها را برای ذخیره مقادیر مشخصه ها اعلام کنید.
private var fanSpeedLowColor = 0
private var fanSpeedMediumColor = 0
private var fanSeedMaxColor = 0- در بلوک
init، کد زیر را با استفاده از تابع افزونهwithStyledAttributesاضافه کنید. شما ویژگی ها و view را ارائه می کنید و متغیرهای محلی خود را تنظیم می کنید. وارد کردنwithStyledAttributesتابعgetColor()مناسب را نیز وارد می کند.
context.withStyledAttributes(attrs, R.styleable.DialView) {
fanSpeedLowColor = getColor(R.styleable.DialView_fanColor1, 0)
fanSpeedMediumColor = getColor(R.styleable.DialView_fanColor2, 0)
fanSeedMaxColor = getColor(R.styleable.DialView_fanColor3, 0)
}- از متغیرهای محلی در
onDraw()برای تنظیم رنگ شماره گیری بر اساس سرعت فن فعلی استفاده کنید. خطی را که رنگ رنگ تنظیم شده است (paint.color=if(fanSpeed== FanSpeed.OFF) Color.GRAYelseColor.GREEN) با کد زیر جایگزین کنید.
paint.color = when (fanSpeed) {
FanSpeed.OFF -> Color.GRAY
FanSpeed.LOW -> fanSpeedLowColor
FanSpeed.MEDIUM -> fanSpeedMediumColor
FanSpeed.HIGH -> fanSeedMaxColor
} as Int- برنامه خود را اجرا کنید، روی شماره گیری کلیک کنید، و تنظیمات رنگ باید برای هر موقعیت متفاوت باشد، همانطور که در زیر نشان داده شده است.
|
|
|
|
برای کسب اطلاعات بیشتر درباره ویژگیهای نمای سفارشی، به ایجاد کلاس مشاهده مراجعه کنید.
دسترسپذیری مجموعهای از تکنیکهای طراحی، پیادهسازی و آزمایش است که برنامه شما را قادر میسازد برای همه، از جمله افراد دارای معلولیت، قابل استفاده باشد.
ناتوانیهای رایجی که میتواند استفاده فرد از دستگاه اندرویدی را تحت تأثیر قرار دهد شامل نابینایی، کمبینایی، کوررنگی، ناشنوایی یا کاهش شنوایی و محدودیتهای مهارتهای حرکتی است. وقتی برنامههای خود را با در نظر گرفتن قابلیت دسترسی توسعه میدهید، تجربه کاربری را نه تنها برای کاربران دارای این ناتوانیها، بلکه برای همه کاربران دیگرتان نیز بهتر میکنید.
Android چندین ویژگی دسترسپذیری را بهطور پیشفرض در نمایهای رابط کاربری استاندارد مانند TextView و Button فراهم میکند. با این حال، هنگامی که یک نمای سفارشی ایجاد میکنید، باید در نظر بگیرید که چگونه آن نمای سفارشی ویژگیهای قابل دسترس مانند توضیحات گفتاری محتوای روی صفحه را ارائه میکند.
در این کار با TalkBack، صفحهخوان Android، آشنا میشوید و برنامهتان را طوری تغییر میدهید که نکات و توضیحات قابلگفتنی را برای نمای سفارشی DialView شامل شود.
مرحله 1. TalkBack را کاوش کنید
TalkBack صفحهخوان داخلی اندروید است. با فعال کردن TalkBack، کاربر میتواند بدون دیدن صفحه با دستگاه Android خود تعامل داشته باشد، زیرا Android عناصر صفحه را با صدای بلند توصیف میکند. کاربرانی که دارای اختلالات بینایی هستند ممکن است برای استفاده از برنامه شما به TalkBack متکی باشند.
در این کار، TalkBack را فعال میکنید تا بفهمد صفحهخوانها چگونه کار میکنند و چگونه برنامهها را پیمایش کنید.
- در دستگاه یا شبیهساز Android، به تنظیمات > دسترسپذیری > TalkBack بروید.
- برای روشن کردن TalkBack روی دکمه روشن/خاموش ضربه بزنید.
- برای تایید مجوزها روی OK ضربه بزنید.
- در صورت درخواست رمز عبور دستگاه خود را تأیید کنید. اگر این اولین باری است که TalkBack را اجرا می کنید، یک آموزش راه اندازی می شود. (این آموزش ممکن است در دستگاه های قدیمی تر در دسترس نباشد.)
- ممکن است مفید باشد که آموزش را با چشمان بسته هدایت کنید. برای باز کردن مجدد آموزش در آینده، به تنظیمات > دسترسی > TalkBack > تنظیمات > راه اندازی آموزش TalkBack بروید.
- برنامه
CustomFanControllerرا کامپایل و اجرا کنید، یا آن را با دکمه Overview یا Recents در دستگاه خود باز کنید. با روشن بودن TalkBack، توجه کنید که نام برنامه و همچنین متن برچسبTextView("کنترل فن") اعلام شده است. با این حال، اگر روی خود نمایDialViewضربه بزنید، هیچ اطلاعاتی در مورد وضعیت نما (تنظیم فعلی برای شماره گیری) یا عملکردی که هنگام ضربه زدن روی نما برای فعال کردن آن انجام می شود، بیان نمی شود.
مرحله 2. توضیحات محتوا را برای برچسب های شماره گیری اضافه کنید
توضیحات محتوا معنی و هدف نماها را در برنامه شما توصیف می کند. این برچسبها به خوانندگان صفحه مانند ویژگی TalkBack Android اجازه میدهند تا عملکرد هر عنصر را به طور دقیق توضیح دهند. برای نماهای ثابت مانند ImageView ، میتوانید توضیحات محتوا را با ویژگی contentDescription به نمای فایل طرحبندی اضافه کنید. نماهای متن ( TextView و EditText ) به طور خودکار از متن موجود در نمای به عنوان توضیحات محتوا استفاده می کنند.
برای نمای کنترل پنکه سفارشی، باید هر بار که روی نما کلیک میشود، توضیحات محتوا را بهصورت پویا بهروزرسانی کنید تا تنظیمات فعلی فن نشان داده شود.
- در پایین کلاس
DialView، یک تابعupdateContentDescription()بدون آرگومان یا نوع بازگشتی اعلام کنید.
fun updateContentDescription() {
}- در داخل
updateContentDescription()، ویژگیcontentDescriptionرا برای نمای سفارشی به منبع رشته مرتبط با سرعت فعلی فن (خاموش، 1، 2 یا 3) تغییر دهید. اینها همان برچسب هایی هستند که درonDraw()زمانی که شماره گیری روی صفحه کشیده می شود استفاده می شود.
fun updateContentDescription() {
contentDescription = resources.getString(fanSpeed.label)
}- به بلوک
init()بروید و در انتهای آن بلوک یک فراخوانی بهupdateContentDescription()اضافه کنید. هنگامی که نما مقدار دهی اولیه می شود، توضیحات محتوا را مقداردهی اولیه می کند.
init {
isClickable = true
// ...
updateContentDescription()
}- یک فراخوان دیگر به
updateContentDescription()در متدperformClick()درست قبل ازinvalidate()اضافه کنید.
override fun performClick(): Boolean {
if (super.performClick()) return true
fanSpeed = fanSpeed.next()
updateContentDescription()
invalidate()
return true
}- برنامه را کامپایل و اجرا کنید و مطمئن شوید TalkBack روشن است. برای تغییر تنظیمات نمای شماره گیری ضربه بزنید و توجه کنید که اکنون که TalkBack برچسب فعلی را اعلام می کند (خاموش، 1، 2، 3) و همچنین عبارت «برای فعال کردن، دو ضربه سریع بزنید».
مرحله 3. اطلاعات بیشتری را برای عمل کلیک اضافه کنید
میتوانید در آنجا توقف کنید و نمای شما در TalkBack قابل استفاده باشد. اما اگر نمای شما بتواند نه تنها نشان دهد که میتوان آن را فعال کرد («برای فعالسازی دو ضربه سریع بزنید») بلکه توضیح دهید که هنگام فعال شدن نما چه اتفاقی میافتد («برای تغییر، دو ضربه سریع بزنید.» یا «برای بازنشانی، دو ضربه بزنید.»)
برای انجام این کار، اطلاعات مربوط به عملکرد نما (در اینجا، یک عمل کلیک یا ضربه زدن) به یک شی اطلاعات گره دسترسی، از طریق یک نماینده دسترسی، اضافه می شود. یک نماینده دسترسپذیری به شما امکان میدهد ویژگیهای مرتبط با دسترسی برنامه خود را از طریق ترکیب (به جای ارثی) سفارشی کنید.
برای این کار از کلاسهای دسترسی در کتابخانههای Android Jetpack ( androidx.* ) استفاده خواهید کرد تا از سازگاری با عقب اطمینان حاصل کنید.
- در
DialView.kt، در بلوکinit، یک نماینده دسترسپذیری را به عنوان یک شیءAccessibilityDelegateCompatجدید تنظیم کنید. در صورت درخواستandroidx.core.view.ViewCompatوandroidx.core.view.AccessibilityDelegateCompatرا وارد کنید. این استراتژی بیشترین میزان سازگاری رو به عقب را در برنامه شما فعال می کند.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
})- در داخل شی
AccessibilityDelegateCompat، تابعonInitializeAccessibilityNodeInfo()را با یک شیAccessibilityNodeInfoCompatنادیده بگیرید و متد super را فراخوانی کنید. وقتی از شما خواسته شدandroidx.core.view.accessibility.AccessibilityNodeInfoCompatرا وارد کنید.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
}
})هر نما دارای درختی از گره های دسترسی است که ممکن است با اجزای طرح بندی واقعی نما مطابقت داشته باشد یا نباشد. سرویسهای دسترسپذیری Android آن گرهها را برای یافتن اطلاعات مربوط به نما (مانند توضیحات محتوای قابل گفتن یا اقدامات احتمالی که میتوان در آن نما انجام داد.) پیمایش میکند. هنگامی که یک نمای سفارشی ایجاد میکنید، ممکن است لازم باشد اطلاعات گره را نادیده بگیرید تا اطلاعات سفارشی برای دسترسی ارائه کنید. در این حالت شما اطلاعات گره را نادیده می گیرید تا نشان دهید که اطلاعات سفارشی برای عملکرد view وجود دارد.
- در داخل
onInitializeAccessibilityNodeInfo()یک شیAccessibilityNodeInfoCompat.AccessibilityActionCompatجدید ایجاد کنید و آن را به متغیرcustomClickاختصاص دهید. ثابتAccessibilityNodeInfo.ACTION_CLICKو یک رشته نگهدارنده را به سازنده منتقل کنید. هنگام درخواست،AccessibilityNodeInfoرا وارد کنید.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
"placeholder"
)
}
})کلاس AccessibilityActionCompat یک عمل در یک view برای اهداف دسترسی را نشان می دهد. یک عمل معمولی یک کلیک یا ضربه است، همانطور که در اینجا استفاده می کنید، اما اقدامات دیگر می تواند شامل به دست آوردن یا از دست دادن فوکوس، عملیات کلیپ بورد (برش/کپی/پیست) یا پیمایش در نما باشد. سازنده این کلاس به یک ثابت کنش (در اینجا، AccessibilityNodeInfo.ACTION_CLICK ) و رشته ای نیاز دارد که TalkBack برای نشان دادن چیستی عمل استفاده می کند.
- برای بازیابی یک منبع رشته، رشته
"placeholder"را با یک فراخوانی بهcontext.getString()جایگزین کنید. برای منبع خاص، سرعت فعلی فن را تست کنید. اگر سرعت در حال حاضرFanSpeed.HIGHاست، رشته"Reset"است. اگر سرعت فن هر چیز دیگری باشد، رشته"Change."این منابع رشته ای را در مرحله بعد ایجاد خواهید کرد.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH) R.string.change else R.string.reset)
)
}
})- پس از بسته شدن پرانتز برای تعریف
customClick، از متدaddAction()برای افزودن عملکرد دسترسی جدید به شی اطلاعات گره استفاده کنید.
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View,
info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
val customClick = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfo.ACTION_CLICK,
context.getString(if (fanSpeed != FanSpeed.HIGH)
R.string.change else R.string.reset)
)
info.addAction(customClick)
}
})- در
res/values/strings.xml، منابع رشته ای را برای «Change» و «Reset» اضافه کنید.
<string name="change">Change</string>
<string name="reset">Reset</string>- برنامه را کامپایل و اجرا کنید و مطمئن شوید TalkBack روشن است. اکنون توجه داشته باشید که عبارت «برای فعال کردن دو بار ضربه بزنید» اکنون یا «دو بار ضربه بزنید تا تغییر دهید» (اگر سرعت فن کمتر از زیاد یا 3 باشد) یا «دو ضربه بزنید تا بازنشانی شود» (اگر سرعت فن از قبل بالا است یا 3). توجه داشته باشید که درخواست "دوبار ضربه زدن به..." توسط خود سرویس TalkBack ارائه می شود.
دانلود کد برای کد لبه تمام شده..
$ git clone https://github.com/googlecodelabs/android-kotlin-drawing-custom-views
یا میتوانید مخزن را بهعنوان یک فایل Zip دانلود کنید، آن را از حالت فشرده خارج کنید و در Android Studio باز کنید.
- برای ایجاد یک نمای سفارشی که ظاهر و رفتار یک زیر کلاس
ViewمانندEditTextرا به ارث می برد، یک کلاس جدید اضافه کنید که آن زیر کلاس را گسترش می دهد و با نادیده گرفتن برخی از متدهای زیر کلاس تنظیمات را انجام دهید. - برای ایجاد نمای سفارشی با هر اندازه و شکل، یک کلاس جدید اضافه کنید که
Viewرا گسترش دهد. - برای تعریف شکل و ظاهر اصلی نما، روشهای
ViewمانندonDraw()را لغو کنید. - از
invalidate()برای ترسیم یا ترسیم مجدد view استفاده کنید. - برای بهینهسازی عملکرد، متغیرها را تخصیص داده و قبل از استفاده از آنها در
onDraw()مانند مقداردهی اولیه متغیرهای عضو، مقادیر مورد نیاز برای طراحی و نقاشی را اختصاص دهید. - برای ارائه رفتار تعاملی view، به جای
OnClickListener()performClick()به نمای سفارشی لغو کنید. این به شما یا سایر توسعه دهندگان اندرویدی که ممکن است از کلاس view سفارشی شما استفاده کنند، قادر می سازدonClickListener()برای ارائه رفتار بیشتر استفاده کنند. - نمای سفارشی را به یک فایل طرحبندی XML با ویژگیهایی اضافه کنید تا ظاهر آن را مشخص کنید، همانطور که با سایر عناصر UI انجام میدهید.
- برای تعریف ویژگی های سفارشی، فایل
attrs.xmlرا در پوشهvaluesایجاد کنید. سپس می توانید از ویژگی های سفارشی برای نمای سفارشی در فایل طرح بندی XML استفاده کنید.
دوره بی ادبی:
مستندات توسعه دهنده اندروید:
- ایجاد نماهای سفارشی
-
@JvmOverloads - اجزای سفارشی
- اندروید چگونه نماها را ترسیم می کند
-
onMeasure() -
onSizeChanged() -
onDraw() -
Canvas -
Paint -
drawText() -
setTypeface() -
setColor() -
drawRect() -
drawOval() -
drawArc() -
drawBitmap() -
setStyle() -
invalidate() - مشاهده کنید
- رویدادهای ورودی
- رنگ کنید
- کتابخانه افزونه کاتلین android-ktx
-
withStyledAttributes - مستندات Android KTX
- وبلاگ اعلان اصلی اندروید KTX
- نمایش های سفارشی را در دسترس تر کنید
-
AccessibilityDelegateCompat -
AccessibilityNodeInfoCompat -
AccessibilityNodeInfoCompat.AccessibilityActionCompat
ویدئوها:
این بخش، تکالیف احتمالی را برای دانشآموزانی که در این آزمایشگاه کد به عنوان بخشی از دورهای که توسط یک مربی هدایت میشود، کار میکنند، فهرست میکند. این وظیفه مربی است که موارد زیر را انجام دهد:
- در صورت نیاز تکالیف را تعیین کنید.
- نحوه ارسال تکالیف را با دانش آموزان در میان بگذارید.
- تکالیف را نمره دهید.
مربیان میتوانند از این پیشنهادات به اندازهای که میخواهند استفاده کنند، و باید با خیال راحت هر تکلیف دیگری را که فکر میکنند مناسب است، محول کنند.
اگر به تنهایی از طریق این کدها کار می کنید، از این تکالیف برای آزمایش دانش خود استفاده کنید.
سوال 1
برای محاسبه موقعیتها، ابعاد و هر مقدار دیگری که برای اولین بار به نمای سفارشی یک اندازه اختصاص داده میشود، کدام روش را نادیده میگیرید؟
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ onDraw()
سوال 2
برای نشان دادن اینکه میخواهید نمای شما با onDraw() دوباره ترسیم شود، پس از تغییر مقدار مشخصه، کدام متد را از رشته UI فراخوانی میکنید؟
▢ onMeasure()
▢ onSizeChanged()
▢ invalidate()
▢ getVisibility()
سوال 3
برای افزودن تعامل به نمای سفارشی خود، کدام روش View باید نادیده بگیرید؟
▢ setOnClickListener()
▢ onSizeChanged()
▢ isClickable()
▢ performClick()
برای پیوند به دیگر کدلب ها در این دوره، صفحه فرود Advanced Android in Kotlin Codelabs را ببینید.



