طراحی صفحات با ConstraintLayout + سورس پروژه

آموزش طراحی رابط کاربری اندروید توسط ConstraintLayout
در جلسه قبل با عنوان آموزش طراحی رابط کاربری در اندروید با کلیات رابط کاربری، ویو، ویجت‌ها و همچنین لایه‌ (Layout یا ViewGroup) های پرکاربرد LinearLayout و RelativeLayout آشنا شدیم. در این جلسه قصد داریم طراحی صفحات با ConstraintLayout را بررسی کنیم.
این layout که نسبت به سایر layout های اندروید عمر کمتری دارد، از انعطاف بسیار بالاتری برخوردار بوده و می‌تواند زمان طراحی و حجم کدهای xml رابط کاربری اکتیویتی را تا حد زیادی کاهش دهد. در نتیجه با کاهش حجم کدها، سرعت نمایش صفحات افزایش یافته و می‌توانیم شاهد بهبود عملکرد کلی اپلیکیشن باشیم.
در صورتی که با ویژگی‌ها و امکانات به خوبی آشنا بوده و به اندازه کافی روی این ViewGroup جدید تمرینات مختلف انجام داده باشیم، احتمالا در اکثر موارد می‌توانیم بدون بکارگیری لایه‌های LinearLayout، RelativeLayout، FrameLayout، TableLayout و GridLayout رابط‌های کاربری مدنظر خود را در بهینه‌ترین حالت ممکن پیاده سازی کنیم.
در این جلسه سعی می‌کنم تمامی قابلیت‌ها و جزئیات این layout تازه نفس را در قالب مثال بیان کنم. توجه داشته باشید این آموزش به نوعی ادامه‌ی جلسه آموزش طراحی رابط کاربری در اندروید بوده و پیش نیاز این جلسه، آشنایی با مفاهیم ابتدایی رابط کاربری می‌باشد که قبلا به آن‌ها پرداخته‌ایم.

آنچه در این آموزش می‌خوانید

 

این جلسه در قالب PDF و در ۱۰۷ صفحه تهیه شده که در ادامه چند صفحه‌ ابتدایی را مشاهده می‌کنید:

ConstraintLayout چیست؟

به نام خدا. ConstraintLayout برای اولین بار در کنفرانس Google I/O در سال ۲۰۱۶ معرفی شد. اصلی ترین هدف گوگل از معرفی این Layout جدید، امکان طراحی لایه‌های پیچیده و در عین حال flat (تخت) بود؛ بر خلاف Layout های قبلی که برای طراحی یک صفحه ناگزیر به استفاده از لایه‌های تو در تو و سلسله مراتبی بودیم.

برتری‌های ConstraintLayout نسبت به ViewGroup های قبلی

این یعنی دیگر لازم نیست چندین لایه RelativeLayout و LinearLayout و… درون یکدیگر و به صورت تو در تو تعریف شود تا بتوان یک صفحه چند قسمتی را ساخت. بنابراین علاوه بر سادگی پیاده سازی این لایه و کاهش حجم نهایی کدهای آن، در طراحی صفحات با ConstraintLayout کارایی و performance اکتیویتی بهبود یافته و می‌تواند در افزایش سرعت نمایش UI روی دستگاه کاربر تاثیر مثبت بگذارد.
ConstraintLayout از اندروید استودیو ۲٫۲ اضافه شد و نسخه ابتدایی آن Beta 1 نام داشت. در طی این چند سال مرتبا اصلاحاتی روی آن انجام و همچنین قابلیت‌های جدیدی نیز اضافه شده است. خبر خوب اینکه این لایه از API 9 به بعد پشتیبانی می‌شود بنابراین مطمئن خواهیم بود تمامی دستگاه‌های اندرویدی که در حال حاضر در اختیار افراد هست به خوبی می‌توانند صفحاتی که با این layout طراحی شده‌اند را نمایش دهند.
در ConstraintLayout می‌توانیم وضعیت قرارگیری تمامی view/widget ها را نسبت به یکدیگر و یا parent شان تعیین کنیم. برای مثال می‌توانیم یک TextView را به نحوی در زیر یک ImageView متصل کنیم که در جهت افقی، متن همواره در مرکز تصویر قرار گیرد. یا اینکه یک یا چند view بر اساس سایز صفحه نمایش دستگاه، چه از جهت افقی و چه عمودی همواره در مرکز قرار گرفته و علاوه بر آن، فاصله آنها از یکدیگر نیز درصدی ثابت باشد.
این یعنی یک انعطاف پذیری بسیار بالا که تا حدود زیادی مشابه طراحی صفحات واکنشگرا یا Responsive در صفحات وب است.
فکر می‌کنم هرچه اینجا بیشتر توضیح بدهم بیشتر هم شما را گیج می‌کنم! بنابراین بهتر است سراغ محیط توسعه اندروید استودیو رفته و امکانات این لایه قدرتمند را در عمل بررسی کنیم.
توجه داشته باشید در نسخه‌های ابتدایی اندروید استودیو که ConstraintLayout اضافه شده بود می‌بایست ابزار آن به صورت جداگانه در SDK و در زیرمجموعه Support Repository دریافت و نصب می‌شد اما در نسخه‌های اخیر، این گزینه از لیست SDK Tools حذف شده و با سایر ابزار اصلی توسعه اندروید ادغام شده است.
قبل از هرچیز توجه داشته باشید در برخورد اول قطعا کار با این layout مقداری گیج کننده خواهد بود بنابراین لازم است بارها و بارها تمرین کرده و خروجی کار را مقایسه کنید تا کاربرد هر قابلیت را بتوانید به درستی درک نمائید.

