در مطلب قبل گفتیم که Jetpack Compose ( از این به بعد بهش میگیم کامپوز ) در سال 2019 معرفی شده و الان در ورژن 1.0 استیبل شده و در اختیار توسعه دهندگان قرار گرفته . اما واقعا کامپوز اومده که چه کاری رو برامون راحتر کنه ؟
کامپوز کتابخونه ایه که روش طراحی رابط کاربری رو تغییر داده . در روش قبلی ( View ) علاوه بر اینکه باید تعیین میکردیم چه چیزی در صفحه ، نمایش داده بشه ( با تعریف xml ) ، باید چگونگی نمایش دادن رو هم تعیین میکردیم ( کد نویسی در اکتیویتی با R.id.viewId ) . ولی در کامپوز فقط توصیف میکنیم چه چیزی میخوایم و بقیه رو کامپوز خودش انجام میده !! چه خوب ، بزارید یه مثال خیلی جالب برای مقایسه دو روش بزنم . فرض کنید از کسی میخوایم که برامون چای بیاره . در روش اول بهش میگیم " برو سماور روشن کن ، چای بریز تو قوری و آب جوش بریز و ...." ، ولی در روش دوم بهش میگیم " یکم چای با شکر " برام بیار و بقیه رو خودش انجام میده . به این روش میگن توصیفی یا اعلامی ( declarative )
برای اینکه بتونیم بیشتر با کامپوز آشنا بشیم یه پروژه کوچیک رو به عنوان تمرین شروع میکنیم تا ببینیم چقدر انجام همه کارهایی که قبل از این نیاز به کد نویسی زیادی داشت به سادگی با کامپوز قابل انجامه . در این تمرین از مطالب سایت گوگل استفاده شده
قبل از هر چیز باید ورژن جدید اندروید استودیو Arctic Fox رو از لینک زیر دانلود کنید . این ورژن تمام قابلیت های مورد نیاز برای استفاده از کامپوز رو در خودش داره
پس از نصب و اجرای اندروید استودیو روی New Project کلیک کنید
حالا کافیه Empty Compose Activity رو انتخاب کنید
اسمی برای پروژتون انتخاب کنید و minimumSdkVersion رو 12 قرار بدید ( کامپوز از اندروید 12 به بالاتر کار خواهد کرد ) . اندروید استودیو فایل های مورد نیاز رو برای پروژه آماده میکنه و حالا همه چیز آماده است فقط یه کار مونده . فایل Gradle باز کنید و در قسمت composeOptions خط kotlinCompilerVersion رو حذف کنید .
composeOptions { kotlinCompilerExtensionVersion compose_version // Remove it -> kotlinCompilerVersion 'version' }
علت اینکار اینه که کامپوز در ورژن جدید در buildscript از پلاگین کاتلین استفاده میکنه و این خط کد دیگه منسوخ ( deprecated ) شده ، میتونید از اینجا اصل مطلب رو بخونید
خوب همه چیز آماده است . بزارید با هم فایل MainActivity.kt رو بررسی کنیم . در بالای فایل مثل همیشه اکتیویتی اصلی برنامه مثل قبل دست نخورده مونده . قسمت جدید setContent به پروژه اضافه شده که دروازه ورود کامپوز به پروژه است ، یعنی هرچیزی که بخواهیم با کامپوز به پروژه اضافه کنیم به این قسمت اضافه خواهد شد
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { BasicsCodelabTheme { Surface(color = MaterialTheme.colors.background) { Greeting("Android") } } } } }
قطعه کد BasicsCodelabTheme تم اصلی برنامه رو معرفی میکنه که در فایل ui/Theme.kt قرار داره و اسمش بصورت اتوماتیک از روی اسم پروژه توسط اندروید استودیو ساخته شده و میتونید به هر اسمی تغییرش بدید . در قسمت های بعدی بیشتر به استایل دهی و تم خواهیم پرداخت .
تابع Greeting هم خود کامپوزه ، همون چیزی که توصیف کردیم تا در صفحه ، نمایش داده بشه
@Composable fun Greeting(name: String) { Text(text = "Hello $name!") }
همانطور که ساختار تابع کامپوز رو میبینید ، یک تابع معمولی کاتلینه ، که با Composable@ مزین شده ! و میشه توابع دیگر کامپوز رو توش صدا زد. همانطور که در تابع Greeting می بینید ، یک String به عنوان پارامتر دریافت میکنیم و اون رو در یک تابع کامپوز تعریف شده Text نشون میدیم. بدلیل استفاده از کاتلین در کامپوز به راحتی میشه از همه قابلیت های کاتلین در طراحی کامپوز استفاده کرد و نحوه نوشتن اون به سادگی نوشتن یک متد در کاتلینه و در مقایسه با کد های XML که برای layout در روش قبلی استفاده میشده بسیار ساده انعطاف پذیرتره .
@Preview(showBackground = true) @Composable fun DefaultPreview() { BasicsCodelabTheme { Greeting("Android") } }
اما میرسیم به قسمت جذاب کامپوز یعنی پیش نمایش . اینجا هم با یک تابع ساده کاتلین کامپوز شده ( تزئین شده با Composable@ ) طرفیم که یک Preview@ هم بهش اضافه شده . این تابع به اندروید استودیو میگه که میخوایم چیزی رو پیش نمایش بدیم و پنجره جدید Compose Preview برامون باز میکنه . کافیه از کامپوز هایی که در پروژه استفاده کردیم به این تابع اضافه کنیم تا در پیش نمایش دیده بشن ، فقط یادتون باشه که باید برای پارامتر های توابع حتما مقدار رو تعیین کنید . ما هم در این مثال از همون تابع Greeting استفاده کردیم و تنها کاری که میمونه اینه که پروژه رو build کنیم و ببینیم چی ساختیم
همچنین هر بار که تغییری در کد کامپوز ایجاد کنیم ، دکمه Build & Refresh در قسمت preview نمایش داده میشه که با کلیک روی اون پیش نمایش بروز خواهد شد .
اکثر عناصر کامپوز یک پارامتر اختیاری modifier دارند . پارامتر modifier تنظیمات مختلفی رو روی عناصر کامپوز اجرا میکنه ، مثل جایگیری روی صفحه یا نحوه نمایش یا رفتار اونها . می تونید اونها را به متغیرها اختصاص بدید و در جاهای مختلف ازش استفاده کنید. همچنین می توانید چندین modifier ها را یکی پس از دیگری با استفاده از توابع factory-extension یا با تابع then بهم متصل کنید . فقط یادتون باشه که ترتیب اجرای modifier ها همونجوری که نوشته میشن از بالا اولین modifier تغییرات رو روی عناصر اعمال خواهد کرد .
برای مثال پارامتر padding modifier فضایی خالی را در اطراف عناصر اعمال می کنه .برای اینکار modifier را به Text خود اضافه کنید:
Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
همینطور که کامپوز های جدید را به صفحه اضافه میکنیم کد ها بصورت تو در تو ادامه پیدا میکنن و هر چه که کد ما بزرگتر میشه درک اون پیچیده تر شده و امکان استفاده از اون در جاهای دیگه هم کمتر میشه . طبق اصول Clean Code ما باید کد هامون رو جوری طراحی کنیم که به قسمت های کوچیک با ساختار و خروجی درست تقسیم بشه و در کامپوز هم باید سعی کنیم با ساخت کامپوز بصورت کامپوننت های کوچک بتونیم قابلیت استفاده مجدد کد را بالا ببریم و سرعت تست پذیری و یافتن خطا در اون رو هم افزایش بدیم
به عنوان اولین تغییر بگذارید کل رابط کاربری این اکتیویتی را به یک متد کامپوز MyApp انتقال بدیم .
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyApp() } } }
@Composable fun MyApp() { BasicsCodelabTheme { Surface(color = Color.Yellow) { Greeting(name = "Android") } } }
@Composable fun Greeting(name: String) { Text(text = "Hello $name!", modifier = Modifier.padding(24.dp)) }
@Preview @Composable fun DefaultPreview() { MyApp() }
متد کامپوز MyApp میتونه به عنوان بدنه اصلی تشکیل دهنده رابط کاربری در جاهای دیگه هم استفاده بشه ، ولی یک مشکل وجود داره و اون هم وجود متد Greeting در MyApp که مختص این اکتویتیه و جای دیگه بهش نیاز نداریم . پس باید MyApp رو جوری تغییر بدیم که بصورت یک کامپوننت کامل قابل حمل بشه ازش در اکتیویتی های دیگه هم استفاده کرد
چیزی که ما نیاز داریم یک متد عمومیه که محتوایی رو مثلا به عنوان پارامتر content دریافت کنه و در بدنه خودش تنظیمات اصلی نمای صفحه را انجام بده . اینجوری میشه محتوای صفحه رو از قالب اصلی جدا کرده و در صفحات دیگه هم از این متد عمومی هم استفاده کرد
@Composable fun MyApp(content: @Composable () -> Unit) { BasicsCodelabTheme { Surface(color = Color.Yellow) { content() } } }
به لطف امکان افزودن تابع ، به عنوان پارامتر در توابع کاتلین ، به راحتی میتوان یک کامپوز رو بعنوان پارامتر دریافت کرد و به این صورت تنظیمات اصلی صفحه مانند تم متریال و رنگ پس زمینه و یا هر چیز دیگر را که معمولا در همه اکتیویتی ها یکسانه به این تابع اضافه کنیم و نهایتا تابع کامپوز دریافتی ( content ) را به عنوان محتوای داخل صفحه قرار بدیم . برای استفاده از این قالب هم به اینصورت انجام میدم .
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyApp { Greeting("Android") } } } }
شاید تعجب کنید وقتی مبینید تابع کامپوز Greeting که به عنوان پارامتر به MyApp فرستاده میشه در بدنه تابع نوشته شده و در پرانتز ابتدای تابع نیامده ! این یکی از ویژگی های کاتلینه که میشه در زمان فرستادن تابعی به عنوان پارامتر به تابع دیگر آن را بجای پرانتز در خود بدنه تابع تعریف کرد و کد بالا رو میشه به این صورت هم نوشت :
MyApp({ Greeting(name = "َAndroid") })
کامپوز و کاتلین هماهنگی خوبی با هم دارن چون کامپوز کلا با کاتلین نوشته شده و میشه ازش مثل کد های متداول استفاده کرد .مثلا فرض کنید بخوایم چند ردیف از Text رو در صفحه نمایش بدیم . اینکار با Column
که چیزی شبیه LinearLayout هست انجام میشه و میشه با ایجاد یک حلقه for به راحتی کد های کاتلین رابط کاربری مورد نظر رو ساخت و این یعنی سادگی درک و فهم و ساخت هر آنچیزی که نیاز دارید
@Composable fun MyScreenContent(names: List<String> = listOf("Android", "there")) { Column { for (name in names) { Greeting(name = name) Divider(color = Color.Black) } } }
این مطلب ادامه داره ......