ایجاد نمای سفارشی (Custom View) در اندروید – از صفر تا صد


پلتفرم اندروید بازه وسیعی از ویجتهای رابط کاربری را ارائه کرده است که برای نیازهای اغلب اپلیکیشنها کافی هستند. این ویجتها عالی هستند و محصولات نهایی کاملاً کاربردی و زیبایی را میسازند؛ اما در پارهای موارد توسعهدهندگان نرمافزار تمایل دارند فراتر از این بیندیشند و رابطهای سفارشی خاص خود را بسازند. بهترین روش برای پاسخ دادن به این خلاقیت، ساخت نمای سفارشی است.
در این نوشته به تعریف و برسی نماهای سفارشی و سپس بخش جذابتر یعنی نمایش آنها خواهیم پرداخت.
اصطلاحات مهم
در آغاز باید برخی اصطلاحهای مقدماتی را برای درک بهتر تعریف کنیم.
نمای اندروید (Android View)
نمای اندروید یا Android View یک کلاس مبنا برای ساخت رابط کاربری است که به توسعهدهندگان فرصتی برای ایجاد طراحیهای پیچیده میدهد. این نما ناحیهای مستطیلی روی صفحه اشغال میکند و مسئول اندازهگیری، طرحبندی و ترسیم خود در راستای عناصر فرزندش است. به علاوه نماها همه ورودیهای کاربر را مدیریت میکنند.
گروه نما (ViewGroup)
یک گروه نما یا ViewGroup، نمای خاصی است که توانایی گنجاندن نماهای دیگر (فرزندان) را در خود دارد و مشخصات طرحبندی خاص خود را تعریف میکند. این نما همچنین محلی است که هر نمای فرعی (subview) میتواند از آن مشتق شود.
نمای سفارشی (Custom View)
هر نمایی که خارج از ویجت مبنای اندروید ایجاد شود را میتوان یک نمای سفارشی یا Custom View دانست. در این نوشته کل توجه ما روی این نمای سفارشی است.
روشهای پیادهسازی
روشهای بسیار مختلفی برای پیادهسازی نماهای سفارشی وجود دارند و رویکردی که انتخاب میشود به نیازهای شما بستگی دارد. در ادامه برخی روشها را مورد بررسی قرار میدهیم:
بسط دادن ویجت اندروید موجود
این روش زمانی که حجم بالایی از کد راهاندازی برای نمای شما لازم باشد و بخواهید از آن در جاهای مختلفی استفاده کنید مناسب خواهد بود. برای اجتناب از درج کد شلوغ درون اکتیویتی یا فرگمان، میتوانید ویجت مبنا را بسط داده و همه کارهای راهاندازی را درون سازنده انجام دهید، از این رو میتوان به سادگی از آن مجدداً استفاده کرد. این روش به طور بدیهی سادهترین رویکرد برای پیادهسازی نماهای سفارشی محسوب میشود.
بسط دادن نمای مبنای اندروید
اگر میخواهید ابتکار به خرج بدهید و همه کارها را از صفر خودتان اجرا کنید، این روش مناسب خواهد بود. در این روش شما همه منطق رفتاری ویجت را خودتان رسم، اندازهگیری و برنامهریزی میکنید.
گروهبندی نماهای موجود با هم
در پارهای اوقات مجموعهای از ویجتها دارید که میخواهید آنها را با هم گروهبندی کنید تا یک نمای کاملاً جدید ایجاد کنید. برای نمونه فرض کنید یک Textview و یک Button دارید و میخواهید آنها را درون یک LinearLayout گروهبندی کنید. این نما عموماً به نام «نمای ترکیبی» (Compound View) شناخته میشود. مزیتهای این کار به شرح زیر هستند:
- یک منطق کپسولهسازی شده و متمرکز داریم.
- میتوانیم از تکرار شدن کد جلوگیری کنیم.
- کد قابلیت استفاده مجدد و ماژولار بودن پیدا میکند.
اندروید نماها را چگونه رسم میکند؟
در این بخش به توضیح روش اندروید برای رسم نماها میپردازیم. در آغاز سه مرحله وجود دارند که پیش از نمایش نهایی نما روی صفحه باید اجرا شوند. این سه مرحله، اندازهگیری، طرحبندی و رسم هستند. هر یک از مراحل به صورت پیمایش «عمق-اول» (depth-first) سلسلهمراتب نما است که از والد به سمت فرزند حرکت میکند. در هر مرحله متدی وجود دارند که میتواند بسته به نیازها override شده و تغییر یابد. این فرایند را میتوان به دو مرحله تقسیم کرد:
- مرحله اندازهگیری و طرحبندی
- مرحله رسم
مرحله اندازهگیری و طرحبندی
در این مرحله، این فرصت را داریم که به سیستم اندروید اعلام کنیم میخواهیم اندازه نمای سفارش چه قدر باشد و این اندازه به محدودیتهای تعیین شده از سوی والد بستگی دارد.
نمودار شمارهگذاری شده زیر شیوه اندازهگیری هر نما را در هر گام نمایش میدهد:
نمای فرزند اقدام به تعریف LayoutParams به صورت برنامهنویسی شده و یا در XML میکند و والد این مقادیر را با استفاده از متد ()getLayoutParams بازیابی میکند.
والد، MeasureSpecs را محاسبه کرده و آن را با استفاده از ()child.measure در سلسلهمراتب به سمت پایین ارسال میکند. Measurespecs شامل حالت و مقدار است.
سه حالت برای اندازهگیری وجود دارند:
- EXACTLY – یک اندازه دقیق مانند تنظیم عرض/ارتفاع برابر با 50 dp یا match_parent استفاده میشود.
- AT_MOST – والد اندازه بیشینهای که فرزند میتواند اتخاذ کند را تعیین میکند. این حالتی است که هنگام تعیین عرض/ارتفاع برابر با wrap_content استفاده میشود.
- UNSPECIFIED – اندازه مشخصی وجود ندارد و فرزند در مورد اندازه آزادانه عمل میکند.
متد ()onMeasure به همراه پارامترهای MeasureSpecs فراخوانی میشود. در این متد View اقدام به محاسبه عرض/ارتفاع مطلوب میکند و آن را با استفاده از setMeasuredDimension تنظیم میکند. به خاطر داشته باشید که متد setMeasuredDimension باید درون measure فراخوانی چون در غیر این صورت موجب بروز استثنای زمان اجرا میشود.
مرحله بعد و آخر مرحله طرحبندی است. در این مرحله، والد ()child.layout را فراخوانی کرده و اندازه نهایی و موقعیت فرزند را تعیین میکند. زمانی که نمای سفارشی را پیادهسازی میکنید، در صورتی که نمای شما، نماهای فرعی دیگری داشته باشد، باید تنها متد ()onLayout را override کنید.
در نتیجه فرایند اندازهگیری مانند یک مذاکره بین یک والد و فرزند است. فرزند، عرض و ارتفاع مطلوب را محاسبه میکند اما والد کسی است که فراخوانی نهایی را برای تنظیم موقعیت و اندازه فرزندش انجام میدهد.
مرحله رسم
آخرین و مهمترین مرحله در رسم نمای سفارشی override کردن متد ()onDraw است. بوم یک کلاس مبنا است که متدهای زیادی برای رسم متن، bitmap-ها، خطوط و دیگر اشکال ابتدایی گرافیکی عرضه کرده است.
هر والد، خود را رسم میکند و سپس تقاضا میکند که هر فرزند نیز همان کار را انجام دهد. یک اثر جنبی جالب در زمانی است که والد ابتدا خود را رسم میکند و در انتها فرزندانش را روی خود رسم کرده و در واقع خود را میپوشاند.
ایجاد نمای سفارشی
اکنون نوبت بخشی رسیده است که مدتها منتظر آن بودیم و آن کدنویسی است. در ادامه به بررسی روش ایجاد یک نمای سفارشی با استفاده از «کاتلین» (Kotlin) میپردازیم. ما جهت مقاصد آموزشی یک شاخص باتری رسم میکنیم تا وضعیت کنونی باتری دستگاه را نمایش دهیم. نمودار زیر وضعیتهای مختلف یک باتری را نشان میدهد:
برای ایجاد یک نمای BatteryMeterView باید مراحل زیر را طی کنیم. یک پروژه اندروید استودیو ایجاد کرده و یک کلاس جدید به نام BatteryMeterView اضافه کنید.
آن را با استفاده از کلاس View بسط دهید و سازندهها را طوری اضافه کنید که با super مطابقت داشته باشند.
ما برای آمادهسازی رسم برخی شیءهای paint، رنگ و شکلها را اعلان میکنیم.
View-ی ما همانند یک ویجت ابتدایی باید مقداری آمادهسازی اولیه داشته باشد و همه مشخصهها نوعی مقدار پیشفرض داشته باشند. در ادامه یک شیء همراه درون BatteryMeterView ایجاد کرده و برخی ثابتها را به آن اضافه میکنیم.
پیش از رسم باتری روی صفحه، باید اندازه و موقعیت آن را بهروزرسانی کنیم. بهترین مکان برای مدیریت هر گونه تغییر اندازه درون متد onSizeChanged است. به این منظور مراحل زیر را طی کنید:
- عرض و ارتفاع محتوا را تنظیم کنید.
- اندازه متن مقدار باتری را به اندازه نصف ارتفاع محتوا تنظیم کنید.
- عرض سر باتری را 1/12 کل عرض باتری تنظیم کنید.
- موقعیت rect پسزمینه را تنظیم کنید.
- موقعیت rect سر باتری را تنظیم کنید.
- موقعیت rext سطح باتری را تنظیم کنید.
نکته: با توجه به مقاصد آموزشی این مقاله از برخی مقادیر ثابت برای حاشیه و آفست محتوا استفاده کردهایم.
اینک برای رسم BatteryMeter شروع به override کردن متد ()onDraw میکنیم.
- پسزمینه نما را رسم میکنیم.
- سر باتری را رسم میکنیم.
- کانتینری که سطح باتری در آن قرار میگیرد را رسم میکنیم.
- اکنون اگر باتری تغییر پیدا کند، یک لوگوی تغییر نمایش میدهیم و در غیر این صورت متن مقدار کنونی باتری را نمایش میدهیم.
به خاطر بسپارید که متد onDraw با بسامد 60 بار در ثانیه (fps 60) فراخوانی میشود و قرار دادن هر نوع عملیات محاسباتی سنگین و ایجاد شیء درون آن میتواند موجب کاهش عملکرد اپلیکیشن شود. برای اجتناب از این وضعیت، میتوانیم همه اشیا را درون سازندهها بسازیم و در صورت نیاز میتوانیم مشخصهها را در ادامه تغییر دهیم.
اکنون برای این که به باتری خود امکان تغییر در زمان اجرا بدهیم، باید متد ()invalidate را هر بار که حالت نما بهروزرسانی میشود فراخوانی کنیم. کاری که invalidate انجام میدهد این است که به اندروید اجازه میدهد بداند که نما خراب شده و نیاز به ترسیم مجدد دارد. لازم به ذکر است که باید مراقب باشید زیرا فراخوانی ()invalidate به تعداد زیاد موجب بروز مشکلاتی میشود.
مرحله نهایی، افزودن نمای باتری به Layout است:
به این ترتیب کار به پایان میرسد و ما اینک یک باتری سنج داریم که خودمان ایجاد کردهایم. برای مشاهده کد کامل پروژه به این لینک (+) مراجعه کنید.
مزایا و معایب پیادهسازی نماهای سفارشی
پیش از جمعبندی این مقاله بهتر است مزایا و معایب پیادهسازی نماهای سفارشی را نیز مورد اشاره قرار دهیم. همانند هر فرایند پیادهسازی دیگری، همواره مزیتها و معایبی وجود دارند، اما این موارد نباید شما را از امتحان کردن آن نا امید کنند.
مزایا
ظاهر/رفتار سفارشی: نمای سفارشی = سفارشیسازی. پلتفرم اندروید گسترده است، اما موارد خاصی وجود دارند که قابلیتهای نماهای اندروید نیازهای شما را برآورده نمیسازند و از این رو نمای سفارشی این فرصت را به شما میدهد که چیزی بسازید که متعلق به شما است. زمانی که نوبت به طراحی و تعامل میرسد، کنترل کاملی دارید زیرا نمای سفارشی گزینههای بینهایتی ارائه میکند.
قابلیت استفاده مجدد/قابلیت خوانایی: زمانی که اپلیکیشنهای بزرگمقیاس توسعه میدهید، قابلیت استفاده مجدد کد همواره موضوعی مورد علاقه است. زمانی که یک نمای سفارشی ایجاد میکنید، میتوانید به سادگی آن را در چند جای دیگر نیز در اپلیکیشن خود مورد استفاده قرار دهید.
عملکرد: در برخی موارد خاص، ساختن نمای سفارشی میتواند موجب مقداری بهبود عملکرد شود.
معایب
در این بخش برخی معایب نماهای سفارشی را بررسی میکنیم.
زمان و تلاش: ایجاد نماهای سفارشی کاری زمانبر است و استفاده از آن قطعاً میتواند دشوار باشد تا اینکه به کارکرد آنها آشنا شوید.
پشتکار: چند چیز وجود دارند که در زمان پیادهسازی نماهای سفارشی باید آگاه باشید. اول این که باید مطمئن باشید فونت، اندازه متن، رنگ، سایهها، هایلایت و استایل را به درستی مدیریت کردهاید. همچنین باید مطمئن شوید که نما روی همه تراکمهای نمایشی به درستی کار میکند چون زیرکلاس بوم اندروید برحسب پیکسل رسم میشود و نه DP. اگر با تصاویر کار میکنید باید به خاطر داشته باشید که نسبت تصویر، بزرگنمایی و مقیاسبندی درستی داشته باشد.
موارد زیادی که باید مدیریت شوند: همچنین باید همه انواع شنوندههای کلیک، تعاملهای کاربر، کلیکهای منفرد، دو بار کلیک، فشردن طولانی، سوایپ کردن و جابجایی را مدیریت کنید.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- آموزش Canvas در HTML — به زبان ساده و گام به گام
- مجموعه آموزشهای برنامهنویسی اندروید
- مجموعه آموزشهای برنامهنویسی
- گنجینه برنامهنویسی اندروید (Android)
- ۵ گام ضروری برای یادگیری برنامهنویسی اندروید — راهنمای جامع
- اپلیکیشن آنی اندروید (Android Instant App) چیست؟ — از صفر تا صد
==
ممنون
وقت گذاشتین این اموزش تهیه کردین
بعد از ران کردن برنامه این ویو به صورت ایستا بود و با وضعیت باتری دستگاه یکی نبود !
برای ست کردن با وضعیت باتری باید چی کار کنیم ، لطفا راهنمایی بکنید.