نوشتن اصلاح‌کننده‌ها

اصلاح‌کننده‌ها به شما امکان می‌دهند یک عنصر ترکیبی را تزئین یا تقویت کنید. اصلاح‌کننده‌ها به شما امکان می‌دهند این نوع کارها را انجام دهید:

  • اندازه، چیدمان، رفتار، و ظاهر عنصر ترکیبی را تغییر دهید
  • اطلاعاتی مثل برچسب‌های دسترس‌پذیری اضافه کنید
  • پردازش درونداد کاربر
  • تعامل‌های سطح بالا، مثل کلیک‌کردنی کردن عنصر، پیمایش‌پذیر کردن، کشیدنی کردن، یا بزرگ‌نمایی‌پذیر کردن را اضافه کنید

اصلاح‌کننده‌ها اشیای استاندارد Kotlin هستند. با فراخواندن یکی از توابع کلاس Modifier، یک اصلاح‌گر بسازید:

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

دو خط نوشتار روی پس‌زمینه رنگی، با حاشیه اطراف نوشتار.

می‌توانید این توابع را به‌هم زنجیر کنید تا آن‌ها را ترکیب کنید:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

پس‌زمینه رنگی پشت نوشتار اکنون در کل عرض دستگاه گسترده می‌شود.

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

  • ‫padding در اطراف عنصر فاصله ایجاد می‌کند.
  • ‫fillMaxWidth باعث می‌شود عنصر ترکیبی حداکثر پهنای داده‌شده از عنصر والد خود را پر کند.

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

ترتیب اصلاح‌گرها مهم است

ترتیب توابع اصلاح‌گر مهم است. ازآنجایی‌که هر تابع تغییراتی در Modifierبرگردانده‌شده توسط تابع قبلی ایجاد می‌کند، ترتیب بر نتیجه نهایی تأثیر می‌گذارد. بیایید مثالی از این مورد ببینیم:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

کل ناحیه، ازجمله حاشیه اطراف لبه‌ها، به کلیک‌ها پاسخ می‌دهد

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

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

حاشیه اطراف لبه چیدمان دیگر به کلیک‌ها پاسخ نمی‌دهد

اصلاح‌گرهای داخلی

‫Jetpack Compose فهرستی از اصلاح‌کننده‌های داخلی ارائه می‌دهد تا به شما کمک کند یک عنصر ترکیبی را تزئین یا تقویت کنید. در اینجا چند اصلاح‌گر رایج که برای تنظیم چیدمان‌هایتان استفاده می‌کنید آورده شده است.

‫padding و size

به‌طور پیش‌فرض، چیدمان‌های ارائه‌شده در Compose فرزندانشان را می‌پیچند. بااین‌حال، بااستفاده از اصلاح‌گر size می‌توانید اندازه را تنظیم کنید:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

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

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

تصویر کودک بزرگ‌تر از محدودیت‌های ناشی از والدینش است

در این مثال، حتی با تنظیم height والد روی 100.dp، ارتفاع Image برابر با 150.dp خواهد بود، زیرا اصلاح‌گر requiredSize اولویت دارد.

اگر می‌خواهید چیدمان فرزند تمام ارتفاع دردسترس مجاز توسط والد را پر کند، اصلاح‌گر fillMaxHeight را اضافه کنید (Compose همچنین fillMaxSize و fillMaxWidth را ارائه می‌دهد):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

ارتفاع تصویر به‌اندازه عنصر والد آن است

برای افزودن حاشیه در اطراف عنصر، یک اصلاح‌گر padding تنظیم کنید.

اگر می‌خواهید بالای خط پایه نوشتار لایه‌گذاری اضافه کنید تا به فاصله مشخصی از بالای چیدمان تا خط پایه برسید، از اصلاح‌گر paddingFromBaseline استفاده کنید:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

نوشتار با حاشیه در بالا

آفست

برای قرار دادن چیدمان نسبت‌به موقعیت اصلی آن، اصلاح‌گر offset را اضافه کنید و انحراف را در محور x و y تنظیم کنید. انحراف‌ها می‌توانند مثبت و همچنین غیرمثبت باشند. تفاوت بین padding و offset این است که افزودن offset به یک عنصر ترکیبی اندازه‌های آن را تغییر نمی‌دهد:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

نوشتار به سمت راست محتوی والد خود منتقل شد

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

