اجزای نمای سفارشی را ایجاد کنید

روش Compose را امتحان کنید
Jetpack Compose جعبه ابزار UI توصیه شده برای اندروید است. نحوه کار با طرح‌بندی‌ها در Compose را بیاموزید.

Android یک مدل کامپوننت‌شده پیچیده و قدرتمند برای ایجاد رابط کاربری شما بر اساس کلاس‌های چیدمان اصلی View و ViewGroup ارائه می‌کند. این پلتفرم شامل انواع زیر کلاس‌های از پیش ساخته View و ViewGroup است که به ترتیب ویجت‌ها و طرح‌بندی نامیده می‌شوند که می‌توانید از آنها برای ساختن رابط کاربری خود استفاده کنید.

فهرست جزئی ویجت‌های موجود شامل Button ، TextView ، EditText ، ListView ، CheckBox ، RadioButton ، Gallery ، Spinner ، و AutoCompleteTextView ، ImageSwitcher ، و TextSwitcher با هدف خاص‌تر است.

از میان طرح‌بندی‌های موجود می‌توان به LinearLayout ، FrameLayout ، RelativeLayout و موارد دیگر اشاره کرد. برای مثال‌های بیشتر، طرح‌بندی‌های رایج را ببینید.

اگر هیچ یک از ویجت ها یا طرح بندی های از پیش ساخته شده نیازهای شما را برآورده نمی کند، می توانید زیرکلاس View خود را ایجاد کنید. اگر فقط نیاز به انجام تنظیمات کوچک در یک ویجت یا چیدمان موجود دارید، می‌توانید ویجت یا طرح‌بندی را فرعی طبقه‌بندی کنید و روش‌های آن را لغو کنید.

ایجاد زیر کلاس های View خود به شما کنترل دقیقی بر ظاهر و عملکرد یک عنصر صفحه نمایش می دهد. برای ارائه ایده ای از کنترلی که با نماهای سفارشی دریافت می کنید، در اینجا چند نمونه از کارهایی که می توانید با آنها انجام دهید آورده شده است:

  • شما می توانید یک نوع View کاملا سفارشی رندر شده ایجاد کنید - برای مثال، یک دکمه "کنترل صدا"، که با استفاده از گرافیک دوبعدی رندر شده است، که شبیه یک کنترل الکترونیکی آنالوگ است.
  • می‌توانید گروهی از کامپوننت‌های View را در یک کامپوننت جدید ترکیب کنید، شاید چیزی شبیه یک جعبه ترکیبی (ترکیبی از فهرست بازشو و فیلد متن ورودی آزاد)، یک کنترل انتخابگر دو صفحه‌ای (یک صفحه چپ و راست با یک لیست) ایجاد کنید. در هر کدام از این موارد می‌توانید مجدداً آن مورد را در کدام لیست قرار دهید) و غیره.
  • شما می توانید روشی را که یک جزء EditText بر روی صفحه نمایش داده می شود لغو کنید. برنامه نمونه NotePad از این به خوبی برای ایجاد یک صفحه یادداشت خط دار استفاده می کند.
  • می‌توانید رویدادهای دیگر را ضبط کنید - مانند فشار دادن کلیدها - و آنها را به روشی سفارشی، مانند بازی، مدیریت کنید.

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

رویکرد اساسی

در اینجا یک نمای کلی از آنچه باید برای ایجاد کامپوننت های View خود بدانید در سطح بالا آورده شده است:

  1. یک کلاس View یا زیر کلاس موجود را با کلاس خود گسترش دهید.
  2. برخی از متدهای superclass را لغو کنید. متدهای سوپرکلاس برای نادیده گرفتن با on برای مثال onDraw() ، onMeasure() و onKeyDown() شروع می شوند. این شبیه رویدادهای on در Activity یا ListActivity است که برای چرخه حیات و سایر قلاب‌های عملکردی لغو می‌کنید.
  3. از کلاس افزونه جدید خود استفاده کنید. پس از تکمیل، می توانید از کلاس برنامه افزودنی جدید خود به جای نمای آن استفاده کنید.

اجزای کاملا سفارشی

