اخبار محصول

ویژگی‌های جدید در نسخه ۲۶ اوت Jetpack Compose

‫۵ دقیقه خواندن
مشاهده نمایه نیک بوچر
Nick Butcher مدیر محصول

امروز، نسخه ۲۶ اوت Jetpack Compose پایدار است! این نسخه، نسخه ۱.۱۲ را در سراسر ماژول‌های اصلی Compose ارائه می‌دهد (نگاشت کامل BOM را ببینید)، میاناهای برنامه‌سازی کاربردی بصری غنی مانند «گرادیان‌های مش» و پشتیبانی از «طیف رنگی گسترده» (WCG)، ویژگی‌های چیدمان ساختاری مانند نواحی نام‌گذاری‌شده در Grid، ادغام یکپارچه با Credential Manager در Android، و بهبودهای قابل‌توجه در آزمایش و عملکرد را معرفی می‌کند.

برای به‌روزرسانی پروژه‌تان به نسخه امروز، نسخه Compose BOM خود را به 2026.08.00 ارتقا دهید:

implementation(platform("androidx.compose:compose-bom:2026.08.00"))

تغییرات ناگهانی

  • AGP و Compile SDK: Compose 1.12 compileSdk را به API 37 به‌روز می‌کند که به حداقل AGP 9.1.2 نیاز دارد. یادآوری می‌کنیم که Compose همیشه آخرین compileSdk را هدف قرار می‌دهد. در اینجا درباره این تغییر بیشتر بدانید.
  • Modifier.onFirstVisible() منسوخ شده است: به Modifier.onVisibilityChanged() منتقل شوید که ردیابی آستانه رؤیت‌پذیری دقیق‌تری ارائه می‌دهد.

گرافیک

Mesh Gradients

‫Compose نسخه ۱.۱۲ MeshGradientPainter را معرفی می‌کند تا به شما کمک کند طیف‌های رنگی چندنقطه‌ای و طبیعی ایجاد کنید.

meshGradient.gif
val rows = 1
val columns = 1

val gradientPainter = remember {
    MeshGradientPainter(rows, columns) {
        // Parameters: row, column, position, color
        setVertex(0, 0, Offset(0f, 0f), Color.Red)     // Top-Left
        setVertex(0, 1, Offset(1f, 0f), Color.Blue)    // Top-Right
        setVertex(1, 0, Offset(0f, 1f), Color.Green)   // Bottom-Left
        setVertex(1, 1, Offset(1f, 1f), Color.Yellow)  // Bottom-Right
    }
}

Box(
    modifier = modifier
        .aspectRatio(16/9f)
        .fillMaxWidth()
        .paint(gradientPainter)
)

برای اطلاعات و مثال‌های بیشتر، به اسناد مراجعه کنید.

پشتیبانی از HDR و Wide Color Gamut

نمایشگرهای مدرن دقت رنگ گسترده‌تر و دامنه پویای بالاتری ارائه می‌دهند. در Compose نسخه ۱.۱۲، پشتیبانی کامل از خط لوله برای طیف رنگی گسترده (P3) و پرداخت HDR در سراسر گرافیک، رنگ، و سایه‌زن‌های Compose فعال شده است. رنگ‌های تعریف‌شده در فضاهای رنگی غیر sRGB (مانند Display P3) بدون محدود کردن رنگ، تا زمان پردازش پلاتفرم حفظ می‌شوند. اگر رنگ‌ها از فضای رنگی پشتیبانی‌نشده (مثلاً CieXyz،‏ CieLab، یا Oklab) استفاده کنند، به فضای رنگی در نسخه Android پشتیبانی‌نشده (مثلاً Bt2020Hlg در Android 13 و نسخه‌های پایین‌تر) متکی باشند، یا اگر برنامه در Android 9 (میانای برنامه‌سازی کاربردی ۲۸) و نسخه‌های پایین‌تر اجرا شود، رنگ‌ها به‌طور ایمن به sRGB برمی‌گردند.

تغییرات قابل‌توجه دیگر:

  • LayerOutsets به GraphicsLayer و Modifier.graphicsLayer اضافه شد که می‌توانید از آن برای افزایش مرزهای دیداری لایه فراتر از اندازه اندازه‌گیری‌شده آن استفاده کنید. LayerOutsets را اعمال کنید تا از رفتار ضمنی clipToBounds هنگام ارتقای لایه به میان‌گیر خارج از صفحه جلوگیری شود.

سبک‌ها

در Google I/O، چشم‌انداز اولیه خود را برای Compose Styles API—یک روش یکپارچه و کارآمد برای سبک‌دهی به مؤلفه‌ها—به‌اشتراک گذاشتیم. از آن زمان، ما به ساخت معماری زیربنایی ادامه داده‌ایم تا ایمنی تایپی دقیق و درستی قابل‌پیش‌بینی را تضمین کنیم و از ساخت سیستم‌های طراحی سفارشی پشتیبانی کنیم. 

