اصلاح کننده ها را بنویسید

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

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

اصلاح‌کننده‌ها (Modifiers) اشیاء استاندارد کاتلین هستند. با فراخوانی یکی از توابع کلاس 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 باعث می‌شود که عنصر قابل ترکیب، حداکثر عرضی را که از والدش دریافت می‌کند، داشته باشد.

بهترین روش این است که تمام composable های شما یک پارامتر modifier بپذیرند و آن اصلاح کننده را به اولین فرزند آن که UI را منتشر می‌کند، منتقل کنند. انجام این کار باعث می‌شود کد شما قابلیت استفاده مجدد بیشتری داشته باشد و رفتار آن قابل پیش‌بینی‌تر و شهودی‌تر شود. برای اطلاعات بیشتر، به دستورالعمل‌های Compose 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 فهرستی از اصلاح‌کننده‌های داخلی را برای کمک به شما در تزئین یا تقویت یک Composable ارائه می‌دهد. در اینجا برخی از اصلاح‌کننده‌های رایج که برای تنظیم طرح‌بندی‌های خود استفاده خواهید کرد، آورده شده است.

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 در اطراف یک عنصر، یک اصلاح‌کننده padding تنظیم کنید.

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

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

متن با حاشیه بالای آن

افست

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

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

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

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

اصلاح‌کننده offset دو overload ارائه می‌دهد - offset که offsetها را به عنوان پارامتر می‌گیرد و offset که یک lambda می‌گیرد. برای اطلاعات بیشتر در مورد زمان استفاده از هر یک از این موارد و نحوه بهینه‌سازی برای عملکرد، بخش Compose performance - Defer reads as long as possible را مطالعه کنید.

ایمنی محدوده در Compose

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

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

ایمنی محدوده (scope safety) مانع از اضافه کردن اصلاح‌کننده‌هایی می‌شود که در سایر ترکیب‌پذیرها و محدوده‌ها کار نمی‌کنند و در زمان آزمون و خطا صرفه‌جویی می‌کند.

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

matchParentSize در Box

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

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

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

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

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

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

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

weight در Row و Column

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

بیایید یک 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 نشان‌دهنده یک رفتار منحصر به فرد است، مانند رفتارهای طرح‌بندی، ترسیم و گرافیک، تمام رفتارهای مرتبط با ژست، تمرکز و معناشناسی، و همچنین رویدادهای ورودی دستگاه. ترتیب آنها مهم است: عناصر اصلاح‌کننده‌ای که ابتدا اضافه می‌شوند، ابتدا اعمال می‌شوند.

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

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

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

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

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

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

هنگام مشاهده‌ی تغییرات مکرر وضعیت‌ها در داخل composableها، مانند animation stateها یا scrollState ، ممکن است تعداد قابل توجهی recomposition انجام شود. در این حالت، modifierهای شما در هر recomposition و احتمالاً برای هر فریم اختصاص داده می‌شوند:

@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
    )
}

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

// 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
    )
}

این می‌تواند به ویژه در ترکیب با طرح‌بندی‌های تنبل (Lazy layouts) مفید باشد. در بیشتر موارد، شما می‌خواهید همه تعداد آیتم‌هایتان، که احتمالاً قابل توجه هم هستند، دقیقاً اصلاح‌کننده‌های یکسانی داشته باشند:

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,
            )
        }
    }
}

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

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

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
        // ...
    )
    // ...
}

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

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)

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

بیشتر بدانید

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

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

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

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}