نحوه طراحی صفحات رابط کاربری اندروید با ConstraintLayout

قبل از هرچیز ابتدا طبق مبحث آموزش ساخت پروژه در اندروید استودیو یک پروژه اندرویدی با نام ConstraintLayout می‌سازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.
کتابخانه این layout به صورت پیش فرض در بلاک dependencies فایل build.gradle(app) پروژه اندرویدی قرار دارد:

dependencies {
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
}

در زمان نگارش این آموزش، نسخه ۲٫۰٫۴ جدیدترین نسخه از این ViewGroup است.
Layout پیش فرض اکتیویتی پروژه از نوع ConstraintLayout بوده که در قسمت Component tree قابل مشاهده است:

ConstraintLayout پیش فرض در اکتیویتی
ConstraintLayout پیش فرض در اکتیویتی

محیط پیش نمایش در دو حالت Design و Blueprint قابل نمایش است که به منظور خلوت بودن صفحه در حین آموزش تنها از حالت Design استفاده می‌کنم.

خطوط اتصال و گره‌ها در ConstraintLayout

از ساده ترین مثال شروع می‌کنم. ملاحظه می‌کنید یک TextView به طور پیش فرض در مرکز صفحه پیش نمایش با عنوان Hello World! قرار گرفته. موس را روی TextView قرار می‌دهم:

خطوط اتصال Constraint
خطوط اتصال Constraint

این TextView توسط ۴ خط اتصال به ۴ لبه‌ی صفحه متصل شده است. حالا روی TextView کلیک می‌کنم:

نقاط اتصال در ConstraintLayout
نقاط اتصال (گره یا نود) در ConstraintLayout

ملاحظه می‌کنید خطوط اتصال پررنگ شده و نقاط اتصال خطوط به ویجت مدنظر نیز به شکل دایره‌های توپر نمایش داده می‌شود. هر کدام از این دایره‌ها را یک گره یا node می‌نامیم. علت قرار گرفتن این TextView در مرکز صفحه این است که هر ۴ گره آن به چهار جهت صفحه نمایش متصل شده.
قبل از انجام هر کاری کد xml صفحه را بررسی می‌کنیم:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ملاحظه می‌کنید یک TextView با تعدادی ویژگی درون layout از جنس ConstraintLayout قرار گرفته است. به عبارتی این layout والد یا parent ویجت TextView است.

app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"

این چهار attribute موقعیت مکانی TextView را نسبت به والد خود تعیین می‌کند. یعنی به ازاء هر خط اتصالی که از گره‌های ویجت به یکی از کناره‌های صفحه نمایش کشیده شده یکی از این ویژگی‌ها به کد TextView اضافه شده است.

app:layout_constraintBottom_toBottomOf="parent"

اگر به نحوه نامگذاری هر کدام دقت کنید کاربرد آن را متوجه خواهید شد.
Bottom to Bottom of parent یعنی پایینِ TextView به پایینِ parent متصل شود. و به همین ترتیب برای ۳ مورد دیگر. نتیجه این می‌شود که ویجت از بالا و پایین به یک اندازه و همچنین از چپ و راست هم به یک اندازه فاصله گرفته بنابراین در مرکز صفحه قرار می‌گیرد.
با توجه به اینکه در اینجا برای تعیین فاصله از واحدهای اندازه گیری استفاده نشده لذا یک رابط کاربری ساده و انعطاف پذیر داریم که در هر صفحه نمایشی اجرا شود اعم از کوچک و بزرگ یا افقی و عمودی، همواره این TextView در مرکز صفحه قرار خواهد گرفت.
اگر یکی از این خطوط حذف شود چه اتفاقی خواهد افتاد؟ می‌خواهم خط بالا را حذف کنم. برای اینکار هم می‌توان در حالت کد، ویژگی layout_constraintTop_toTopOf را از تکست ویو حذف کرد هم اینکه در حالت دیزاین، خط بالا را انتخاب کرده و delete ‌کنیم. همچنین اگر روی هریک از خطوط راست کلیک کنیم گزینه‌های مختلفی نمایش داده می‌شود که یکی از آنها Delete است:

