طرح‌بندی‌ها در نماها

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

چیدمان ساختار میانای کاربر را در برنامه‌تان تعریف می‌کند، مثلاً در یک فعالیت. همه عناصر موجود در چیدمان بااستفاده از سلسله‌مراتبی از اشیاء View و ViewGroup ساخته شده‌اند. View معمولاً چیزی را ترسیم می‌کند که کاربر می‌تواند ببیند و با آن تعامل داشته باشد. ViewGroup محفظه‌ای نامرئی است که ساختار چیدمان را برای View و دیگر اشیای ViewGroup تعریف می‌کند، همان‌طور که در شکل ۱ نشان داده شده است.

شکل ۱. تصویر سلسله‌مراتب نما که چیدمان میانای کاربر را تعریف می‌کند.

به اشیاء View اغلب ابزاره گفته می‌شود و می‌توانند یکی از زیرکلاس‌های متعدد باشند، مثل Button یا TextView. اشیاء ViewGroup معمولاً چیدمان نامیده می‌شوند و می‌توانند یکی از انواع مختلفی باشند که ساختار چیدمان متفاوتی ارائه می‌دهند، مانند LinearLayout یا ConstraintLayout.

می‌توانید چیدمان را به دو روش اعلام کنید:

  • عناصر میانای کاربر را در XML اعلام کنید. Android واژگان XML ساده‌ای ارائه می‌دهد که با کلاس‌ها و زیرکلاس‌های View مطابقت دارد، مانند کلاس‌های مربوط به ابزاره‌ها و چیدمان‌ها. همچنین می‌توانید از ویرایشگر چیدمان Android Studio برای ساختن چیدمان XML بااستفاده از میانای کشیدن و رها کردن استفاده کنید.

  • نمونه‌سازی عناصر چیدمان در زمان اجرا. برنامه شما می‌تواند اشیاء View و ViewGroup را ایجاد کند و ویژگی‌های آن‌ها را به‌صورت برنامه‌نویسی‌شده دستکاری کند.

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

چارچوب Android به شما این انعطاف‌پذیری را می‌دهد که از یکی یا هر دو این روش‌ها برای ساختن واسط کاربر برنامه‌تان استفاده کنید. برای مثال، می‌توانید چیدمان‌های پیش‌فرض برنامه‌تان را در XML اعلام کنید و سپس چیدمان را در زمان اجرا اصلاح کنید.

نوشتن XML

بااستفاده از واژگان XML در Android، می‌توانید به‌سرعت چیدمان‌های میانای کاربر و عناصر صفحه‌نمایش موجود در آن‌ها را طراحی کنید، درست همان‌طور که صفحه‌های وب را در HTML با مجموعه‌ای از عناصر تودرتو ایجاد می‌کنید.

هر فایل چیدمان باید دقیقاً یک عنصر ریشه داشته باشد که باید شیء View یا ViewGroup باشد. پس‌از تعریف عنصر ریشه، می‌توانید اشیای چیدمان یا ابزاره‌های بیشتری را به‌عنوان عناصر فرزند اضافه کنید تا به‌تدریج سلسله‌مراتبی View بسازید که چیدمان شما را تعریف می‌کند. برای مثال، در اینجا یک چیدمان XML وجود دارد که از یک LinearLayout عمودی برای نگه داشتن یک TextView و یک Button استفاده می‌کند:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical" >
    <TextView android:id="@+id/text"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Hello, I am a TextView" />
    <Button android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, I am a Button" />
</LinearLayout>

پس‌از اعلام چیدمان در XML، فایل را با پسوند .xml در دایرکتوری res/layout/ پروژه Android خود ذخیره کنید تا به‌درستی کامپایل شود.

برای اطلاعات بیشتر درباره نحو فایل XML چیدمان، به منبع چیدمان مراجعه کنید.

بار کردن منبع XML

