مشاوره رایگان
دریافت لینک های دانلود دریافت پستی درب منزل مبلغ کل: تومان
جهت دریافت کد تخفیف به کانال تلگرام مراجعه و یا با پشتیبان آنلاین هماهنگ نمایید

آموزش ساخت view های سفارشی و ترسیم شکل روی آنها در اندروید


آموزش ساخت view های سفارشی و ترسیم شکل روی آنها در اندروید

 

در این مقاله ما یک view سفارشی شده ایجاد می کنیم. همچنین می آموزیم که چطور می توان روی view ها به واسطه ی canvas نقاشی کرد. view را به صورت واکنشی (responsive) طراحی می کنیم. برای view صفات xml جدیدی می سازیم و همچنین حالت view را نگه می داریم تا با چرخش گوش به حالت اولیه باز نگردد.

 

سیستم عامل Android دارای چندین کلاس View است که نیازهای یک برنامه معمولی را پوشش می دهد.  اما گاهی اوقات این view ها متناسب با نیازهای شما نیستند و شما به دلایلی مانند دلایل زیر نیاز به view های سفارشی شده پیدا می کنید.

  • طراحی ابتکاری و یا انیمیشن
  • تعامل با کاربر به شکلی متفاوت از حالت معمول
  • نمایش انواع مختلف داده ها
  • برخی از بهینه سازی ها در عملکرد برنامه
  • قابلیت استفاده مجدد از کدی که می نویسید

 

این آموزش را با ساخت view ی سفارشی شده ای شروع می کنیم که به شکل یک چهره ای احساسی است و به میل کاربر به چهره ای شاد و یا غمگین تبدیل می شود و از این طریق می آموزیم که چگونه می توان صفات جدیدی برای کد xml یک view تعریف کرد و چگونه با کمک canvas روی view اشکال و خطوط ترسیم کرد.

 

پیش نیازها: این آموزش تماماً در مورد view های سفارشی شده است پس خواننده باید آشنایی اولیه با برنامه نویسی اندروید، زبان کاتلین، xml و اندروید استودیو داشته باشد. اگر اطلاعات اولیه در این زمینه ها ندارید ابتدا از سایر آموزش های داخل سایت استفاده کنید. برای پیگیری این آموزش ، باید از Android Studio 3.0.1 یا بالاتر و Kotlin 1.2.21 یا بالاتر استفاده کنید هرچند به احتمال زیاد نسخه روی رایانه شما جدیدتر از این نسخه است و در این زمینه مشکلی نخواهید داشت.

 

 

کار با view های اصلی

 

اندروید دارای مجموعه ای از view های (ابزارک های) اصلی است و کلاس پایه هر ابزارک Android کلاس View (با حرف V بزرگ) است. تصویر زیر بخشی از سلسله مراتب view ها را نشان می دهد:

 

دو روش برای ایجاد یک view از  اندروید و تنظیم مقادیر برای ویژگی های آن وجود دارد:

  1. از طریق فایل xml در فایل های layout
  2. از طریق کد جاوا و یا کاتلین

 

 

کار با view ها در کاتلین

 

از طریق کد کاتلین می توان view به فایل layout اضافه کرد. مثلا در کد زیر TextView را بجای فایل layout قرار می دهیم. در اکتیویتی اصلی خط setContentView(R.layout.activity_main) در متد onCreate را با کد زیر جایگزین کنید:

val email = TextView(this)  // 1
email.text = "Hello Custom Views" // 2
setContentView(email) // 3
  1. در اینجا شما یک TextView به کمک context بدست آمده از اکتیویتی می سازید
  2. بجای متن، عبارت « Hello Custom Views » را قرار می دهید
  3. TextView را بجای محتوای صفحه قرار می دهید

اگر برنامه را build و اجرا کنید می بینید که عبارت « Hello Custom Views » روی صفحه نقش می بندد. مثل تصویر زیر:

 

 

کار با view ها در فایل xml

 

حالا فایل res/layout/activity_main.xml را باز کنید. برای استفاده از یکی از view های اصلی اندروید مانند TextView ، فقط کافی است آن را از پنجره سمت چپ به داخل ویرایشگر بکشید و آن را روی ویرایشگر بیندازید ، یا به ویرایشگر متن XML بروید و خطوط زیر را به کد XML اضافه کنید:

<TextView
  android:id="@+id/email"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Hello Custom Views"/>

می توانید بسیاری از ویژگی های اساسی از کلاس View را در XML تغییر دهید. برخی از این صفات عبارتند از: id, layout_width, layout_height, alpha, visibility, elevation, padding, tag,  و غیره.

برای تغییر یک ویژگی ، مانند متن در TextView ، فقط نام ویژگی (android: text) را اضافه کنید و مانند آخرین خط قطعه کد قبلی ، مقداری را به آن اختصاص دهید (مثل متنی که من در کد وارد کرده ام: "Hello Custom Views").

اگر در مرحله قبل ()onCreate را تغییر داده اید، برای استفاده از فایل xml لایوت باید کد setContentView (R.layout.activity_main) را به ()onCreate باز گردانید برای اینکار onCreate در MainActivity را به حالت اولیه باز گردانید و کدی را که قبلاً اضافه کرده اید بردارید. پروژه را Build و اجرا کنید. عبارت "Hello Custom Views" را روی صفحه خود مشاهده خواهید کرد ، مانند تصویر:

 

 

View ها در اندروید

 

کلاس View عنصر اصلی سازنده رابط کاربری در اندروید است. هر view برای ترسیم خود وفرزندانش (view های داخلش البته در صورتی که view از نوع ViewGroup باشد) یک فضای مستطیل شکل را در صفحه اشغال می کند. در ضمن هر view مسئول رسیدگی به رخدادهایش (مثل رخداد کلیک) نیز هست.