حذف خطوط اتصال در ConstraintLayout

حذف خطوط اتصال در ConstraintLayout

حذف خطوط اتصال در ConstraintLayout
حذف خطوط اتصال در ConstraintLayout

مشاهده می‌کنید با حذف خط اتصال بالا، TextView از جهت افقی در مرکز قرار دارد اما در جهت عمودی به پایین صفحه چسبیده است. برای در مرکز قرار گرفتن ویجت کافی است دوباره ویژگی layout_constraintTop_toTopOf با مقدار parent را در ویرایشگر کد اضافه کرده و یا در محیط ویژوال به وسیله عمل Drag & Drop دایره یا گره بالای ویجت را گرفته و روی لبه بالای صفحه رها کنم:

ساخت خطوط اتصال در ConstraintLayout

اتصال view ها به یکدیگر در ConstraintLayout

ایجاد خطوط اتصال در ConstraintLayout
ایجاد خطوط اتصال در ConstraintLayout

در نهایت مجدد توانستم ویجت را نسبت به بالا و پایین در مرکز قرار دهم.
برای مشاهده جزئیات بیشتر روی گزینه Attributes نوار سمت راست محیط دیزاین کلیک می‌کنم. سپس روی صفحه پیش نمایش یا Component tree ویجت TextView را انتخاب می‌کنم تا ویژگی‌های مرتبط با آن نمایش داده شود:

بخش Attributes در محیط طراحی اندروید استودیو
بخش Attributes در محیط طراحی اندروید استودیو

در قسمت Declared Attributes عینا مواردی که در کد TextView دیدیم لیست شده و می‌توان هرکدام را تغییر داد.
در قسمت Layout امکانات بیشتری برای اعمال تغییرات وجود دارد. در چهار جهت آیکون مربعی شکل که نماد ویجت انتخاب شده یعنی TextView است، فیلدهای عددی دیده می‌شود که در حالت پیش فرض عدد صفر را نشان می‌دهند. این اعداد، فاصله یا margin را از جهت‌هایی که توسط خط اتصال به والد یا یک ویجت دیگر متصل شده تعیین می‌کنند.
در اینجا که ویجت ما در مرکز صفحه قرار گرفته تعیین فاصله اهمیتی ندارد اما در ادامه آموزش و هنگامی که بخواهیم یک ویجت را در کنار یک ویجت دیگر قرار دهیم از این ویژگی استفاده خواهیم کرد.

تعیین فاصله از چپ و پایین توسط Bias

در سمت چپ و پایین دو گزینه در کنار عدد مارجین قرار دارد که توسط آن و با حرکت دادن دایره روی خط، میزان فاصله از چهار جهت را می‌توان تغییر داد. این قابلیت Bias نام دارد. دقت داشته باشید در اینجا خبری از واحدهای px و dp نیست بلکه بر اساس درصد تعیین می‌شود. به صورت پیش فرض هردو Bias روی ۵۰ قرار دارد یعنی مرکز صفحه.
اگر بخاطر داشته باشید در کد مربوط به TextView ویژگی با نام Bias وجود نداشت. این یعنی مقدار پیش فرض Bias برای یک ویجت همین ۵۰% یا مقدار ۰٫۵ است و صرفا در صورتی این ویژگی به ویجت اضافه خواهد شد که مقداری غیر از ۵۰ برای آن درنظر بگیریم.
برای تمرین، ویجت روی صفحه یا همین گزینه پایین که مربوط به جهت افقی هست را مقداری به سمت چپ بکشید:

جابجایی عناصر توسط ویژگی Bias در ConstraintLayout
جابجایی عناصر توسط ویژگی Bias در ConstraintLayout

ملاحظه می‌کنید عدد Horizontal Bias به ۲۵ تغییر کرد و ویجت هم در جهت افق به اندازه ۲۵ درصد به سمت چپ نزدیکتر شد. حالا اگر کد ویجت را دوباره بررسی کنید خط زیر به آن اضافه شده:

app:layout_constraintHorizontal_bias="0.25"

در محیط ویژوال عدد از ۰ تا ۱۰۰ و در کد از ۰ تا ۱ تعیین می‌شود. یعنی ۲۵ درصد برای Bias افقی (Horizontal) برابر است با مقدار ۰٫۲۵ برای ویژگی آن در کدها.
صفحه پیش نمایش را در حالت افقی (Landscape) قرار می‌دهم:

تست ویژگی Bias در صفحه نمایش Landscape
تست ویژگی Bias در صفحه نمایش Landscape

باز هم ویجت در فاصله ۲۵ درصدی از سمت چپ صفحه قرار گرفته و انعطاف پذیری ConstraintLayout را ثابت می‌کند.

نکته: ویژگی Bias فقط برای جهتی قابل استفاده است که هردو طرف آن به والد یا ویجت‌های دیگر متصل شده باشد. به طور خلاصه این ویژگی، فاصله ویجت از مرکز در جهت افقی یا عمودی را مشخص می‌کند.

تعیین عرض و ارتفاع Widget یا View ها

عرض و ارتفاع هر ویجتی که به layout اضافه می‌شود ابتدا wrap_content است؛ یعنی به اندازه محتوای درون خودش جا می‌گیرد. اما دو حالت دیگر هم قابل انجام است.

تغییر عرض و ارتفاع ویو ها در ConstraintLayout
تغییر عرض و ارتفاع ویو ها در ConstraintLayout

در تصویر بالا مشاهده می‌کنید با بردن نشانگر بر روی یکی از فاصله‌های افقی عبارت Wrap Content نمایش داده می‌شود. یکبار روی یکی از آنها کلیک می‌کنم:

تعیین حالت Fixed برای اندازه ویجت‌ها
تعیین حالت Fixed برای اندازه ویجت‌ها

نماد فاصله‌های افقی از دو فلش به یک خط ممتد تغییر پیدا کرد و عبارت Fixed را نمایش می‌دهد. همچنین در قسمت Declared Attributes مقدار width از wrap_content به ۷۶dp برای من تغییر داده شد یعنی اندروید استودیو یک مقدار پیش فرض در واحد dp را درنظر گرفت. حالا من می‌توانم مقدار مدنظر خودم را بجای ۷۶ وارد کنم.
در مواقعی به این حالت نیاز داریم که بخواهیم برای یک ویجت عرض یا ارتفاع مشخص و ثابتی را درنظر بگیریم و نباید اندازه آن به صفحه نمایش یا حالت قرار گیری آن بستگی داشته باشد.
یک مرتبه دیگر روی نماد فاصله افقی کلیک می‌کنم:

تعیین حالت Match Constraints برای اندازه ویجت‌ها

اینبار نوع فاصله گذاری به Match Constraints تغییر پیدا کرد. این حالت همان match_parents در layout هایی مانند RelativeLayout است اما در ConstraintLayout برای اینکه یک ویجت تمام عرض یا ارتفاع در دسترس را اشغال کند بجای match_parent مقدار ۰dp باید داده شود که اندروید استودیو این تغییر را به صورت خودکار برای من انجام داد:

تعیین حالت Match Constraints برای اندازه ویجت‌ها

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

تعیین حالت Match Constraints برای اندازه ویجت‌ها
تعیین حالت Match Constraints برای اندازه ویجت‌ها
نکته: مانند layout های قبل، انجام تغییرات محدود به حالت دیزاین یا کد نیست و به هر دو روش می‌توان کار را انجام داد. اما در مرحله یادگیری توصیه می‌کنم هر تغییری که در یک حالت انجام می‌دهید نتیجه آنرا در حالت دیگر هم بررسی کنید تا ببینید انجام یک کار در محیط ویژوال چه کدی را به کدهای لایه اضافه می‌کند یا بلعکس اگر یک ویژگی به یک ویجت اضافه می‌کنید چه تغییری در صفحه پیش نمایش اعمال می‌شود.
در صورتی که تسلط کافی بر هردو حالت کد و دیزاین داشته باشید در هنگام ساخت و ویرایش صفحات بخصوص layout های پیچیده، کنترل بیشتری بر روی لایه‌ها و ویجت‌ها خواهید داشت.

خب! حالا که تا حدودی با کلیت کار آشنا شدیم ادامه آموزش را در قالب چند مثال عملی طراحی صفحات با ConstraintLayout پیش می‌برم تا درک بهتری از امکانات آن داشته باشید.
از مسیر New > Layout Resource file یک layout جدید با نام layout_two.xml به پروژه اضافه می‌کنم. هر قسمت را در یک فایل جداگانه توضیح می‌دهم تا همه کدها در سورس پروژه ضمیمه شده را در اختیار داشته باشید.
یک ویجت Button را از پالت ابزار کشیده و در مرکز صفحه پیش نمایش قرار می‌دهم:

نمایش یک View در وسط صفحه نمایش در ConstraintLayout
نمایش یک View در وسط صفحه نمایش در ConstraintLayout