وقتی برنامه‌تان را کامپایل می‌کنید، هر فایل چیدمان XML در یک منبع View کامپایل می‌شود. منبع چیدمان را در پیاده‌سازی فراخوانی برگشتی Activity.onCreate() برنامه‌تان بار کنید. این کار را با فراخوانی setContentView() و ارسال ارجاع به منبع چیدمان در قالب زیر انجام دهید: R.layout.layout_file_name. برای مثال، اگر چیدمان XML شما به‌صورت main_layout.xml ذخیره شده است، آن را به‌صورت زیر برای Activity خود بار کنید:

کاتلین

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

جاوا

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_layout);
}

چارچوب Android روش فراخوانی onCreate() را در Activity شما هنگام راه‌اندازی Activity فراخوانی می‌کند. برای اطلاعات بیشتر درباره چرخه حیات فعالیت، به مقدمه‌ای بر فعالیت‌ها مراجعه کنید.

مشخصه‌ها

هر شیء View و ViewGroup از انواع مختلفی از مشخصه‌های XML پشتیبانی می‌کند. برخی‌از مشخصه‌ها مختص شیء View است. برای مثال، TextView از مشخصه textSize پشتیبانی می‌کند. بااین‌حال، این مشخصه‌ها توسط هر View شیء که این کلاس را گسترش می‌دهد نیز به ارث برده می‌شود. برخی‌از آن‌ها برای همه اشیای View مشترک هستند، زیرا از کلاس ریشه View به ارث برده شده‌اند، مانند مشخصه id. مشخصه‌های دیگر به‌عنوان پارامترهای چیدمان درنظر گرفته می‌شوند که مشخصه‌هایی هستند که جهت‌های چیدمان خاصی از شیء View را توصیف می‌کنند، همان‌طور که توسط شیء والد ViewGroup آن شیء تعریف شده است.

شناسه

هر شیء View می‌تواند یک شناسه عدد صحیح منسوب به خود داشته باشد تا View را به‌طور منحصربه‌فرد در درخت شناسایی کند. وقتی برنامه کامپایل می‌شود، این شناسه به‌عنوان عدد صحیح ارجاع داده می‌شود، اما شناسه معمولاً در فایل XML چیدمان به‌عنوان رشته در مشخصه id اختصاص داده می‌شود. این یک مشخصه XML مشترک برای همه اشیاء View است و توسط کلاس View تعریف شده است. خیلی زیاد از آن استفاده می‌کنید. نحو شناسه در برچسب XML به‌صورت زیر است:

android:id="@+id/my_button"

نماد at (@) در ابتدای رشته نشان می‌دهد که تجزیه‌کننده XML بقیه رشته شناسه را تجزیه و گسترش می‌دهد و آن را به‌عنوان منبع شناسه شناسایی می‌کند. نماد به‌علاوه (+) یعنی این نام منبع جدیدی است که باید ایجاد شود و به منابع شما در فایل R.java اضافه شود.

چارچوب Android منابع شناسه بسیار دیگری را ارائه می‌دهد. هنگام ارجاع به شناسه منبع Android، به نماد به‌علاوه نیاز ندارید، اما باید فضای نام بسته android را به‌صورت زیر اضافه کنید:

android:id="@android:id/empty"

فضای نام بسته android نشان می‌دهد که شما به شناسه‌ای از کلاس منابع android.R ارجاع می‌دهید، نه کلاس منابع محلی.

برای ایجاد نماها و ارجاع به آن‌ها از برنامه‌تان، می‌توانید از الگوی رایج زیر استفاده کنید:

  1. نمایی را در فایل چیدمان تعریف کنید و شناسه یکتایی به آن اختصاص دهید، مانند مثال زیر:
    <Button android:id="@+id/my_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/my_button_text"/>
  2. نمونه‌ای از شیء نما ایجاد کنید و آن را از چیدمان ضبط کنید، معمولاً در روش onCreate() ، همان‌طور که در مثال زیر نشان داده شده است:

    کاتلین

    val myButton: Button = findViewById(R.id.my_button)

    جاوا

    Button myButton = (Button) findViewById(R.id.my_button);

تعریف شناسه‌ها برای اشیاء نمایشی هنگام ایجاد RelativeLayout مهم است. در چیدمان نسبی، نماهای هم‌سطح می‌توانند چیدمان خود را نسبت به نمای هم‌سطح دیگری که با شناسه یکتا ارجاع داده می‌شود تعریف کنند.