برای اطمینان از اینکه این لایه بنیادی را به‌درستی دریافت می‌کنیم، میانای برنامه‌سازی کاربردی آزمایشی باقی خواهد ماند و می‌توانید انتظار تغییرات اساسی را داشته باشید. 

بهینه‌سازی‌های زمان اجرا

Keyed SideEffect Overload

SideEffect اکنون از آرگومان‌های کلیدی پشتیبانی می‌کند که به شما امکان می‌دهد هر زمان که کلیدهای خاصی تغییر می‌کنند، عوارض جانبی یک‌باره را فعال کنید. این کار می‌تواند عملکرد را درمقایسه بااستفاده از LaunchedEffect یا DisposableEffect بهبود بخشد، به‌ویژه زمانی که به coroutine یا dispose block نیاز ندارید. SideEffect تا ۹۰٪ سریع‌تر از LaunchedEffect و حدود ۲۰٪ سریع‌تر از DisposableEffect است. توجه داشته باشید که SideEffect جلوه خود را قبل‌از DisposableEffect و LaunchedEffect اجرا می‌کند، بنابراین هنگام انتقال جلوه‌های موجود به این API احتیاط کنید، به‌ویژه برای LaunchedEffects که برای شروع پس‌از تکمیل فریم فعلی به ارسال شدن متکی هستند. 

@Composable
fun AnalyticsTracker(userId: String, screenName: String) {
    SideEffect(key1 = userId, key2 = screenName) {
        analytics.logScreenView(userId, screenName)
    }
}

پویانمایی

انتقال‌های تعاملی دو مرحله‌ای

  • ترکیب‌پذیرهای جدید: DeferredAnimatedContent و DeferredAnimatedVisibility امکان ایجاد انتقال‌های دو مرحله‌ای لذت‌بخش را فراهم می‌کنند، مثلاً برای ردیابی اشاره برگشت پیش‌گویانه.
  • کنترل دستی پویانمایی: درطول فاز تأخیری انتقال، ویژگی‌های پویانمایی‌شده (مانند مقیاس یا جابه‌جایی) اکنون می‌توانند به‌صورت دستی و هم‌زمان دستکاری شوند (مثلاً ردیابی اشاره تند کشیدن).
  • انتقال یکپارچه: پس‌از پایان فاز تأخیری، موتور گذار کنترل را به‌دست می‌گیرد و یک انتقال یکپارچه، ازجمله انتقال سرعت، به گذار خودکار انجام می‌دهد.
  • پشتیبانی از عنصر هم‌رسانی‌شده: پرچم جدید permitTransformDuringDeferredTransition در SharedContentConfig کنترل می‌کند که آیا عناصر هم‌رسانی‌شده درطول فاز انتقال معوق به‌صورت بصری همراه با محفظه‌های مادرشان تبدیل می‌شوند یا نه.
val state = remember { DeferredTransitionState(initialScreen) }
val transition = rememberDeferredTransition(state)

if (predictiveBackInProgress) {
    state.defer(targetScreen)
} else {
    state.animateTo(targetScreen)
}

transition.DeferredAnimatedContent(
    targetState = targetScreen,
    mutableTransformSpec = {
       MutableContentTransform {
           // Manually manipulate properties during the deferred phase
           initialContentTransform { scale = swipeProgress }
       }
    }
) { screen ->
    ScreenContent(screen)
}

در زیر دو نسخه نمایشی از موارد استفاده‌ای که در آن پویانمایی مبتنی بر اشاره به پویانمایی راه‌انداز منتقل می‌شود آورده شده است:

colorCards.gif

ادغام‌های نوشتار، ورودی، و پلاتفرم

قالب‌بندی نوشتار قابل‌ویرایش

میاناهای برنامه‌سازی کاربردی جدید قالب‌بندی متن غنی را برای نوشتار قابل‌ویرایش در BasicTextField ارائه می‌دهند. اکنون می‌توانید بااستفاده از SpanStyle و ParagraphStyle ازطریق روش جدید addStyle() در محدوده TextFieldBuffer (مانند داخل textFieldState.edit { ... } یا InputTransformation)، قالب‌بندی نویسه و پاراگراف درون‌خطی را به‌صورت برنامه‌نویسی‌شده اعمال و دستکاری کنید. علاوه‌براین، TextFieldBuffer میاناهای برنامه‌سازی کاربردی getSpanStyles() و getParagraphStyles() را ارائه می‌دهد که اشیاء TrackedRange را برمی‌گردانند و به شما امکان می‌دهند سبک‌های اعمال‌شده را بخوانید، به‌روز کنید، یا بردارید. برای تکمیل ایجاد قالب‌بندی، TextFieldState اکنون یک ویژگی فقط خواندنی textStyles را برای پرسمان سبک‌های فعال در محدوده‌ها نشان می‌دهد، درحالی‌که TextFieldBuffer originalTextStyles را برای بررسی وضعیت قالب‌بندی قبل‌از ویرایش ارائه می‌دهد. قالب‌بندی نوشتار و حاشیه‌نویسی‌های سفارشی در تغییرات پیکربندی حفظ می‌شوند.