اینجا ظاهرا مشکلی نیست و پس از رها کردن، دکمه در مرکز صفحه باقی می‌ماند. حالا می‌خواهم پروژه را اجرا کنم تا خروجی کار را روی دیوایس اندرویدی هم ببینیم. در MainActivity.java فایل layout_two را جایگزین activity_main می‌کنم تا هنگام اجرای پروژه روی دیوایس نمایش داده شود.

MainActivity.java

package ir.android_studio.constraintlayout;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_two);
    }
}

پروژه را روی شبیه ساز Genymotion اجرا می‌کنم:

تفاوت خروجی ConstraintLayout در صفحه پیش نمایش و دستگاه اندرویدی
تفاوت خروجی ConstraintLayout در صفحه پیش نمایش و دستگاه اندرویدی

برخلاف آنچه در Preview اندروید استودیو دیدیم که دکمه در مرکز طولی و عرضی صفحه نمایش قرار گرفته بود، روی دیوایس اندرویدی دکمه در گوشه صفحه قرار گرفته است. به سورس layout دقت کنید:

layout_two.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        tools:layout_editor_absoluteX="156dp"
        tools:layout_editor_absoluteY="342dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

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

اضافه کردن خطوط اتصال چپ و راست در ConstraintLayout
اضافه کردن خطوط اتصال چپ و راست در ConstraintLayout

خروجی Constraints در شبیه ساز اندرویدی

مشاهده می‌کنید دکمه در جهت افقی درست در مرکز صفحه قرار گرفته. ویژگی‌های layout_editor_absolute تنها زمانی فعال می‌شود که خطوط اتصال در آن جهت وجود نداشته باشد. بنابراین با اضافه شدن دو خط چپ و راست، ویژگی layout_editor_absoluteX از دکمه حذف شد و فقط layout_editor_absoluteY باقی ماند:

<Button
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    tools:layout_editor_absoluteY="342dp" />

در حال حاضر نحوه نمایش وضعیت constraint ویجت دکمه به اینصورت است:

مدیریت خطوط اتصال در ابزار Constraints Widget
مدیریت خطوط اتصال در ابزار Constraints Widget

در جهت بالا و پایین نماد + وجود دارد که به وضوح نشان می‌دهد در این گره‌ها اتصالی انجام نشده است. البته اگر اینجا روی + کلیک کنیم علاوه بر ایجاد خط اتصال، یک فاصله (margin) به مقداری که قبلا در layout_editor_absoluteY تعریف شده نیز اضافه خواهد شد.
به عبارت دیگر layout_marginTop جایگزین ویژگی layout_editor_absoluteY می‌شود:

دکمه افزودن خط اتصال در ابزار Constraints Widget
دکمه افزودن خط اتصال در ابزار Constraints Widget

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

حذف یکباره تمامی Constraint ها

حذف تمامی خطوط اتصال با Clear All Constraints
حذف تمامی خطوط اتصال با Clear All Constraints

گزینه Clear All Constraints همانطور که از نامش پیداست تمامی اتصالاتی که درون layout بر روی ویجت‌ها اعمال شده را به یکباره حذف می‌کند. این مورد مواقعی کاربرد دارد که تعدادی تغییرات در صفحه انجام داده‌ایم و لازم است همگی اصلاح شوند. با استفاده از این گزینه همه تغییرات قبلی حذف شده و لازم نیست یک به یک constraint ها را حذف کنیم.
همچنین کاربرد دیگر آن هنگامی است که یک layout که قبلا با استفاده از یک ViewGroup دیگر مانند LinearLayout طراحی شده را بخواهیم به ConstraintLayout تبدیل کنیم.

تبدیل ViewGroup های دیگر به ConstraintLayout

برای مثال قصد داریم رابط کاربری مربوط به آموزش دیتابیس در اندروید که قبلا با استفاده از RelativeLayout ساخته بودیم را به ConstraintLayout تبدیل کنیم. در قسمت Component Tree روی ViewGroup راست کلیک کرده و گزینه Convert RelativeLayout to ConstraintLayout را انتخاب می‌کنم:

تبدیل یک ViewGroup به ConstraintLayout در اندروید استودیو

تبدیل یک ViewGroup به ConstraintLayout در اندروید استودیو
تبدیل یک ViewGroup به ConstraintLayout در اندروید استودیو

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

ایجاد خودکار Constraint ها

ایجاد خودکار خطوط اتصال توسط Infer Constraints
ایجاد خودکار خطوط اتصال توسط Infer Constraints