ViewGroup زیر کلاس (فرزند)  کلاس View است. ViewGroup کلاس پدر (کلاس پایه) تمامی کلاس های Layout در اندروید است. ViewGroup ها به عنوان  ظرفی برای قرار دهی سایر View ها و  ViewGroup ها در مکان دلخواه در layout استفاده می شوند. اگر رابطه View و ViewGroup را متوجه نشده اید تصویر زیر را با دقت ببینید فلش ها از کلاس فرزند به طرف کلاس پدر است:

 

 

View و ViewGroup های سفارش شده

 

View سفارشی شده یعنی چه؟

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

بیایید فرض کنیم بین View های اصلی ارائه شده از سوی اندروید ما View دکمه (Button) نداریم ولی در هنگام ساخت برنامه به آن نیاز پیدا کرده ایم. ما باید یک View که به نیازمان نزدیکتر است برای سفارشی کردن انتخاب کنیم در اینجا بهترین انتخاب TextView هست چون با این انتخاب می توانیم از ویژگی های قبلاً ارائه شده در TextView مثل متن، رنگ متن، اندازه متن وغیره استفاده کنیم و مقدار کدی که خودمان به آن اضافه می کنیم را کاهش دهیم. بعد از این انتخاب شروع به سفارش کردن TextView می کنیم تا ظاهری شبیه دکمه پیدا کند. این دقیقاً همان کاری است که اندروید در ساخت View ها انجام داده اگر تصویر قبلی راببینید بهتر متوجه می شوید. در آن تصویر  Button که از TextView ارث بری می کند گونه شخصی سازی شده ای از آن است.

البته بجای بهینه سازی یک view خاص می توان از کلاس پایه ای View ارث بری کرد و از پایه تمام کد های لازم را نوشت.

 

ViewGroup سفارشی شده یعنی چه؟

بعضی وقتها نیاز هست چند view را در یک مجموعه واحد قرار داد تا به صورت یک عنصر  با یکدیگر تبادل اطلاعات داشته باشند و این مجموعه واحد را بتوان در جاهای مختلف استفاده کرد و از تکرار کد جلوگیری کرد. می توان آن را «Compound View»  یا « ویووی ترکیبی » نامید.

به عنوان مثال ، ممکن است بخواهید یک نوار کشویی و یک چهره احساسی ایجاد کنید که کاربر بتواند نوار را به سمت راست بکشد تا چهره  را شادتر کند یا به سمت چپ بکشد تا چهره را غم انگیز تر کند. در ضمن ممکن است بخواهید میزان شادی چهره را نیز به صوت متنی در یک TextView نشان دهید. در اینجا ما سه view نیاز داریم (ImageView, SeekBar, TextView) که به هم وابسته اند.  می توان این سه view را داخل یک فایل layout قرار داد سپس کلاسی ایجاد کرد که از یکی از لایوت های اولیه اندروید (مثلاً LinearLayout یا RelativeLayout و غیره) ارث بری می کند و در این کلاس کد مربوط به تغییر حالت چهره و ... را نوشت.

بجز مورد بالا دلیل دیگری که می توان برای سفارشی کردن ViewGroup ذکر کرد ایجاد روش جدیدی برای مرتب سازی و قرار دادن view ها در صفحه است مثلاً قرار دادن  view ها به صورت دایره ای بجای خطی در LinearLayout .

 

 

اندروید چگونه view ها را در صفحه رسم می کند

 

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

اندروید ترسیم اکتیویتی را از بالاترین والد (که والد کل layout است) آغاز می کند. سپس فرزندان والد را رسم می کند. حالا اگر یکی از فرزندان خودش والد باشد و فرزندانی داشته باشد اندروید تمام فرزندان این والد را نیز قبل از گذر از آن رسم می کند. به این روش پیمایش بین والدها و فرزندها حالت پیمایش اول عمق می گویند.

اندروید فرزندان هر ViewGroup را به ترتیبی که قرار داده اید رسم می کند پس view ای که اول اضافه شده باشد (در فایل xml بالاتر باشد) زودتر رسم می شود.

 

اندروید layout را در سه مرحله ترسیم می کند:

  1. مرحله اندازه گیری: هر view باید خودش را بسنجد.
  2. مرحله چیدمان: هر ViewGroup با استفاده از اندازه فرزند و همچنین با پیروی از دستورات ، موقعیت مناسب فرزندان خود را بر روی صفحه پیدا می کند.
  3. مرحله ترسیم: بعد از اندازه گیری و موقعیت یابی تمام viewها ، هر view خودش را ترسیم می کند.

 

 

ساخت یک view سفارشی

 

سرانجام وقت آن است که خودتان شروع به ساخت view سفارشی کنید!

با ایجاد یک کلاس جدید Kotlin در package اصلی برنامه شروع کنید و نام آن را EmotionalFaceView بگذارید. آنرا از کلاس View ارث ببرید:

class EmotionalFaceView : View

حالا اگر نشانگر موس را روی کلمه View ببرید پیغام زیر را می بینید:

“This type has a constructor, and thus must be initialized here”

یعنی باید به سازنده کلاس View آرگمان های لازم را ارسال کنید.

 

 

سازنده کلاس View

 