val state = rememberTextFieldState("Formatted text in Compose 1.12")

// Apply bold and color styles to a range of text
state.edit {
    addStyle(
        SpanStyle(fontWeight = FontWeight.Bold, color = Color.Blue),
        start = 0,
        end = 9
    )
}

// Query active styles from TextFieldState
val currentStyles = state.textStyles

انتخاب نوشتار

SelectionState API جدید کنترل برنامه‌ای و قابلیت مشاهده را بر انتخاب نوشتار در SelectionContainer فراهم می‌کند. Hoisting a SelectionState object via rememberSelectionState() and passing into SelectionContainer exposes selectedTexts as a reactive list of AnnotatedStrings and provides methods like selectAll(), clear(), select(TextRange), and extendSelectionByWord().

علاوه‌براین، از getSelectableTexts() برای بازیابی همه موارد نوشتاری قابل‌انتخاب در ترتیب چیدمان و انتخاب نوشتار در سراسر composable در SelectionContainer بااستفاده از محدوده سراسری استفاده کنید. 

@Composable
fun ProgrammaticSelectionExample() {
    val selectionState = rememberSelectionState()

    Column {
        Button(
            onClick = { selectionState.selectAll() },
            modifier = Modifier.disableSelectionClearOnTap()
        ) {
            Text("Select All")
        }

        SelectionContainer(state = selectionState) {
            Text("Text content to be selected programmatically.")
        }
    }
}

یکپارچه‌سازی Credential Manager

فیلدهای نوشتاری Compose اکنون به‌طور بومی با Credential Manager (میانای برنامه‌سازی کاربردی ۳۴+) Android ازطریق چارچوب «تکمیل خودکار» ادغام می‌شوند (زیر میانای برنامه‌سازی کاربردی ۳۴ توسط androidx.credentials library مدیریت می‌شود). با پیوست کردن ویژگی معنایی جدید credentialRequest با CredentialRequestData، ورودی‌های نوشتاری می‌توانند گذرکلیدها، اطلاعات کاربری ذخیره‌شده، یا درخواست‌های ورود به سیستم را مستقیماً در جریان ورودی کاربر درخواست کنند.

@Composable
fun LoginField(textFieldState: TextFieldState) {
    val credentialData = remember {
        CredentialRequestData(
            // Specify Credential Manager request options
        )
    }

    BasicTextField(
        state = textFieldState,
        modifier = Modifier.semantics {
            credentialRequest = credentialData
        }
    )
}

تغییرات قابل‌توجه دیگر:

  • پشتیبانی از تنظیمات گوناگونی قلم در قلم‌های بارگیری‌شدنی.
  • پیمایش خودکار هنگام کشیدن نوشتار انتخاب‌شده به فراتر از نمای دید در SelectionContainer فعال شد.
  • پشتیبانی از صداهای تعامل خودکار (کلیک‌ها و پیمایش تمرکز) به مؤلفه‌های Compose اضافه شد، با یک SoundEffectOnInteraction قابل‌ساخت جدید برای اجازه انصراف. توجه داشته باشید که درنتیجه این تغییر، شنوندگان کلیک معنایی اکنون باید از رشته اصلی فراخوانی شوند که ممکن است بر تعداد کمی از چارچوب‌های تست تأثیر بگذارد.
  • KeyboardType اکنون شامل Date، Time، DateTime، و SignedDecimal می‌شود.
  • BasicSecureTextField اکنون به‌طور پیش‌فرض از TextObfuscationMode.System استفاده می‌کند، درحالی‌که RevealLastTyped به‌عنوان لغو مطلق عمل می‌کند.

بهبودهای چیدمان

مناطق نام‌گذاری‌شده در چیدمان شبکه‌ای

ساختن چیدمان‌های پیچیده دوبعدی اکنون با مناطق نام‌گذاری‌شده در مؤلفه @Experimental جدول آسان‌تر شده است. به‌جای مدیریت شاخص‌های عددی ستون و ردیف در بین موارد، می‌توانید مناطق معنایی را در GridConfigurationScope خود تعریف کنید و ترکیب‌پذیرها را براساس نام منطقه قرار دهید.