گزینه Infer Constraints عملی متضاد با Clear All Constraints را انجام می‌دهد. واژه infer به معنی “حدس زدن” است. یعنی در یک layout از جنس ConstraintLayout که یک یا چندین ویجت بدون خطوط اتصال و مابقی قابلیت‌های آن اضافه شده، با کلیک روی این گزینه، اندروید استودیو به صورت حدسی تنظیمات و خطوط اتصال را برای آنها اعمال می‌کند اما همانطور که در قسمت قبل اشاره شد، این تنظیمات عموما رضایت بخش نبوده و در عمل کاربرد زیادی نخواهد داشت.
فعلا با ویجت دکمه کاری ندارم و آنرا از صفحه حذف می‌کنم.
یک گزینه دیگر با نام Default Margins در بالای صفحه Preview وجود دارد:

تعیین فاصله یا Margin پیش فرض

تعیین مقدار Defalut Margin در ConstraintLayout
تعیین مقدار Defalut Margin در ConstraintLayout

در طراحی یک layout برای ایجاد فاصله بین المان‌های مختلف از ویژگی Margin استفاده می‌کنیم. معمولا فاصله‌ای که در یک layout برای المان‌های مختلف استفاده می‌شود یکسان است تا چینش عناصر در رابط کاربری منظم و یکپارچه باشد.
مقدار Defalut Margin به طور پیش فرض روی عدد ۰ تنظیم شده. من این مقدار را روی ۱۶dp تنظیم می‌کنم و کار را ادامه می‌دهم.
ابتدا یک ImageView به صفحه اضافه می‌کنم:

افزودن یک ویجت به constraintlayout در اندروید استودیو

در قسمت Attributes عرض این ویجت را ۰dp یا همان Match Constraint و ارتفاع را ۲۰۰dp تعیین می‌کنم. همچنین ویژگی scaleType را بر روی centerCrop تنظیم می‌کنم تا در هر اندازه‌ای از صفحه نمایش، تصویر به طور کامل در محل ImageView قرار گرفته و فضای خالی در اطراف آن ایجاد نشود:

افزودن یک ویجت به constraintlayout در اندروید استودیو

سپس گره‌های سه جهت بالا و چپ و راست را به سه طرف والد یا به عبارتی لبه‌های صفحه نمایش متصل می‌کنم:

افزودن یک ویجت به constraintlayout در اندروید استودیو

در تصویر بالا ملاحظه می‌کنید دو گره متصل شده یک فاصله ۱۶dp از لبه گرفته‌اند. قبلا این عدد را در Defalut Margin تعریف کرده بودیم. همچنین بعد از اینکه گره‌های بالا و سمت چپ را متصل کردم، به دلیل اینکه قبلا عرض تصویر به اندازه عرض صفحه پیش نمایش کشیده شده بود، گره سمت راست در داخل کادر قرار ندارد. اگر موس را بر روی خط چین سمت راست قرار داده و آنرا به سمت لبه راست صفحه بکشم خط اتصال برقرار می‌شود. یا اینکه در کادر Attributes در قسمت Constraint Widget روی نماد + سمت راست ویجت ImageView کلیک می‌کنم. اینجا فاصله ۱۶dp به طور خودکار اعمال نشد بنابراین به صورت دستی وارد می‌کنم. خروجی نهایی کار به اینصورت خواهد بود:

افزودن یک ویجت به constraintlayout در اندروید استودیو

layout_two.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:id="@+id/imageView5"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/img_22" />

</androidx.constraintlayout.widget.ConstraintLayout>

تا اینجای کار ما یک تصویر به بالای صفحه اضافه کردیم که در هر اندازه‌ای از صفحه نمایش، از سه جهت فاصله ۱۶dp خواهد داشت.

نکته: دوباره تاکید می‌کنم برای اینکه در طراحی با استفاده از این Layout یا ViewGroup مهارت کافی کسب کنید لازم است تمرینات زیادی انجام دهید. چند صفحه نمونه را به عنوان الگو (از سایر اپلیکیشن‌ها و سورس کدها) جلوی خود قرار داده و سعی کنید صفحه‌ای مشابه آن را بسازید.
ذهنتان را محدود به کارهایی که من انجام می‌دهم نکنید. برای مثال می‌توانستم اندازه کادر تصویر در صفحه پیش نمایش را کوچک کنم تا بتوانم هرسه گره را در داخل صفحه به لبه‌ها متصل کرده و بعد عرض را match constraint تعیین کنم. یا اینکه می‌شد در محیط Code به سادگی ویژگی‌های خط اتصال constraint و margin را برای ImageView تعیین کرد بدون آنکه نیازی به این جابجایی‌ها در محیط Design داشته باشیم.
شاید در نحوه تنظیم چیدمان من ایرادی وجود داشته باشد و شما بتوانید آنرا بهتر و ساده‌تر اجرا کنید. البته شاید که نه؛ حتما!