کلاس View چهار سازنده دارد و برای سفارشی کردن آن حداقل یکی از سازنده ها را باید override کنید. هر چهار مورد را در زیر مقایسه می کنیم تا موردی که نیاز دارید را انتخاب کنید:

 

  1. constructor(context: Context) برای ساختن یک view جدید در کد کاتلین یا جاوا ولی نیاز به context از اکتیویتی دارد.
  2. constructor(context: Context, attrs: AttributeSet) برای ساخت یک view از فایل xml
  3. constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) مثل حالت دوم برای ساخت یک view از فایل xml ولی با داشتن یک style .
  4. constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) مثل حالت دوم و سوم برای ساخت یک view از فایل xml ولی با داشتن یک style یا style resource .

 

سازنده دوم را انتخاب کنید تا امکان استفاده از view سفارشی شده در فایل xml فراهم باشد. می توانید به شکل زیر از این سازنده استفاده کنید:

constructor(context: Context, attrs: AttributeSet): super(context, attrs)

یا آن را به عنوان سازنده اصلی کلاس به صورت زیر استفاده کنید:

class EmotionalFaceView(context: Context, attrs: AttributeSet) : View(context, attrs)

حالا می توانید این view سفارشی شده را به صورت زیر در فایل xml استفاده کنید:

<!--Full path for the cusom view -->
<com.test.emotionalface.EmotionalFaceView
   android:id="@+id/emotionalFaceView"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:layout_centerInParent="true"
   android:layout_below="@+id/textView" />

تبریک اولین view سفارشی شده خودتان را ساختید و در فایل xml آن را بکار گرفتید. ولی هنوز موارد سفارشی مورد نظرتان را به آن اضافه نکرده اید.

پروژه را Build و اجرا کنید. همانطور که انتظار دارید تغییری در نتیجه نمی بینید. ولی نگران نباشید بخش جالب کار را همین الان شروع می کنیم.

 

 

ترسیم روی Canvas

 

با تعریف متغیر Paint و متغیر های زیر داخل کلاس EmotionalFaceView  ابزارهای طراحی و نقاشی و رنگ را برای خود فراهم می کنیم:

// Paint object for coloring and styling
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// Some colors for the face background, eyes and mouth.
private var faceColor = Color.YELLOW
private var eyesColor = Color.BLACK
private var mouthColor = Color.BLACK
private var borderColor = Color.BLACK
// Face border width in pixels
private var borderWidth = 4.0f
// View size in pixels
private var size = 320

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

override fun onDraw(canvas: Canvas) {
  // برای حفظ طراحی هایی که قبلا انجام شده حتماً در اینجا متد والد را به صورت زیر فراخوانی کنید
  super.onDraw(canvas)
}

هشدار! متد onDraw از جمله متدهایی است که اندروید در هر ثانیه چندین بار فراخوانی می کند چون برای ترسیم در صفحه به این متد نیاز دارد. پس از انجام کارهای زمانبر (مثل کار با شبکه، دیتابیس، فایل، محاسبات سنگین و...) یا کارهایی که حافظه مصرف می کنند (مثل ساختن یک شی جدید) داخل این متد پرهیز کنید در غیر اینصورت ممکن است شاهد گیر کردن برنامه تان باشید. در این برنامه آزمایشی ما ممکن است برای سادگی بعضی از شی ها را داخل این متد ایجاد کنیم ولی در برنامه واقعی باید شی ها را در سازنده کلاس view بسازید و سپس در این متد فقط مقادیر آنها را تغییر دهید. اگر داخل این متد شی جدیدی بسازید اندروید استودیو نیز با زرد کردن کد شما این هشدار را به شما می دهد.

برای ترسیم چهره خندان سه متد جدید به کلاس خود اضافه کنید (متدهای ترسیم گردی صورت، چشمان و دهان) هر سه متد هم باید پارامتر canvas یا همان بوم نقاشی را بگیرند. حالا هر سه متد را داخل متد onDraw  فراخوانی کنید ولی پس زمینه صورت را قبل از دو متد دیگر فراخوانی کنید تا روی چشم و دهان را با صورت نپوشانید و به دلیل مشابه متد والد را نیز قبل از این سه متد فراخوانی کنید. به صورت زیر:

override fun onDraw(canvas: Canvas) {
   // برای حفظ طراحی هایی که قبلا انجام شده حتماً در اینجا متد والد را به صورت زیر فراخوانی کنید 

 super.onDraw(canvas)

  drawFaceBackground(canvas)
  drawEyes(canvas)
  drawMouth(canvas)
}

private fun drawFaceBackground(canvas: Canvas) {
}

private fun drawEyes(canvas: Canvas) {
}

private fun drawMouth(canvas: Canvas) {
}

 

  • ترسیم پس زمینه صورت

کد زیر را داخل متد drawFaceBackground قرار دهید:

// 1
 paint.color = faceColor
 paint.style = Paint.Style.FILL

 // 2
 val radius = size / 2f

 // 3
 canvas.drawCircle(size / 2f, size / 2f, radius, paint)

 // 4
 paint.color = borderColor
 paint.style = Paint.Style.STROKE
 paint.strokeWidth = borderWidth

 // 5
 canvas.drawCircle(size / 2f, size / 2f, radius - borderWidth / 2f, paint)

توضیحات کد بالا:

  1. تنظیم رنگ استفاده شده برای نقاشی برابر رنگ داخل متغیر faceColor  و تنظیم آن به صورتی که کل محدوده نقاشی شده را رنگ کند (بجای ترسیم خط).
  2. محاسبه شعاع برای دایره ای که می خواهیم به عنوان پس زمینه چهره ترسیم کنیم.
  3. دایره پس زمینه را با مرکز (x، y) ، در حالی که x و y برابر با نصف size هستند (چون باید در مرکز view قرار بگیرد) ، و با شعاع محاسبه شده رسم می کنیم.
  4. تنظیم رنگ استفاده شده برای نقاشی برابر رنگ داخل متغیر borderColor  و تغییر تنظیمات paint به شکلی که خط رسم کند (بجای شکل توپر). و سپس تنظیم ضخامت خط.
  5. ترسیم یک خط مرزی با مرکزیت مرکز دایره ولی با شعاعی کمتر از شعاع دایره.

 