لازم نیست شناسه در کل درخت منحصربه‌فرد باشد، اما باید در بخشی از درخت که جستجو می‌کنید منحصربه‌فرد باشد. اغلب ممکن است کل درخت باشد، بنابراین بهتر است درصورت امکان آن را منحصربه‌فرد کنید.

پارامترهای چیدمان

مشخصه‌های چیدمان XML با نام layout_something پارامترهای چیدمان را برای View تعریف می‌کنند که برای ViewGroup که در آن قرار دارد مناسب است.

هر کلاس ViewGroup یک کلاس تودرتو را پیاده‌سازی می‌کند که ViewGroup.LayoutParams را گسترش می‌دهد. این زیرکلاس حاوی انواع ویژگی‌هایی است که اندازه و موقعیت هر نمای فرزند را، به‌گونه‌ای که برای گروه نما مناسب باشد، تعریف می‌کند. همان‌طور که در شکل ۲ نشان داده شده است، گروه نمای اصلی پارامترهای چیدمان را برای هر نمای فرعی، ازجمله گروه نمای فرعی، تعریف می‌کند.

شکل ۲. مصورسازی سلسله‌مراتب نما با پارامترهای چیدمان مرتبط با هر نما.

هر زیرکلاس LayoutParams نحو خاص خود را برای تنظیم مقادیر دارد. هر عنصر فرزند باید LayoutParams مناسبی برای والد خود تعریف کند، هرچند ممکن است LayoutParams متفاوتی برای فرزندان خود تعریف کند.

همه گروه‌های نما شامل عرض و ارتفاع هستند که از layout_width و layout_height استفاده می‌کنند و هر نما باید آن‌ها را تعریف کند. بسیاری از LayoutParams حاشیه‌ها و مرزهای اختیاری را شامل می‌شوند.

می‌توانید عرض و ارتفاع را با اندازه‌گیری‌های دقیق مشخص کنید، اما ممکن است نخواهید این کار را اغلب انجام دهید. اغلب، از یکی از این ثابت‌ها برای تنظیم عرض یا ارتفاع استفاده می‌کنید:

  • ‫wrap_content: به نمای شما می‌گوید که اندازه خود را با ابعاد موردنیاز محتوایش تطبیق دهد.
  • match_parent: به نمای شما می‌گوید تا جایی که گروه نمای اصلی اجازه می‌دهد بزرگ شود.

به‌طور کلی، توصیه نمی‌کنیم که عرض و ارتفاع چیدمان را بااستفاده از واحدهای مطلق مانند پیکسل مشخص کنید. رویکرد بهتر استفاده از اندازه‌گیری‌های نسبی است، مانند واحدهای پیکسل مستقل از تراکم (dp)، wrap_content، یا match_parent، زیرا به برنامه شما کمک می‌کند به‌درستی در اندازه‌های مختلف صفحه‌نمایش دستگاه نمایش داده شود. انواع اندازه‌گیری پذیرفته‌شده در منبع چیدمان تعریف شده‌اند.

موقعیت چیدمان

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

می‌توانید مکان یک نما را با فراخوانی روش‌های getLeft() و getTop() بازیابی کنید. مورد اول مختصات چپ (x) مستطیل نشان‌دهنده نما را برمی‌گرداند. مورد دوم مختصات بالا (y) مستطیل را که نشان‌دهنده نما است برمی‌گرداند. این روش‌ها مکان نما را نسبت به والد آن برمی‌گرداند. برای مثال، وقتی getLeft() مقدار ۲۰ را برمی‌گرداند، یعنی نما در فاصله ۲۰ پیکسلی سمت راست لبه چپ والد مستقیم خود قرار دارد.

علاوه‌براین، روش‌های راحتی برای اجتناب از محاسبات غیرضروری وجود دارد: یعنی getRight() و getBottom(). این روش‌ها مختصات لبه‌های راست و پایین مستطیل نشان‌دهنده نما را برمی‌گردانند. برای مثال، فراخوانی getRight() شبیه به محاسبه زیر است: getLeft() + getWidth().