در قدم بعد قصد دارم یک آیکون یا تصویر را به نحوی روی تصویر فعلی قرار دهم که مرکز آن، لبه پایینی این ImageView باشد. ابتدا یک ImageView دیگر را از پالت روی صفحه پیش نمایش کشیده و یک آیکون یا تصویر را به دلخواه انتخاب می‌کنم:

قرار گرفتن مرکز یک ویجت روی حاشیه یک ویجت دیگر

کد این ویجت به اینصورت است:

<ImageView
    android:id="@+id/imageView6"
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:background="#DFCD2E"
    android:padding="5dp"
    app:srcCompat="@android:drawable/stat_sys_speakerphone"
    tools:layout_editor_absoluteX="47dp"
    tools:layout_editor_absoluteY="244dp" />

حالا گره‌های بالا و پایین آیکون را به گره پایین ImageView قبلی متصل می‌کنم:

قرار گرفتن مرکز یک ویجت روی حاضیه یک ویجت دیگر

قرار گرفتن مرکز یک ویجت روی حاضیه یک ویجت دیگر

قرار گرفتن مرکز یک ویجت روی حاضیه یک ویجت دیگر

ملاحظه می‌کنید پس از انجام این کار، مرکز افقی تصویر دوم بر روی خط پایینی تصویر اول قرار گرفت. به همین سادگی!
در نهایت نود سمت چپ را هم به سمت چپ تصویر بزرگ متصل می‌کنم. با توجه به اینکه قبلا مقدار ۱۶dp را برای فاصله پیش فرض یا همان Default Margin تعیین کرده بودم، در اینجا نیز همین مقدار برای فاصله تعیین می‌شود:

قرار گرفتن مرکز یک ویجت روی حاضیه یک ویجت دیگر

قرار گرفتن مرکز یک ویجت روی حاضیه یک ویجت دیگر

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

layout_two.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/imageView5"
        android:layout_width="0dp"
        android:layout_height="200dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:scaleType="centerCrop"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/img_22" />

    <ImageView
        android:id="@+id/imageView6"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:background="#DFCD2E"
        android:padding="5dp"
        app:layout_constraintBottom_toBottomOf="@+id/imageView5"
        app:layout_constraintStart_toStartOf="@+id/imageView5"
        app:layout_constraintTop_toBottomOf="@+id/imageView5"
        app:srcCompat="@android:drawable/stat_sys_speakerphone" />

</androidx.constraintlayout.widget.ConstraintLayout>

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

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

همتراز کردن ویجت‌های متنی توسط Baseline

یکی دیگر از قابلیت‌های بسیار کاربردی ConstraintLayout در ویجت‌های متنی مانند EditText و Button ها استفاده می‌شود و Baseline نام دارد. توسط این ویژگی به سادگی می‌توانیم دو ویجت را در راستای افقی در یک تراز قرار دهیم.
یک TextView و یک EditText به صفحه اضافه می‌کنم:

همتراز کردن Widget های متنی توسط ویژگی Baseline

قابلیت Baseline بصورت پیش فرض روی ویجت‌ها فعال نیست و با راست کلیک روی آن و انتخاب گزینه Show Baseline فعال می‌گردد:

همتراز کردن Widget های متنی توسط ویژگی Baseline

پس از انجام این کار یک قسمت کپسولی شکل به وسط EditText اضافه شد:

همتراز کردن Widget های متنی توسط ویژگی Baseline

با کشیدن آن به سمت وسط ویجت TextView کپسول Baseline این ویجت نیز ظاهر می‌شود. بنابراین خطی که از Baseline ویجت EditText کشیده شده را روی آن رها می‌کنم:

همتراز کردن Widget های متنی توسط ویژگی Baseline

همتراز کردن Widget های متنی توسط ویژگی Baseline
همتراز کردن Widget های متنی توسط ویژگی Baseline

حالا این دو ویجت در یک تراز قرار گرفته‌اند به طوری که متن درون EditText کاملا همراستای متن TextView دیده می‌شود. با تغییر موقعیت TextView ویجت EditText هم در همان جهت حرکت می‌کند و تراز باز هم برقرار باقی می‌ماند.
منابع تکمیلی: Developer ، Developer ، Developer ، Developer ، Developer ، Medium

جهت مطالعه ادامه آموزش، فایل PDF را دانلود نمائید

توجه : سورس پروژه درون پوشه Exercises قرار دارد

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