اگر برنامه را Build و اجرا کنید باید تصویر زیر را ببینید:

 

  • رسم بیضی برای چشم ها

کد زیر را داخل متد ()drawEyes قرار بدید:

// 1
paint.color = eyesColor
paint.style = Paint.Style.FILL

// 2
 val leftEyeRect = RectF(size * 0.32f, size * 0.23f, size * 0.43f, size * 0.50f)

canvas.drawOval(leftEyeRect, paint)

// 3
val rightEyeRect = RectF(size * 0.57f, size * 0.23f, size * 0.68f, size * 0.50f)

canvas.drawOval(rightEyeRect, paint)

توضیحات کد بالا:

  1. رنگ استفاده شده برای نقاشی را برابر eyesColor  قرار می دهد و آن را به صورتی تنظیم می کند که کل محوطه نقاشی شده را رنگ کند (حالت توپر بجای ترسیم خط).
  2. یک شی از کلاس RectF می سازد (کلاس RectF برای تعریف مستطیل با اندازه های اعشاری کاربرد دارد). مکان های رئوس مستطیل را به صورت کسری از size قرار می دهد. سپس چشم چپ را با کمک این مستطیل به صورت یک بیضی رسم می کند.
  3. چشم راست را مشابه چشم چپ ولی در مکانی متفاوت رسم می کند.

 

برنامه را Build و اجرا کنید اکنون باید به صورت زیر باشد:

  • ترسیم منحنی برای دهان

برای رسم خطوط خمیده بر روی canvas به یک شی از جنس Path نیاز است. متغیر کلاسی زیر را به کلاس EmotionalFaceView  اضافه کنید:

private val mouthPath = Path()

بعد از ساختن شی mouthPath دستورالعمل های ترسیم آن را با افزودن کدهای زیر داخل متد drawMouth ایجاد کنید:

// 1
mouthPath.moveTo(size * 0.22f, size * 0.7f)
// 2
mouthPath.quadTo(size * 0.50f, size * 0.80f, size * 0.78f, size * 0.70f)
// 3
mouthPath.quadTo(size * 0.50f, size * 0.90f, size * 0.22f, size * 0.70f)
// 4
paint.color = mouthColor
paint.style = Paint.Style.FILL
// 5
canvas.drawPath(mouthPath, paint)

توضیحات کد بالا:

  1. با فراخوانی متد ()moveTo نقطه شروع مسیر را برابر (x0,y0) که x0 برابر ۲۲٪ متغیر size است و y0 برابر ۷۰٪ متغیر size است.
  2. یک مسیر منحنی از نقطه شروع و از طریق (x1 ، y1) رسم می کند که با (x2 ، y2) به پایان می رسد که در آن x1 برابر ۵۰٪ و y1 برابر ۸۰٪ متغیر size است و x2 برابر ۷۸٪ و y2 برابر ۷۰٪ متغیر size است.
  3. یک مسیر منحنی از آخرین نقطه پایان (x2 ، y2) و تا (x3 ، y3) شروع کرده و با (x0، y0) به پایان می رساند که در آن x3 برابر ۵۰٪ و y3 برابر ۹۰٪ متغیر size است و x0 برابر ۲۲٪ و y0 برابر ۷۰٪  متغیر size است.
  4. رنگ نقاشی را برابر متغیر mouthColor  قرار می دهد و آن را تنظیم می کند تا به صورت توپر رسم شود.
  5. مسیر را روی canvas رسم می کند.

 

برنامه را Build و اجرا کنید باید به صورت زیر باشد:

 

 

تغییر view از حالت ثابت به حالت واکنشی

 

در حال حاضر ، view سفارشی شما دارای اندازه ثابت است ، اما شما می خواهید واکنشی باشد و متناسب با اندازه والد خود یا صفحه گوشی تغییر کند. در ضمن شما می خواهید که چهره خندان همیشه به صورت دایره باشد و با تغییر اندازه صفحه به صورت بیضی کشیده نشود.

اندروید عرض و ارتفاع view را اندازه گیری می کند. شما می توانید به این مقادیر از طریق measuredWidth, measuredHeight دسترسی پیدا کنید برای ارائه اندازه دقیق و کارآمد از view به اندروید متد onMeasure را داخل  view باید override کنید:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { 

     super.onMeasure(widthMeasureSpec, heightMeasureSpec)
      // 1
      size = Math.min(measuredWidth, measuredHeight)
     // 2
     setMeasuredDimension(size, size)

}

توضیحات کد بالا:

  1. ضلع کوچکتر view را پیدا می کند.
  2. با فراخوانی setMeasuredDimension(int, int) با دو آرگمان برابر برای عرض و ارتفاع شکل view به صورت مربع با طول و عرض برابر می شود.

 

اگر برنامه را Build و اجرا کنید به صورت زیر می شود:

 

 

ایجاد صفات xml سفارشی شده

 

برای ایجاد صفات جدید (که توسط اندروید تعریف نشده) داخل پوشه res/values فایلی با نام دلخواه مثلاً attrs.xml بسازید و کد زیر را به آن اضافه کنید:

<!--1-->
<declare-styleable name="EmotionalFaceView">
  <!--2-->
  <attr name="faceColor" format="color" />
  <attr name="eyesColor" format="color" />
  <attr name="mouthColor" format="color" />
  <attr name="borderColor" format="color" />
  <attr name="borderWidth" format="dimension" />
  <attr name="state" format="enum">
    <enum name="happy" value="0" />
    <enum name="sad" value="1" />
  </attr>
</declare-styleable>

توضیحات کد بالا:

  1. تگ declare-styleable باز می شود که مقدار داخل name آن برابر نام کلاس view سفارش شده ما است.
  2. ویژگی های جدید را با نام های مختلف اضافه می کند و نوع آنها را نیز به شکل مناسب تنظیم می کند.

 

حالا که این صفات را تعریف کردید به فایل res/layout/activity_main.xml  بروید و کد آن را به صورت زیر تغییر دهید:

<com.test.emotionalface.EmotionalFaceView
   android:id="@+id/happyButton"
   android:layout_width="@dimen/face_button_dimen"
   android:layout_height="@dimen/face_button_dimen"
   android:layout_alignParentLeft="true"
   android:layout_alignParentStart="true"
   app:borderColor="@color/white"
   app:eyesColor="@color/white"
   app:faceColor="@color/red"
   app:mouthColor="@color/white"
   app:state="happy" />

<com.test.emotionalface.EmotionalFaceView
   android:id="@+id/sadButton"
   android:layout_width="@dimen/face_button_dimen"
   android:layout_height="@dimen/face_button_dimen"
   android:layout_alignParentEnd="true"
   android:layout_alignParentRight="true"
   app:borderColor="@color/black"
   app:eyesColor="@color/black"
   app:faceColor="@color/light_grey"
   app:mouthColor="@color/black"
   app:state="sad" />

در کد بالا به صفات جدیدی که برای EmotionalFaceView استفاده کرده اید  نیز توجه کنید:

 borderColor, eyesColor, state, mouthColor,  faceColor 

در این کد از دو EmotionalFaceView  در فایل layout استفاده کردید به این صورت از کدی که یک بار نوشتید می توانید بارها استفاده کنید.  استفاده مجدد از view های سفارشی شده یکی از فواید آنها است.

چهره اول حالت شاد "state="happy  و چهره دوم حالت غمگین "state="sad  دارد. از این دو چهره کوچک در ادامه بجای دکمه برای تغییر حالت چهره بزرگتر که در وسط تصویر قرار دارد استفاده می کنیم.

 

اگر برنامه را Build و اجرا کنید تصویر زیر را می بینید:

همانطور که می بینید صفات جدید xml فعلاً هیچ تاثیری روی EmotionalFaceView ندارند.

 

برای دریافت مقدار صفات داخل فایل xml و استفاده از آنها در EmotionalFaceView کدهای داخل کلاس که برای مقدار دهی به متغیر ها است (کدهای خارج متدها) را به صورت زیر تغییر دهید (متد هایی که قبلاً به کلاس اضافه کردیم را بدون تغییر نگه دارید):



// 1
companion object {
  private const val DEFAULT_FACE_COLOR = Color.YELLOW
  private const val DEFAULT_EYES_COLOR = Color.BLACK
  private const val DEFAULT_MOUTH_COLOR = Color.BLACK
  private const val DEFAULT_BORDER_COLOR = Color.BLACK
  private const val DEFAULT_BORDER_WIDTH = 4.0f

  const val HAPPY = 0L
  const val SAD = 1L
}

// 2
private var faceColor = DEFAULT_FACE_COLOR
private var eyesColor = DEFAULT_EYES_COLOR
private var mouthColor = DEFAULT_MOUTH_COLOR
private var borderColor = DEFAULT_BORDER_COLOR
private var borderWidth = DEFAULT_BORDER_WIDTH

private val paint = Paint()
private val mouthPath = Path()
private var size = 0

// 3
var happinessState = HAPPY
  set(state) {
    field = state
    // 4
    invalidate()
  }

// 5
init {
  paint.isAntiAlias = true
  setupAttributes(attrs)
}

private fun setupAttributes(attrs: AttributeSet?) {
  // 6
  // Obtain a typed array of attributes
  val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.EmotionalFaceView,
      0, 0)

  // 7
  // استخراج مقدار صفات جدیدی که تعریف کرده بودیم از فایل ایکس ام ال
  happinessState = typedArray.getInt(R.styleable.EmotionalFaceView_state, HAPPY.toInt()).toLong()
  faceColor = typedArray.getColor(R.styleable.EmotionalFaceView_faceColor, DEFAULT_FACE_COLOR)
  eyesColor = typedArray.getColor(R.styleable.EmotionalFaceView_eyesColor, DEFAULT_EYES_COLOR)
  mouthColor = typedArray.getColor(R.styleable.EmotionalFaceView_mouthColor, DEFAULT_MOUTH_COLOR)
  borderColor = typedArray.getColor(R.styleable.EmotionalFaceView_borderColor,
      DEFAULT_BORDER_COLOR)
  borderWidth = typedArray.getDimension(R.styleable.EmotionalFaceView_borderWidth,
      DEFAULT_BORDER_WIDTH)

  // 8
  // TypedArray objects are shared and must be recycled.
  typedArray.recycle()
}