اندازه، حاشیه داخلی، و حاشیه خارجی

اندازه نما با عرض و ارتفاع بیان می‌شود. یک نما دو جفت مقدار عرض و ارتفاع دارد.

جفت اول به‌عنوان عرض اندازه‌گیری‌شده و ارتفاع اندازه‌گیری‌شده شناخته می‌شود. این ابعاد تعیین می‌کنند که یک نما چقدر می‌خواهد در داخل والد خود بزرگ باشد. می‌توانید ابعاد اندازه‌گیری‌شده را با فراخوانی getMeasuredWidth() و getMeasuredHeight() به‌دست آورید.

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

برای اندازه‌گیری ابعاد، نما لایه‌گذاری را درنظر می‌گیرد. حاشیه داخلی به پیکسل برای بخش‌های چپ، بالا، راست، و پایین نما بیان می‌شود. می‌توانید از لایه‌گذاری برای جابه‌جا کردن محتوای نما به تعداد مشخصی پیکسل استفاده کنید. برای مثال، حاشیه داخلی چپ دو پیکسلی، محتوای نما را دو پیکسل به سمت راست لبه چپ می‌برد. می‌توانید بااستفاده از روش setPadding(int, int, int, int) حاشیه داخلی را تنظیم کنید و با فراخوانی getPaddingLeft()، getPaddingTop()، getPaddingRight()، و getPaddingBottom() آن را پُرسمان کنید.

اگرچه یک نما می‌تواند حاشیه داخلی را تعریف کند، اما از حاشیه خارجی پشتیبانی نمی‌کند. بااین‌حال، گروه‌های نما از حاشیه پشتیبانی می‌کنند. برای اطلاعات بیشتر، ViewGroup و ViewGroup.MarginLayoutParams را ببینید.

برای اطلاعات بیشتر درباره ابعاد، به بعد مراجعه کنید.

علاوه‌بر تنظیم حاشیه‌ها و فاصله‌گذاری به‌صورت برنامه‌نویسی، می‌توانید آن‌ها را در چیدمان‌های XML خود نیز تنظیم کنید، همان‌طور که در مثال زیر نشان داده شده است:

  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical" >
      <TextView android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="16dp"
                android:padding="8dp"
                android:text="Hello, I am a TextView" />
      <Button android:id="@+id/button"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_marginTop="16dp"
              android:paddingBottom="4dp"
              android:paddingEnd="8dp"
              android:paddingStart="8dp"
              android:paddingTop="4dp"
              android:text="Hello, I am a Button" />
  </LinearLayout>
  

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

چیدمان‌های متداول

هر زیرکلاس از کلاس ViewGroup روشی منحصربه‌فرد برای نمایش نماهایی که در آن قرار می‌دهید ارائه می‌دهد. انعطاف‌پذیرترین نوع چیدمان و نوعی که بهترین ابزارها را برای کم‌عمق نگه داشتن سلسله‌مراتب چیدمان شما ارائه می‌دهد، ConstraintLayout است.

در زیر برخی‌از انواع چیدمان رایج که در پلاتفرم Android تعبیه شده است آورده شده است.

ایجاد چیدمان خطی

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

ساختن برنامه‌های وب در WebView

صفحه‌های وب را نمایش می‌دهد.

ساختن فهرست‌های پویا

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

چیدمان‌های رایج ممکن با RecyclerView و AdapterView شامل موارد زیر است:

فهرست

فهرست تک‌ستونی پیمایشی را نمایش می‌دهد.

شبکه

جدولی پیمایشی از ستون‌ها و ردیف‌ها را نمایش می‌دهد.

RecyclerView امکانات بیشتری ارائه می‌دهد و گزینه ایجاد مدیر چیدمان سفارشی را فراهم می‌کند.

پر کردن نمای آداپتور با داده‌ها

می‌توانید AdapterView مثل ListView یا GridView را با مقید کردن نمونه AdapterView به Adapter پر کنید، که داده‌ها را از منبعی خارجی بازیابی می‌کند و View ایجاد می‌کند که هر ورودی داده را نشان می‌دهد.