دانلود نسخه کامل این آموزش به همراه سورس پروژه
تعداد صفحات : ۱۰۷
حجم : ۴ مگابایت
قیمت : ۱۵ هزار تومان
توجه: صرفا در صورتی از درگاه پشتیبان استفاده کنید که قادر به پرداخت از طریق سبد دانلود نباشید.
افزودن به سبد دانلود درگاه پشتیبان
این مطلب چقدر برایتان مفید بود؟ لطفا امتیاز دهید
4.8/5 - (6 امتیاز)
پرسش‌ها و دیدگاه‌های کاربران
دوره آموزش برنامه نویسی اندروید
دوره آموزش برنامه نویسی اندروید

با دریافت این دوره به تمامی آموزش‌های غیر رایگان و رایگان موجود در وب سایت دسترسی دارید که تخفیفی برای آموزش‌های غیر رایگان نیز درنظر گرفته شده. این پکیج به دو صورت دانلودی و ارسال پستی ارائه می‌گردد.
آموزش‌های اندروید استودیو در دو دسته «پایه» و «تکمیلی» منتشر می‌شوند.
آموزش‌های پایه شامل مباحث اصلی و ضروری و آموزش‌های تکمیلی مطالبی است که می‌بایست در کنار مطالب اصلی بررسی شود.
با خرید این دوره، به تمامی آموزش‌های غیر رایگانی که در آینده منتشر می‌شود نیز به صورت رایگان دسترسی خواهید داشت!

یک دیدگاه بنویسید

پرسش‌های زیر تایید و پاسخ داده نـــخواهند شد:
۱: جزء موارد مطرح شده در صفحات مشکلات و پرسش‌های رایج و بروزرسانی‌های محتوای آموزشی باشد
۲: سوال قبلا توسط کاربران در دیدگاه‌ها مطرح و پاسخ داده شده باشد
۳: پرسش خارج از مبحث آموزشی موجود در این صفحه باشد

  • شادی گفت:

    چجوری ازتون تشکر کنم ک اینقدر قشنگ توضیح دادید و مخصوصا کامل واقعا ممنون من چند سال ب دلایلی برنامه نویسی گذاشتم کنار، مونده بودم چجوری دوباره شروع کنم وقتی زبانم خیلی خوب نیست، ولی اینقدر قشنگ توضیح دادید امیدوار شدم دستتون درد نکنه

  • محمد گفت:

    سلام خسته نباشید
    ایا مشکلی پیش نمیاد ما از چند constraintlayout داخل هم استفاده کنیم ؟
    چون بعضی جاها دیگه واقعا نیازه که یک سری ویو ها برای دسته بندی خوب داخل یک constraintlayout دیگه قرار بگیره
    یا اگر قابلیت خاصی برای این اینکه بتونیم چندین ویو رو در یک دسته استفاده کنیم تا بتونیم سایر ویو هارو نسبت به اون دسته نمایش بدیم هست بگید استفاده کنیم ( قابلیت Group رو من چک کردم فقط برای visibility قابل استفاده هست )

    • سیدمهدی مطهری گفت:

      هدف اصلی ConstraintLayout بی نیاز شدن از لایه های تو در تو یا nested هست و طبیعتا ایده خوبی نیست اینکار. من تست نکردم.
      بعید میدونم اگه قابلیت ها رو درست بکار بگیرید نیازی به این کار باشه

  • m-asghari گفت:

    باسلام و خسته نباشید چند بار برای خرید ConstraintLayout تلاش کردم ولی در صفحه دانلودهای من بدون استفاده درج شده لطفا بررسی و بفرمایید چکاری باید انجام دهم. از حسابم هم کم شده.
    باتشکر اصغری

    • سیدمهدی مطهری گفت:

      سلام. تراکنش ها رو بررسی کردم تراکنش داخل پنل ثبت نشده. این یعنی اگر هم مبلغ از حسابتون کسر شده مجدد از طریق بانک به حسابتون برگشت خورده. لطفا حسابتون رو بررسی کنید اگر برگشت نخورده از طریق صفحه “تماس با ما” اطلاع بدید پیگیری بشه.
      ممنون

  • فرزاد عسکری گفت:

    سلام مهندس/ تصویر به Design معرفی کردم کردم و وقتی خروجی میگیرم که روی شبیه ساز به نمایش در بیاد تصویر نصف هست کامل به نمایش درنمیاد.ممنون میشم مشکلم حل کنید/

  • ادریس گفت:

    سلام
    من قبلاً پکیج کامل گرفتم ولی الان این آموزش جدید داخل پکیج وجود نداره. بخام دانلود کنم هم میگه باید هزینه بدم باز.

    • سیدمهدی مطهری گفت:

      سلام. بزرگوار نحوه دریافت نسخه جدید دوره در توضیحات صفحه دوره آموزشی قید شده. باید وارد حساب کاربری بشید و در قسمت “دانلودهای من” مجدد دوره رو دانلود کنید. بدون پرداخت هزینه اضافی