توضیحات کد بالا:

  1. دو ثابت یکی برای حالت شاد HAPPY و دیگری برای حالت غمگین SAD اضافه می کند.
  2. مقدار پیش فرضی برای صفات جدیدمان در نظر می گیریم که اگر در فایل لایوت xml این صفات وارد نشده بود از این مقادیر پیش فرض استفاده شود.
  3. یک صفت جدید با نام happinessState  برای حالت شادی چهره اضافه می کند.
  4. پس از اعمال تغییرات با فراخوانی متد invalidate درخواست رسم مجدد view از اندروید می کنیم. با اینکار اندروید متد onDraw  را مجدداً فراخوانی می کند.
  5. متد جدید setupAttributes  که تعریف کردیم را در سازنده کلاس فراخوانی می کنیم تا پس از ساخته شدن view صفات از فایل xml خوانده شود.
  6. از صفات xml یک آرایه از نوع TypedArray می گیرد.
  7. ویژگی های سفارشی شده را در متغیرها ذخیره می کند.
  8. typedArray را بازیافت می کند تا فضای ذخیره شده برای نگهداری اطلاعات داخل این آرایه آزاد شود و حافظه اضافی مصرف نشود.

اگر برنامه را Build و اجرا کنید، تصویر زیر را می بینید:

همانطوری که در تصویر بالا می بینید صفت happinessState  همچنان بی تاثیر است زیر هر دو صورت های کوچک حالت چهره شاد را نشان می دهند در صورتی که در فایل xml مقدار صفت state  برای یکی از آنها sad بود.

برای رفع این مشکل باید متد drawMouth  را ویرایش کنیم. در اولین خط از این متد کد زیر را اضافه کنید:

mouthPath.reset()

با اینکار قبل از رسم اگر مسیری از قبل داخل mouthPath باشد پاک می شود تا مسیر از ابتدا رسم شود. چون اندروید متد onDraw را در هر ترسیم (که ممکن است چندبار در ثانیه باشد) چند بار فراخوانی می کند این کار باعث می شود که یک مسیر چند بار رسم نشود.

حالا می خواهیم بر اساس happinessState  چهره را شاد یا غمگین رسم کنیم. کد زیر این کار را انجام می دهد:

if (happinessState == HAPPY) {
 // منحنی لازم برای دهان شاد
 mouthPath.quadTo(size * 0.5f, size * 0.80f, size * 0.78f, size * 0.7f)
 mouthPath.quadTo(size * 0.5f, size * 0.90f, size * 0.22f, size * 0.7f)
} else {
 // منحنی لازم برای دهان ناراحت
 mouthPath.quadTo(size * 0.5f, size * 0.50f, size * 0.78f, size * 0.7f)
 mouthPath.quadTo(size * 0.5f, size * 0.60f, size * 0.22f, size * 0.7f)
}

با این تغییر کد داخل drawMouth به صورت زیر می شود:



private fun drawMouth(canvas: Canvas) {

  // Clear
  mouthPath.reset()

  mouthPath.moveTo(size * 0.22f, size * 0.7f)

  if (happinessState == HAPPY) {
    // منحنی لازم برای دهان شاد
    mouthPath.quadTo(size * 0.5f, size * 0.80f, size * 0.78f, size * 0.7f)
    mouthPath.quadTo(size * 0.5f, size * 0.90f, size * 0.22f, size * 0.7f)
  } else {
    // منحنی لازم برای دهان ناراحت
    mouthPath.quadTo(size * 0.5f, size * 0.50f, size * 0.78f, size * 0.7f)
    mouthPath.quadTo(size * 0.5f, size * 0.60f, size * 0.22f, size * 0.7f)
  }

  paint.color = mouthColor
  paint.style = Paint.Style.FILL

  // Draw mouth path
  canvas.drawPath(mouthPath, paint)
}

اگر برنامه را Build و اجرا کنید می بینید که تصویر صورت کوچک خاکستری رنگ به صورت چهره غمگین می شود:

 

 

تعامل کاربر با برنامه

 

می توانید برنامه را به شکلی تغییر دهید که با کلیک کاربر رو چهره های کوچک خندان یا غمگین چهره بزرگ وسطی خندان یا غمگین شود.  در ابتدا کد زیر را به MainActivity اضافه کنید:

import kotlinx.android.synthetic.main.activity_main.*

با این افزونه در کاتلین دیگر نیازی به findViewById ندارید و برای استفاده از هر view در کدتان کافی است id آن view را استفاده کنید.  حالا کد زیر را برای رسیدگی به رخداد کلیک روی چهره به onCreate در MainActivity اضافه کنید:

// 1
happyButton.setOnClickListener({
   emotionalFaceView.happinessState = EmotionalFaceView.HAPPY//تغییر چهره اصلی به حالت شاد
})
// 2
sadButton.setOnClickListener({
   emotionalFaceView.happinessState = EmotionalFaceView.SAD//تغییره چهره اصلی به حالت غمگین
})

در کد بالا برحسب اینکه کاربر روی کدام صورت کلیک می کند حالت چهره اصلی را تغییر می دهیم.

 

برنامه را Build و اجرا کنید و روی هر دو دکمه کلیک کنید و تغییر حالت چهره را مشاهده کنید:

 

 

نگهداری حالت view

 

برای نگهداری از حالت view در صورت تغییر در پیکربندی دستگاه مثل چرخش صفحه باید متدهای onSaveInstanceState و onRestoreInstanceState را override کنید به صورت نمونه زیر:

override fun onSaveInstanceState(): Parcelable {
 // 1
 val bundle = Bundle()
 // 2
 bundle.putLong("happinessState", happinessState)
 // 3
 bundle.putParcelable("superState", super.onSaveInstanceState())
 return bundle
}

override fun onRestoreInstanceState(state: Parcelable) {
 // 4
 var viewState = state
 if (viewState is Bundle) {
   // 5
   happinessState = viewState.getLong("happinessState", HAPPY)
   // 6
   viewState = viewState.getParcelable("superState")
 }
 super.onRestoreInstanceState(viewState)
}

