در مطلب قبل پروژه ی جدید کامپوز رو شروع کردیم و تغییراتی توش اعمال کردیم و دیدیم که چقدر کامپوز کارها رو راحت کرده . حالا میخوایم بیشتر با کامپوز آشنا بشیم
رابط کاربری در برنامه های اندروید باید داده هایی رو به کاربر نشون بدن و این داده ها همیشه از اول برنامه ثابت نیست و در طول اجرای برنامه تغییر میکنه . کامپوز به این داده ها میگه وضعیت State . اما وضعیت چیه ؟
همونطور که دیدیم توابع کامپوز ، اطلاعات رو گرفته و در صفحه ، نمایش میدن . حالا اگر اطلاعات تغییر کنن ، کامپوز اتوماتیک میره اون تابعی که اطلاعات رو میگرفت و نشون میداد رو صدا میزنه و بهش میگه بیا داده ات تغییر کرده و خودت رو با داده جدید به روز کن ! ، که ما به این کار میگیم Recomposing و کامپوز وقتی میتونه چنین کاری کنه که اطلاعات تغییر پذیر رو به صورت وضعیت state براش تعریف کنیم . البته کامپوز میدونه کدوم عناصر به این داده احتیاج دارن و فقط همونها رو به روز میکنه و اینکار سرعت تغییر در رابط کاربر رو بسیار بالا میبره .
برای اضافه کردن وضعیت در کامپوز باید از توابع mutableStateOf استفاده کنیم ، که میشه گفت یک حافظه قابل تغییر کامپوز شده است و برای اینکه وضعیت رو ذخیره شده داشته باشیم باید از remember استفاده کنیم . حالا عناصر کامپوز در جاهای مختلف صفحه وضعیت رو بصورت متغییر داخل خودشون استفاده میکنن و بصورت خودکار هر وقت Recomposing اتفاق بیافته وضعیت جدید رو میگیرن و صفحه نمایش رو به روز میکنن .
برای تمرین یک شمارنده میسازیم که تعداد دفعات که کاربر روی Button کلیک کرده رو میشماره . شمارنده ما از یک Button تشکیل شده که محتوای داخل اون با یک Text تعداد دفعات کلیک شدن رو نشون میده و متد برای Button تعداد کلیک شدن رو افزایش میده :
@Composable fun Counter() { val count = remember { mutableStateOf(0) } Button( = { count.value++ }) { Text("I've been clicked ${count.value} times") } }
همونطور که میبینیم count با استفاده از remember و mutableStateOf با مقدار اولیه صفر ، بصورت وضعیت در کامپوز تعریف شده . این یعنی وقتی count تغییر کنه ( در این مثال وقتی کاربر روی Button کلیک میکنه ++count.value ) هر کی ازش استفاده کرده ( در این مثال Text ) باید مقدار جدید رو نشون بده و اصطلاحا Recomposing اتفاق می افته .
بیاید با هم شمارنده رو به پروژه قبلیمون اضافه میکنیم
@Composable fun MyScreenContent(names: List<String> = listOf("Android", "there")) { Column { for (name in names) { Greeting(name = name) Divider(color = Color.Black) } Divider(color = Color.Transparent, thickness = 32.dp) Counter() } }
و اینم خروجی ، دکمه ای که با کلیک روش نوشته اش تغییر میکنه
وضعیت ها در کامپوز باید جایی تعریف بشن که تخصیص اطلاعات و به روز رسانی اونها راحتر باشه . ما در کامپوز که پر است از توابع تو در تو و پیچیده ، سعی میکنیم وضعیت ها رو در سطح توابع بالاتر تعریف کنیم و آنها رو بصورت پارامتر به تابع کامپوز بفرستیم تا کنترل بهتری روش داشته باشیم ، به اینکار state hoisting می گن .
با تعریف وضعیت ها در سطح بالاتر عملا از وضعیت های تکراری و ایرور ها جلو گیری میکنیم و همچنین امکان به روز رسانی چند تابع کامپوز رو با هم با یک وضعیت از قبل تعریف شده در تابع بالاتر فراهم میکنیم . اینکار در عین حال قابلیت استفاده مجدد و تست پذیری کد های کامپوز رو بالا میبره
به عنوان تمرین میخوایم وضعیت شمارش کلیک توسط کاربر رو در پروژه مون بالا ببریم یا اصطلاحا state hoisting کنیم. یعنی تابع کامپوز Counter ما ، متغییر count رو به عنوان پارامتر دریافت خواهد کرد . علاوه بر خود وضعیت در این مثال ، با استفاده قابلیت ارسال تابع به عنوان پارامتر ، حتی متد تغییر دهنده وضعیت رو هم بالا میبریم و به عنوان پارامتر به Counter میفرستیم . عملا با اینکار کنترل بهتری روی وضعیت و نحوه تغییر وضعیت خواهیم داشت . تابع Counter ما خیلی خوب قابلیت استفاده مجدد خواهد داشت و تست کردن چنین تابعی هم خیلی راحته
@Composable fun Counter(count: Int, updateCount: (Int) -> Unit) { Button( = { updateCount(count+1) }) { Text("I've been clicked $count times") } }
اینجوری هم این تابع رو استفاده میکنیم . به نحوه ارسال متد به عنوان پارامتر توجه کنید .
@Composable fun MyScreenContent(names: List<String> = listOf("Android", "there")) { val counterState = remember { mutableStateOf(0) } Column { for (name in names) { Greeting(name = name) Divider(color = Color.Black) } Divider(color = Color.Transparent, thickness = 32.dp) Counter( count = counterState.value, updateCount = { newCount -> counterState.value = newCount } ) } }
به همین سادگی میشه توابع کامپوزی ساخت که خاصیت تست پذیری و استفاده مجدد بالایی داشته باشن .
این مطلب ادامه دارد ....