شما می توانید اجزای گرافیکی کاملاً سفارشی سازی شده را ایجاد کنید که هر طور که می خواهید ظاهر شوند. شاید شما یک VU متر گرافیکی بخواهید که شبیه یک گیج آنالوگ قدیمی باشد، یا یک نمای متنی آوازخوانی که در آن هنگام آواز خواندن همراه با دستگاه کارائوکه، یک توپ جهنده در امتداد کلمات حرکت می کند. ممکن است چیزی بخواهید که اجزای داخلی قادر به انجام آن نباشند، مهم نیست که چگونه آنها را ترکیب می کنید.

خوشبختانه، شما می‌توانید اجزایی ایجاد کنید که به هر شکلی که می‌خواهید به نظر می‌رسند و رفتار می‌کنند، فقط با تخیل، اندازه صفحه، و قدرت پردازش موجود محدود می‌شوند، با در نظر گرفتن اینکه برنامه شما ممکن است مجبور باشد روی چیزی با قدرت بسیار کمتری اجرا شود. ایستگاه کاری دسکتاپ شما

برای ایجاد یک کامپوننت کاملا سفارشی، موارد زیر را در نظر بگیرید:

  • عمومی‌ترین نمایی که می‌توانید گسترش دهید View است، بنابراین معمولاً با گسترش آن برای ایجاد کامپوننت فوق‌العاده جدید خود شروع می‌کنید.
  • شما می‌توانید سازنده‌ای تهیه کنید که می‌تواند ویژگی‌ها و پارامترها را از XML بگیرد، و می‌توانید چنین ویژگی‌ها و پارامترهایی مانند رنگ و محدوده VU متر یا عرض و میرایی سوزن را مصرف کنید.
  • احتمالاً می‌خواهید شنونده‌های رویداد، دسترسی‌های دارایی و اصلاح‌کننده‌ها و همچنین رفتار پیچیده‌تری را در کلاس جزء خود ایجاد کنید.
  • تقریباً مطمئناً می‌خواهید که onMeasure() را نادیده بگیرید و همچنین اگر می‌خواهید کامپوننت چیزی را نشان دهد، احتمالاً باید onDraw() را لغو کنید. در حالی که هر دو دارای رفتار پیش‌فرض هستند، onDraw() پیش‌فرض هیچ کاری انجام نمی‌دهد، و پیش‌فرض onMeasure() همیشه اندازه 100x100 را تعیین می‌کند که احتمالاً شما نمی‌خواهید.
  • همچنین می‌توانید در صورت لزوم روش‌های دیگر on نادیده بگیرید.

گسترش onDraw() و onMeasure()

متد onDraw() یک Canvas ارائه می‌کند که می‌توانید هر چیزی را که می‌خواهید روی آن پیاده‌سازی کنید: گرافیک دوبعدی، سایر مؤلفه‌های استاندارد یا سفارشی، متن استایل‌دار یا هر چیز دیگری که فکرش را بکنید.

onMeasure() کمی بیشتر درگیر است. onMeasure() یک قطعه مهم از قرارداد رندر بین کامپوننت شما و ظرف آن است. onMeasure() باید برای گزارش کارآمد و دقیق اندازه‌گیری‌های قسمت‌های موجود در آن نادیده گرفته شود. این امر با الزامات حد از والد - که به متد onMeasure() منتقل می‌شوند - و با الزام فراخوانی متد setMeasuredDimension() با عرض و ارتفاع اندازه‌گیری شده پس از محاسبه، کمی پیچیده‌تر می‌شود. اگر این متد را از یک متد overrid شده onMeasure() فراخوانی نکنید، در زمان اندازه گیری منجر به یک استثنا می شود.

در سطح بالا، پیاده سازی onMeasure() چیزی شبیه به این است:

  • متد overridden onMeasure() با مشخصات عرض و ارتفاع فراخوانی می‌شود که به عنوان الزامات محدودیت‌های اندازه‌گیری عرض و ارتفاع شما در نظر گرفته می‌شود. پارامترهای widthMeasureSpec و heightMeasureSpec هر دو کدهای عدد صحیح هستند که ابعاد را نشان می دهند. یک ارجاع کامل به نوع محدودیت‌هایی که این مشخصات می‌توانند به آن نیاز داشته باشند را می‌توان در مستندات مرجع تحت View.onMeasure(int, int) یافت. این مستندات مرجع همچنین کل عملیات اندازه‌گیری را توضیح می‌دهد.
  • متد onMeasure() کامپوننت شما، عرض و ارتفاع اندازه گیری را محاسبه می کند که برای رندر کردن مولفه مورد نیاز است. باید سعی کند در مشخصاتی که در آن ارائه شده است باقی بماند، اگرچه ممکن است از آنها فراتر رود. در این مورد، والدین می‌توانند کاری را که باید انجام دهند، از جمله بریدن، پیمایش، پرتاب یک استثنا، یا درخواست از onMeasure() برای امتحان مجدد، شاید با مشخصات اندازه‌گیری متفاوت، انتخاب کنند.
  • هنگامی که عرض و ارتفاع محاسبه شد، روش setMeasuredDimension(int width, int height) با اندازه گیری های محاسبه شده فراخوانی کنید. عدم انجام این کار منجر به استثنا می شود.