@OptIn(ExperimentalGridApi::class)
@Composable
fun DashboardLayout() {
    Grid(
        config = {
            area("header", row = 0, column = 0, rowSpan = 1, columnSpan = 2)
            area("sidebar", row = 1, column = 0)
            area("content", row = 1, column = 1)
            gap(16.dp)
        }
    ) {
        HeaderSection(modifier = Modifier.gridItem(areaId = "header"))
        NavigationSidebar(modifier = Modifier.gridItem(areaId = "sidebar"))
        MainContentView(modifier = Modifier.gridItem(areaId = "content"))
    }
}

برای اطلاعات بیشتر، به مستندات مراجعه کنید.

عملکرد

مثل هر نسخه دیگری، ما همچنان روی عملکرد Compose سرمایه‌گذاری می‌کنیم تا مطمئن شویم که این چارچوب به شما کمک می‌کند برنامه‌های زیبا و کارآمد بسازید. در این نسخه، بر بهبود عملکرد راه‌اندازی تمرکز کرده‌ایم و اکنون «زمان تا نمایش اولیه» (مدت‌زمانی که طول می‌کشد تا برنامه اولین قاب خود را تولید کند) را می‌بینیم که با «نماها» در معیارهای ما قابل‌مقایسه است.

timetoinitialdisplay@2x _v2.png

ارتقاهای ابزار و آزمایش

همگام‌سازی آزمایشی

Compose نسخه ۱.۱۲ میاناهای برنامه‌سازی کاربردی آزمایشی جدیدی را معرفی می‌کند که برای کاهش زمان اجرای آزمایش و حذف ناپایداری درطول نمونه‌برداری حالت طراحی شده‌اند:

  • hasPendingWork: به‌طور غیرفعال بررسی می‌کند که آیا واسط کاربر کار معلقی دارد یا نه، بدون اینکه ساعت را جلو ببرد، که برای حلقه‌های پویانمایی دستی ایده‌آل است.
  • runWithoutImplicitWait: همگام‌سازی ضمنی را هنگام گام برداشتن در قاب‌های ساعت دستی (مثلاً آزمایش‌های پویانمایی) موقتاً غیرفعال می‌کند.
@Test
fun testAnimationStateFast() {

composeTestRule.mainClock.autoAdvance = false
    
    while (composeTestRule.hasPendingWork()) {
        composeTestRule.mainClock.advanceTimeByFrame()
        composeTestRule.waitForIdle()
        
        composeTestRule.runOnUiThread {
            composeTestRule.runWithoutImplicitWait {
                // This is most effective when querying multiple nodes in a single frame. 
                // It prevents the redundant synchronization overhead that would 
                // otherwise occur on every individual query.
                val box1 = composeTestRule.onNodeWithTag("Box1").fetchSemanticsNode()
                val box2 = composeTestRule.onNodeWithTag("Box2").fetchSemanticsNode()
                
                assertThat(box1.boundsInRoot.right).isAtMost(box2.boundsInRoot.left)
            }
        }
    }
}

تغییرات قابل‌توجه دیگر:

  • اکنون میانای برنامه‌سازی کاربردی captureToImage به شما امکان می‌دهد بالاپر یا کادر گفتگویی را همراه با لنگر آن در یک نقشه بیتی ضبط کنید.
  • onRootWithViewInteraction اضافه شد تا جستجوهای معنایی Compose به «نماهای Android» خاص محدود شود. این کار آزمایش کردن میاناهای کاربر ترکیبی، مثل RecyclerViews، را بدون نیاز به برچسب‌های آزمایشی منحصربه‌فرد در کد تولید ساده می‌کند.
  • اکنون می‌توان حاشیه‌نویسی‌های @PreviewWrapper را برای کلاس‌های سفارشی @MultiPreview اعمال کرد، که امکان استفاده مجدد از تنظیمات پیش‌نمایش (مانند زمینه‌های سفارشی) را در چندین مؤلفه فراهم می‌کند.

نوشتن خوبی داشته باشید!

Compose 1.12 با گرادیان‌های مش، پشتیبانی از گستره رنگی وسیع، قلم‌های متغیر بارگیری‌شدنی، ادغام Credential Manager، و ابزارهای آزمایش سریع‌تر، توسعه برنامه را آسان‌تر و گویاتر از همیشه می‌کند. مثل همیشه، نظرات شما برای ما ارزشمند است، بنابراین لطفاً بازخورد خود را درباره این تغییرات یا آنچه می‌خواهید در آینده ببینید، در ردیاب مشکل ما به اشتراک بگذارید. از نوشتن لذت ببرید!

نویسنده:
ادامه خواندن