// Clicksite
// Email: clicksite.ir@gmail.com

توضیحات کد بالا:

  1. یک Bundle جدید برای ذخیره داده ها در آن می سازد
  2. مقدار happinessState را در آن ذخیره می کند
  3. برای اینکه هیچ اطلاعاتی از کلاس پایه (کلاس پدر) این کلاس از دست نرود اطلاعات مربوط به آن را نگهداری می کند
  4. نوع Parcelable  را بررسی و آن را تبدیل به Bundle می کند
  5. مقدار happinessState  را بازیابی می کند
  6. اطلاعات مربوط به کلاس پایه را بدون تغییر به کلاس پایه می دهد

 

برنامه را Build و اجرا کنید سپس حالت چهره را از شاد تبدیل به غمگین کنید و گوشی را بچرخانید حالت چهره پس از چرخش گوشی باید مثل قبل از چرخش غمگین بماند مثل تصویر زیر:

 

در انتها برخی از کارهای مهمی که در این مثال انجام دادید را در زیر می بینید:

 

  • دایره، بیضی و مسیر روی Canvas رسم کردید.
  • View را تغییر دادید تا به تغییر اندازه ها به شکل مناسب واکنش نشان دهد.
  • صفات جدیدی که اندروید نداشت برای view سفارشی خودتان تعریف کردید.
  • حالت view را ذخیره کردید تا پس از چرخش گوشی به حالت اولیه باز نگردد.
  • View سفارش شده تان را برای مقاصد مختلف و در جاهای مختلفی استفاده کردید.

 

 

 

 

 

فصلِ: 20 , تعداد قسمت ها: 238 , سطح: صفر تا صد

این فصل در یک نگاه:

فصل رایگان مربوط به مفاهیم php و api نویسی …

توضیحات کلی مجموعه: دوره متخصص اندروید کلیک سایت کامل ترین دوره جامع برنامه نویسی اندروید(حتما دمو دوره را ببینید)   چرا دوره متخصص جایگزین دوره صفرتاصد اندروید شد؟ کلیک سایت تصمیم گرفت …
فصلِ: 30 , تعداد قسمت ها: 182 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ساخت اپلیکیشن علی بابا-آموزش کالبک ها-…

دوره "متخصص" جایگزین دوره صفرتاصد اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دوره صفرتاصد شده است؟چرا باید دوره متخص…
فصلِ: 11 , تعداد قسمت ها: 121 , سطح: صفر تا صد

این فصل در یک نگاه:

عملیات شبیه فیلتر-ساخت الرت دیالوگ وچیدمان براساس فروش-مرتب سازی-نحوه خطایابی-استفاده از bottom sheet در اندروید-سبد خری…

آموزش ساخت اپلیکیشن اندروید دیجی کالا  : ساخت برنامه اندروید یکی از پر طرفدارترین آموزش های کلیک سایت می باشد. با توجه به فراگیر شدن سیستم اندروید و آشنایی افراد با این سیستم ، برنامه های ک…
فصلِ: 3 , تعداد قسمت ها: 179 , سطح: صفر تا صد

این فصل در یک نگاه:

در این دوره آموزشی ، برنامه نویسی اندروید و ios را با استفاده از زامارین خواهیم آموخت. همچنین به صورت کاملا پروژه محور ، اپلیکیشن فروشگاهی مشابه دیجی کالا را پیاده سازی خواهیم کرد. در انته…
فصلِ: 2 , تعداد قسمت ها: 58 , سطح: صفر تا صد

این فصل در یک نگاه:

توضیحات کلی مجموعه: سرفصل های دوره آموزش ساخت اپلیکیشن فیلیمو معرفی اهداف دوره مقدمه و بررسی پیش نیازهای دوره بررسی جزئی و …
فصلِ: 1 , تعداد قسمت ها: 9 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ساخت api key-آموزش استفاده از GPS گوشی ونمایش مکان شخص برروی نقشه-متصل کردن چند نقطه برروی نقشه با خط-رسم چند ضلعی…

حتما دمو دوره رو ببینید اولین دوره کامل کار با نقشه گوگل ومسیریابی در نقشه گوگل(این اموزش مشابه فارسی ندارد) اموزش کار با نقشه گوگل در برنامه نویسی اندروید یکی از مهم ترین مباحث هست که …
فصلِ: 1 , تعداد قسمت ها: 14 , سطح: صفر تا صد

این فصل در یک نگاه:

ساخت انواع نوتیفیکیشن ها -کار با وب سرویس پوشه-ارسال نوتیفیکیشن با سرویس پوشه-ارسال نوتیفیکیشن با one signal-اموزش کار ب…

با این مجموعه همه چیز در مورد نوتیفیکیشن و فایربیس رو خواهید اموخت یکی از مواردی که در استخدام کاربران در شرکت های برنامه نویسی بسیار موثر هست مبحث کار با firebase هست که ما دراین دوره ام…
فصلِ: 1 , تعداد قسمت ها: 16 , سطح: صفر تا صد

این فصل در یک نگاه:

لیست کردن فایل های صوتی ویدیویی گوشی-طاحی متریال وزیبا-استفاده از تب بندی-حرکت سیک بار همراه با اهنگ وزمان-ست کردن زمان …

توضیحات کلی مجموعه: اموزش ساخت یک موزیک پلیر و ویدیو پلیر ح رفه ای که کاملا کاربردی است و قابلیت نصب روی گوشی های مختلف را دارد از جمله امکانات این اپلیکیشن: لیست کردن فایل های صوتی وید…
فصلِ: 1 , تعداد قسمت ها: 61 , سطح: صفر تا صد

این فصل در یک نگاه:

اموزش ساخت اپلیکیشن کافه بازار-اشتراک گذاری اپلیکیشن-کار با رتروفیت-کاربا ران تیم پرمیشن- کار با فرگمنت ها-کار با sqlite…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دو…
فصلِ: 1 , تعداد قسمت ها: 9 , سطح: صفر تا صد

این فصل در یک نگاه:

پرداخت درون برنامه ای بازار-اموزش فروش سکه-اموزش فروش اشتراک-اموزش فروش نسخه پولی-اموزش چک کردن خرید کردن کاربر-اموزش کا…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 1 , تعداد قسمت ها: 20 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش کامل پیکربندی mvp-اموزش اتصال با سرور با رتروفیت و rxjava-اموزش کار با دیتابیس-آموزش کار با bottom navigation-اموز…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 1 , تعداد قسمت ها: 35 , سطح: متوسط

این فصل در یک نگاه:

این آموزش در یک فصل شامل 35 جلسه آموزش تصویری به همراه سورس کد ارائه شده است.…

سرفصل ها مقدمه و معرفی متد آموزشی بررسی جزئی و خلاصه موارد موردنیاز پیش از آموزش شامل: کار با ویوها،  کار با کتابخانه های مختلف و ... …
فصلِ: 1 , تعداد قسمت ها: 1 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ارسال پیامک با سامانه مدیر پیامک-ارسال کد فعال سازی برنامه با پیامک-اعتبارسنجی کد فعال سازی-ثبت نام در اپلیکیشن با…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 2 , تعداد قسمت ها: 19 , سطح: صفر تا صد

این فصل در یک نگاه:

آموزش ارسال توکن در اندروید -اموزش امنیت در برنامه نویسی اندروید-اموزش ارسال توکن در هدر در اندروید-اموزش ساخت token -ام…

توضیحات کلی مجموعه: دموی دوره را حتما ببینید دوره امنیت و دیزاین پترن در برنامه نویسی اندروید که شامل دو فصل هست. فصل اول شامل اموزش singletone design patern-builder design patern-command …
فصلِ: 2 , تعداد قسمت ها: 44 , سطح: صفر تا صد

این فصل در یک نگاه:

در فصل دوم این دوره بیشتر میپردازیم به کار با کتابخانه ها و موارد پیشرفته تر مانند دوربین ، ضبط صدا، دیتا بیس ، و .....…

توضیحات کلی مجموعه: سلام خدمت کلیک سایتی های عزیز مجموعه آموزشی صفر تا صد برنامه نویسی اندروید در محیط بیسیک 4 اندروید (basic 4 android) معرفی محیط بیسیک 4 اندروید محیط b4a  تحت کمپانی A…
فصلِ: 3 , تعداد قسمت ها: 61 , سطح: صفر تا صد

این فصل در یک نگاه:

فصل جدید و اپدیت های جدید-آموزش کار با برودکست ریسیور در کاتلین-آموزش چک کردن اتصال به اینترنت در کاتلین-اموزش اپلود کرد…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 5 , تعداد قسمت ها: 21 , سطح: متوسط

این فصل در یک نگاه:

نحوه ارورگیری-رفع ایراد-رفع مشکل-اشتراک گذاری مطالب-ساخت اکتیویتی تنظیمات-اشتراک گذاری مطالب-جستجو کردن…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 5 , تعداد قسمت ها: 128 , سطح: صفر تا صد

این فصل در یک نگاه:

پرداخت درون برنامه ای بازار با کاتلین…

دوره "متخصص" جایگزین دوره های اندروید شد. برای دیدن دوره متخصص اندروید بر روی لینک زیر کلیک کنید : https://b2n.ir/d36082 چرا دوره متخصص جایگزین دیگر دوره های اندروید شده است؟چرا باید دوره…
فصلِ: 8 , تعداد قسمت ها: 62 , سطح: صفر تا صد

این فصل در یک نگاه:

اپلود تصاویر روی سرور نود جی اس-کار با کانال در چت- ساخت کانال-ساخت گروه-تفاوت کانال و گروه-انلاین بودن-ارسال تصاویر در …

  توضیحات کلی مجموعه آموزش اندروید اپلیکیشن چت مشابه تلگرام( با استفاده از Socket IO ): دموی مجموعه را حتما ببینید دراین دوره سعی میشود بسیاری از اپشن هایی که اپلیکیشن موبوگرام داراست ر…
فصلِ: 6 , تعداد قسمت ها: 194 , سطح: صفر تا صد

این فصل در یک نگاه:

در این فصل آپدیت های مربوط به دوره را قرار میدهیم…

با سلام و خسته نباشد خدمت کلیک سایتی های عزیز در ادامه با توضیحات مختصری درمورد دوره ی react native با ما همراه باشید: React Native چیست؟ قطعا یکی از آرزوهای برنامه نویسان این میباشد که ب…

تولید شده توسط کلیک سایت

پشتیبانی آنلاین
آماده پاسخگویی هستیم
انتخاب تصویر جهت ارسال:
در حال ضبط صدا

(جهت توقف و یا لغو ضبط از دکمه های زیر استفاده کنید)

توقف و ارسال :
لغو ضبط
در حال حاضر تمامی کارشناسان آفلاین هستند. همواره می توانید با شماره تلگرام / واتساپ 09010005000 به صورت آنلاین با ما در ارتباط باشید. جهت ورود به واتساپ کلیک کنید
0 پیام جدید
پشتیبان در حال تایپ ...
ارسال تصویر ضبط صدا
0 کارشناسان آنلاین می باشند
این گفت و گو توسط پشتیبان به اتمام رسید