در اینجا خلاصه‌ای از سایر روش‌های استانداردی که فریم‌ورک بر روی viewها فراخوانی می‌کند آورده شده است:

دسته بندی روش ها توضیحات
خلقت سازندگان یک فرم سازنده وجود دارد که زمانی که view از روی کد ایجاد می شود و یک فرم زمانی که نمای از یک فایل layout باد می شود فراخوانی می شود. فرم دوم ویژگی های تعریف شده در فایل layout را تجزیه و اعمال می کند.
onFinishInflate() پس از یک view فراخوانی می شود و همه فرزندان آن از XML باد می شوند.
طرح بندی onMeasure(int, int) برای تعیین اندازه مورد نیاز برای این نما و همه فرزندان آن فراخوانی شده است.
onLayout(boolean, int, int, int, int) زمانی نامیده می شود که این دیدگاه باید اندازه و موقعیتی را به همه فرزندان خود اختصاص دهد.
onSizeChanged(int, int, int, int) هنگامی که اندازه این نما تغییر می کند، فراخوانی می شود.
طراحی onDraw(Canvas) زمانی فراخوانی می شود که نما باید محتوای خود را ارائه کند.
پردازش رویداد onKeyDown(int, KeyEvent) زمانی فراخوانی می شود که یک رویداد key down رخ دهد.
onKeyUp(int, KeyEvent) هنگامی که یک رویداد کلیدی رخ می دهد، فراخوانی می شود.
onTrackballEvent(MotionEvent) هنگامی که یک رویداد حرکت گوی رخ می دهد، تماس گرفته می شود.
onTouchEvent(MotionEvent) هنگامی که یک رویداد حرکتی صفحه لمسی رخ می دهد، تماس گرفته می شود.
تمرکز کنید onFocusChanged(boolean, int, Rect) زمانی فراخوانی می شود که نمای فوکوس را به دست آورد یا از دست بدهد.
onWindowFocusChanged(boolean) زمانی فراخوانی می شود که پنجره حاوی نما فوکوس می کند یا از دست می دهد.
پیوست کردن onAttachedToWindow() هنگامی که نما به پنجره متصل است، فراخوانی می شود.
onDetachedFromWindow() هنگامی که نما از پنجره آن جدا می شود، فراخوانی می شود.
onWindowVisibilityChanged(int) زمانی فراخوانی می شود که نمای پنجره حاوی نما تغییر کند.

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

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

در اندروید، دو نمای دیگر برای انجام این کار به راحتی در دسترس هستند: Spinner و AutoCompleteTextView . صرف نظر از این، این مفهوم برای جعبه ترکیبی مثال خوبی است.