Android چندین زیرکلاس از Adapter ارائه می‌دهد که برای بازیابی انواع مختلف داده‌ها و ساختن نماها برای AdapterView مفید هستند. دو آداپتور رایج عبارت‌اند از:

ArrayAdapter
وقتی منبع داده شما آرایه است، از این آداپتور استفاده کنید. به‌طور پیش‌فرض، ArrayAdapter با فراخوانی toString() روی هر مورد و قرار دادن محتوا در TextView، نمایی برای هر مورد آرایه ایجاد می‌کند.

برای مثال، اگر آرایه‌ای از رشته‌ها دارید که می‌خواهید در ListView نمایش دهید، بااستفاده از سازنده، ArrayAdapter جدیدی را مقداردهی اولیه کنید تا چیدمان هر رشته و آرایه رشته‌ای را مشخص کنید:

کاتلین

    val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray)
    

جاوا

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, myStringArray);
    

آرگومان‌های این سازنده عبارتند از:

  • برنامه شما Context
  • چیدمانی که حاوی TextView برای هر رشته در آرایه است
  • آرایه رشته‌ای

سپس با setAdapter() در ListView تماس بگیرید:

کاتلین

    val listView: ListView = findViewById(R.id.listview)
    listView.adapter = adapter
    

جاوا

    ListView listView = (ListView) findViewById(R.id.listview);
    listView.setAdapter(adapter);
    

برای سفارشی‌سازی ظاهر هر مورد، می‌توانید روش toString() را برای اشیاء موجود در آرایه لغو کنید. یا برای ایجاد نمایی برای هر موردی که چیزی غیراز TextView است—برای مثال، اگر برای هر مورد آرایه ImageView می‌خواهید—کلاس ArrayAdapter را گسترش دهید و getView() را لغو کنید تا نوع نمای موردنظرتان را برای هر مورد برگرداند.

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

کاتلین

    val fromColumns = arrayOf(ContactsContract.Data.DISPLAY_NAME,
                              ContactsContract.CommonDataKinds.Phone.NUMBER)
    val toViews = intArrayOf(R.id.display_name, R.id.phone_number)
    

جاوا

    String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
                            ContactsContract.CommonDataKinds.Phone.NUMBER};
    int[] toViews = {R.id.display_name, R.id.phone_number};
    

وقتی SimpleCursorAdapter را نمونه‌سازی می‌کنید، چیدمان را برای استفاده در هر نتیجه، Cursor حاوی نتایج، و این دو آرایه ارسال کنید:

کاتلین

    val adapter = SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0)
    val listView = getListView()
    listView.adapter = adapter
    

جاوا

    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
            R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
    ListView listView = getListView();
    listView.setAdapter(adapter);
    

سپس SimpleCursorAdapter بااستفاده از چیدمان ارائه‌شده، نمایی برای هر ردیف در Cursor ایجاد می‌کند و هر مورد fromColumns را در نمای toViews مربوطه درج می‌کند.

اگر درطول دوره عمر برنامه‌تان، داده‌های اساسی را که توسط آداپتور خوانده می‌شود تغییر دهید، notifyDataSetChanged() را فراخوانی کنید. این کار به نمای پیوست‌شده اطلاع می‌دهد که داده‌ها تغییر کرده است و خودش را بازآوری می‌کند.

مدیریت رویدادهای کلیک

می‌توانید با پیاده‌سازی میانای AdapterView.OnItemClickListener به رویدادهای کلیک روی هر مورد در AdapterView پاسخ دهید. برای مثال:

کاتلین

listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id ->
    // Do something in response to the click.
}

جاوا

// Create a message handling object as an anonymous class.
private OnItemClickListener messageClickedHandler = new OnItemClickListener() {
    public void onItemClick(AdapterView parent, View v, int position, long id) {
        // Do something in response to the click.
    }
};

listView.setOnItemClickListener(messageClickedHandler);

منابع بیشتر

ببینید چیدمان‌ها چگونه در برنامه نمایشی Sunflower در GitHub استفاده می‌شوند.