اصلاح‌گر offset دو سربار ارائه می‌دهد - offset که آفست‌ها را به‌عنوان پارامتر می‌گیرد و offset که لامبدا را می‌گیرد. برای اطلاعات دقیق‌تر درباره زمان استفاده از هریک از این موارد و نحوه بهینه‌سازی برای عملکرد، بخش عملکرد «نوشتن» - خواندن را تا حد امکان به‌تعویق بیندازید را بخوانید.

هدف‌گیری ایمنی در «نوشتن»

در «نوشتن»، اصلاح‌کننده‌هایی وجود دارد که فقط وقتی می‌توانند استفاده شوند که روی فرزندان ترکیب‌پذیرهای خاصی اعمال شوند. ‫Compose این کار را ازطریق محدوده‌های سفارشی انجام می‌دهد.

برای مثال، اگر می‌خواهید کودک را به اندازه والدین Box بدون تأثیر بر اندازه Box بزرگ کنید، از اصلاح‌گر matchParentSize استفاده کنید. ‫matchParentSize فقط در BoxScope دردسترس است. بنابراین، فقط برای کودکی که ولی او Box باشد قابل‌استفاده است.

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

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

matchParentSize در Box

همان‌طور که در بالا ذکر شد، اگر می‌خواهید چیدمان فرزند هم‌اندازه با والد باشد Box بدون اینکه بر اندازه Box تأثیر بگذارد، از اصلاح‌گر matchParentSize استفاده کنید.

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

در مثال زیر، کودک Spacer اندازه خود را از والدش Box می‌گیرد، که به نوبه خود اندازه خود را از بزرگ‌ترین کودکان، ArtistCard در این مورد، می‌گیرد.

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

پس‌زمینه خاکستری که ظرفش را پر می‌کند

اگر به‌جای matchParentSize از fillMaxSize استفاده شود، Spacer تمام فضای دردسترس مجاز برای عنصر مادر را اشغال می‌کند و درنتیجه باعث می‌شود عنصر مادر گسترش یابد و تمام فضای دردسترس را پر کند.

پس‌زمینه خاکستری که صفحه را پر می‌کند

‫weight در «Row» و Column

همان‌طور که در بخش قبلی درباره فضای خالی و اندازه دیدید، به‌طور پیش‌فرض، اندازه عنصر ترکیبی با محتوایی که دربرمی‌گیرد تعریف می‌شود. بااستفاده از «اصلاح‌گر» weight که فقط در RowScope و ColumnScope دردسترس است، می‌توانید اندازه عنصر ترکیبی را به‌گونه‌ای تنظیم کنید که در عنصر والد انعطاف‌پذیر باشد.

بیایید Row را که حاوی دو عنصر ترکیبی Box است درنظر بگیریم. جعبه اول دو برابر weight جعبه دوم است، بنابراین دو برابر عرض دارد. ازآنجایی‌که Row به‌اندازه 210.dp پهنا دارد، Box اول به‌اندازه 140.dp پهنا دارد، و دومی به‌اندازه 70.dp پهنا دارد:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

عرض تصویر دو برابر عرض نوشتار است

استخراج و استفاده مجدد از اصلاح‌کننده‌ها

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

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

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

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

روال‌های مطلوب برای استفاده مجدد از اصلاح‌کننده‌ها

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

استخراج و استفاده مجدد از اصلاح‌کننده‌ها هنگام مشاهده وضعیت‌های مکرراً درحال تغییر

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

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

درعوض، می‌توانید نمونه یکسانی از اصلاح‌کننده را ایجاد، استخراج، و مجدداً استفاده کنید و آن را به این صورت به عنصر ترکیبی منتقل کنید:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

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

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

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

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

استخراج و استفاده مجدد از اصلاح‌گرهای محدوده‌دار

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

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

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

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

زنجیره‌سازی بیشتر اصلاح‌کننده‌های استخراج‌شده

با فراخواندن تابع .then() می‌توانید زنجیره‌های اصلاح‌گر استخراج‌شده را بیشتر به‌هم متصل کنید یا به آن‌ها اضافه کنید:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

فقط به‌خاطر داشته باشید که ترتیب اصلاح‌کننده‌ها مهم است!

بیشتر بدانید

ما فهرست کاملی از اصلاح‌کننده‌ها، با پارامترها و محدوده‌های آن‌ها ارائه می‌دهیم.

برای تمرین بیشتر درباره نحوه استفاده از اصلاح‌کننده‌ها، می‌توانید چیدمان‌های پایه در codelab Compose را نیز مرور کنید یا به مخزن Now in Android مراجعه کنید.

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