برای ایجاد یک جزء ترکیبی، موارد زیر را انجام دهید:

  • درست مانند یک Activity ، برای ایجاد کامپوننت‌های موجود از رویکرد اعلانی (بر اساس XML) استفاده کنید یا آن‌ها را به‌صورت برنامه‌نویسی از کد خود قرار دهید. نقطه شروع معمول نوعی Layout است، بنابراین کلاسی ایجاد کنید که یک Layout را گسترش دهد. در مورد یک جعبه ترکیبی، ممکن است از یک LinearLayout با جهت افقی استفاده کنید. می‌توانید طرح‌بندی‌های دیگری را در داخل قرار دهید، بنابراین جزء ترکیبی می‌تواند به‌طور دلخواه پیچیده و ساختارمند باشد.
  • در سازنده کلاس جدید، هر پارامتری را که superclass انتظار دارد را بردارید و ابتدا آنها را به سازنده superclass ارسال کنید. سپس، می توانید نماهای دیگر را برای استفاده در مؤلفه جدید خود تنظیم کنید. این جایی است که شما فیلد EditText و لیست پاپ آپ را ایجاد می کنید. شما ممکن است ویژگی ها و پارامترهای خود را در XML معرفی کنید که سازنده شما می تواند آنها را بکشد و از آنها استفاده کند.
  • در صورت تمایل، شنوندگانی برای رویدادهایی ایجاد کنید که نماهای حاوی شما ممکن است ایجاد کنند. یک مثال یک روش شنونده برای شنونده کلیک بر روی آیتم لیست است تا در صورت انتخاب لیست، محتویات EditText را به روز کند.
  • به صورت اختیاری، ویژگی های خود را با دسترسی ها و اصلاح کننده ها ایجاد کنید. به عنوان مثال، اجازه دهید مقدار EditText در ابتدا در کامپوننت تنظیم شود و در صورت نیاز محتوای آن را پرس و جو کنید.
  • به صورت اختیاری، onDraw() و onMeasure() را نادیده بگیرید. این معمولاً هنگام گسترش یک Layout ضروری نیست، زیرا طرح‌بندی دارای رفتار پیش‌فرض است که احتمالاً خوب کار می‌کند.
  • به‌طور اختیاری، on دیگر مانند onKeyDown() را نادیده بگیرید، به عنوان مثال برای انتخاب مقادیر پیش‌فرض خاصی از فهرست بازشو یک جعبه ترکیبی هنگامی که روی کلید خاصی ضربه می‌زنید.

استفاده از Layout به عنوان مبنایی برای کنترل سفارشی مزایایی دارد، از جمله موارد زیر:

  • می‌توانید با استفاده از فایل‌های XML اعلامی، دقیقاً مانند یک صفحه فعالیت، طرح‌بندی را مشخص کنید، یا می‌توانید نماها را به صورت برنامه‌نویسی ایجاد کنید و آنها را از کد خود در طرح‌بندی قرار دهید.
  • متدهای onDraw() و onMeasure() ، به علاوه اکثر متدهای دیگر on متدها، رفتار مناسبی دارند، بنابراین شما مجبور نیستید آنها را نادیده بگیرید.
  • شما می توانید به سرعت نماهای ترکیبی پیچیده دلخواه بسازید و از آنها مجدداً استفاده کنید که گویی یک جزء واحد هستند.

یک نوع نمای موجود را تغییر دهید

اگر مؤلفه ای شبیه آنچه شما می خواهید وجود دارد، می توانید آن مؤلفه را گسترش دهید و رفتاری را که می خواهید تغییر دهید لغو کنید. می‌توانید تمام کارهایی را که انجام می‌دهید با یک مؤلفه کاملاً سفارشی‌سازی شده انجام دهید، اما با شروع با یک کلاس تخصصی‌تر در سلسله‌مراتب View ، می‌توانید رفتاری را دریافت کنید که به صورت رایگان آنچه را که می‌خواهید انجام می‌دهد.

به عنوان مثال، برنامه نمونه NotePad بسیاری از جنبه های استفاده از پلتفرم اندروید را نشان می دهد. از جمله آنها، گسترش نمای EditText برای ایجاد یک دفترچه یادداشت خط دار است. این یک مثال کامل نیست، و APIهای انجام این کار ممکن است تغییر کنند، اما اصول را نشان می دهد.

اگر قبلاً این کار را انجام نداده اید، نمونه NotePad را به Android Studio وارد کنید یا با استفاده از پیوند ارائه شده به منبع نگاه کنید. به طور خاص، تعریف LinedEditText را در فایل NoteEditor.java ببینید.

مواردی که در این فایل قابل ذکر است:

  1. تعریف

    کلاس با خط زیر تعریف می شود:
    public static class LinedEditText extends EditText

    LinedEditText به عنوان یک کلاس داخلی در فعالیت NoteEditor تعریف می شود، اما عمومی است تا بتوان به عنوان NoteEditor.LinedEditText از خارج از کلاس NoteEditor به آن دسترسی داشت.

    همچنین، LinedEditText static است، به این معنی که به اصطلاح «روش‌های مصنوعی» را تولید نمی‌کند که به آن اجازه دسترسی به داده‌های کلاس والد را می‌دهد. این بدان معناست که به‌عنوان یک کلاس جداگانه رفتار می‌کند تا چیزی که شدیداً با NoteEditor مرتبط باشد. اگر نیازی به دسترسی به حالت از طبقه بیرونی نداشته باشند، این یک راه تمیزتر برای ایجاد کلاس های داخلی است. کلاس تولید شده را کوچک نگه می دارد و به راحتی از کلاس های دیگر استفاده می کند.

    LinedEditText EditText گسترش می دهد، که در این مورد، نمای شخصی سازی شده است. وقتی کار را تمام کردید، کلاس جدید می تواند یک نمای EditText معمولی را جایگزین کند.

  2. مقداردهی اولیه کلاس

    مثل همیشه، سوپر اول نامیده می شود. این یک سازنده پیش فرض نیست، اما یک سازنده پارامتری است. EditText با این پارامترها هنگامی که از یک فایل طرح بندی XML باد می شود ایجاد می شود. بنابراین، سازنده باید آنها را بگیرد و به سازنده سوپرکلاس نیز منتقل کند.

  3. روش های نادیده گرفته شده

    این مثال فقط متد onDraw() را نادیده می گیرد، اما ممکن است لازم باشد هنگام ایجاد مؤلفه های سفارشی خود، دیگران را نادیده بگیرید.

    برای این نمونه، نادیده گرفتن متد onDraw() به شما امکان می‌دهد خطوط آبی روی بوم نمای EditText را نقاشی کنید. بوم به متد overridden onDraw() منتقل می شود. متد super.onDraw() قبل از پایان متد فراخوانی می شود. روش superclass باید فراخوانی شود. در این حالت، پس از رنگ آمیزی خطوطی که می خواهید شامل شود، آن را در انتها فراخوانی کنید.

  4. جزء سفارشی

    شما اکنون جزء سفارشی خود را دارید، اما چگونه می توانید از آن استفاده کنید؟ در مثال NotePad، کامپوننت سفارشی مستقیماً از طرح‌بندی اعلامی استفاده می‌شود، بنابراین به note_editor.xml در پوشه res/layout نگاه کنید:

    <view xmlns:android="http://schemas.android.com/apk/res/android"
        class="com.example.android.notepad.NoteEditor$LinedEditText"
        android:id="@+id/note"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:padding="5dp"
        android:scrollbars="vertical"
        android:fadingEdge="vertical"
        android:gravity="top"
        android:textSize="22sp"
        android:capitalize="sentences"
    />
    

    جزء سفارشی به عنوان یک نمای عمومی در XML ایجاد می شود و کلاس با استفاده از بسته کامل مشخص می شود. کلاس داخلی که شما تعریف می‌کنید با استفاده از نماد NoteEditor$LinedEditText ، که یک روش استاندارد برای ارجاع به کلاس‌های داخلی در زبان برنامه‌نویسی جاوا است، ارجاع داده می‌شود.

    اگر جزء view سفارشی شما به عنوان یک کلاس داخلی تعریف نشده است، می توانید مولفه view را با نام عنصر XML اعلام کرده و ویژگی class حذف کنید. به عنوان مثال:

    <com.example.android.notepad.LinedEditText
      id="@+id/note"
      ... />
    

    توجه داشته باشید که کلاس LinedEditText اکنون یک فایل کلاس جداگانه است. وقتی کلاس در کلاس NoteEditor تودرتو است، این تکنیک کار نمی کند.

    سایر ویژگی‌ها و پارامترها در تعریف، آنهایی هستند که به سازنده مؤلفه سفارشی منتقل می‌شوند و سپس به سازنده EditText منتقل می‌شوند، بنابراین آنها همان پارامترهایی هستند که برای نمای EditText استفاده می‌کنید. این امکان وجود دارد که پارامترهای خود را نیز اضافه کنید.

ایجاد کامپوننت های سفارشی فقط به همان اندازه که شما نیاز دارید پیچیده است.

یک جزء پیچیده‌تر می‌تواند حتی بیشتر on روش‌ها غلبه کند و روش‌های کمکی خود را معرفی کند و به طور قابل‌توجهی ویژگی‌ها و رفتار خود را سفارشی کند. تنها محدودیت تخیل شما و کاری است که برای انجام آن جزء نیاز دارید.