$30 off During Our Annual Pro Sale. View Details »

Jetpack Compose A to Z (full)

Ji Sungbin
February 23, 2023

Jetpack Compose A to Z (full)

Jetpack Compose A to Z 리허설 전 full 자료 (70분)

Ji Sungbin

February 23, 2023
Tweet

More Decks by Ji Sungbin

Other Decks in Programming

Transcript

  1.  $PMVNO3PX🙅  #PY#PY8JUI$POTUSBJOUT🙅  -B[Z-JTU🙅  $POTUSBJOU-BZPVU🙅  -BZPVU🙅

     -PPLBIFBE-BZPVU🥳 BOJNBUJPOGSBNFਸ୶੸ೞӝਤೠۨ੉ইਓ
  2. start fi nish 1st intermediate layout 2nd intermediate layout 3rd

    intermediate layout Lookahead 1. Modi fi er.onPlaced 2. Modi fi er.intermediateLayout
  3. /** * ۨ੉ইਓਸ Ѿ੿ೞӝ ਤ೧ measure ܳ ޷ܻ ೞҊ ୶റ

    placement ױ҅ܳ प೯ೞח ۨ੉ইਓੑפ׮. => measure ܳ ޷ܻ ೞח ױ҅: lookahead ױ҅ * lookahead ױ҅о ՘աݶ ୶റ [LookaheadLayoutScope.intermediateLayout] ਸ ా೧ * lookahead Ѿҗܳ ӝ߈ਵ۽ ۨ੉ইਓ੄ measure ߂ placement ܳ ઑ੿ೡ ࣻ ੓ח ژ ׮ܲ measure ߂ placement ױ҅о द੘ؾפ׮. => intermediate layout * * ੉ܳ ੉ਊೞৈ ۨ੉ইਓ੉ ޷ܻ measure ػ intermediate layout ਸ ೱ೧ ௼ӝ৬ ਤ஖ܳ ੼ର੸ਵ۽ ߸҃ೡ ࣻ ੓णפ׮. */ @ExperimentalComposeUiApi @UiComposable @Composable fun LookaheadLayout( content: @Composable @UiComposable LookaheadLayoutScope.() -> Unit, modifier: Modifier = Modifier, measurePolicy: MeasurePolicy )
  4. /** * [LookaheadLayout] ੄ ݽٚ(૒੽ ߂ р੽) ೞਤ ۨ੉ইਓী ؀ೠ

    receiver ߧਤܳ ઁҕ೤פ׮. * lookahead ױ҅ীࢲ ҅࢑ػ ۨ੉ইਓ੄ measurement ߂ placement ח * [LookaheadLayoutScope] ীࢲ пп [Modifier.intermediateLayout] ߂ [Modifier.onPlaced] ܳ ా೧ observe ೡ ࣻ ੓णפ׮. */ @ExperimentalComposeUiApi interface LookaheadLayoutScope { fun Modifier.onPlaced( onPlaced: ( lookaheadScopeCoordinates: LookaheadLayoutCoordinates, layoutCoordinates: LookaheadLayoutCoordinates ) -> Unit ): Modifier fun Modifier.intermediateLayout( measure: MeasureScope.( measurable: Measurable, constraints: Constraints, lookaheadSize: IntSize ) -> MeasureResult ): Modifier }
  5. @ExperimentalComposeUiApi interface LookaheadLayoutScope { /** * intermediate layout ੉ ߓ஖ؼ

    ਤ஖о ҅࢑ػ റ ഐ୹ؾפ׮. * * [LookaheadLayoutCoordinates] о ઱য૑ݶ ҅࢑ػ intermediate layout ੄ য়೐ࣇҗ അ੤ ߓ஖ظ ੓ח ஹನ੷࠶੄ য়೐ࣇਸ * [LookaheadLayoutCoordinates.localLookaheadPositionOf] ৬ [LookaheadLayoutCoordinates.localPositionOf] ܳ ࢎਊೞৈ ঳ਸ ࣻ ੓णפ׮. * ੉ܳ ా೧ ҅࢑ػ intermediate layout ੄ য়೐ࣇਸ ӝ߈ਵ۽ ஹನ੷࠶੄ ߓ஖ܳ ઑ੿ೡ ࣻ ੓णפ׮. * * [onPlaced ۈ׮ ੋ੗] * * @param lookaheadScopeCoordinates [LookaheadLayout] ੉ ࢎਊೞח [LookaheadLayoutCoordinates] * @param layoutCoordinates ੉ modifier ੄ ஹನ੷࠶੉ ࢎਊೞח [LookaheadLayoutCoordinates] */ fun Modifier.onPlaced( onPlaced: ( lookaheadScopeCoordinates: LookaheadLayoutCoordinates, layoutCoordinates: LookaheadLayoutCoordinates ) -> Unit ): Modifier fun Modifier.intermediateLayout( measure: MeasureScope.( measurable: Measurable, constraints: Constraints, lookaheadSize: IntSize
  6. start fi nish Lookahead 1st intermediate layout 2nd intermediate layout

    will be 3rd intermediate layout OEJOUFSNFEJBUFMBZPVUীࢲप೯غח࢚ട JOUFSNFEJBUFMBZPVU੉ߓ஖ؼਤ஖ܳ҅࢑ೣ ੉റ೧׼ਤ஖ܳModifier.onPlaced۽ࠁն
  7. @ExperimentalComposeUiApi interface LookaheadLayoutScope { /** * intermediate layout ੉ ߓ஖ؼ

    ਤ஖о ҅࢑ػ റ ഐ୹ؾפ׮. * * [LookaheadLayoutCoordinates] о ઱য૑ݶ ҅࢑ػ intermediate layout ੄ য়೐ࣇҗ അ੤ ߓ஖ظ ੓ח ஹನ੷࠶੄ য়೐ࣇਸ * [LookaheadLayoutCoordinates.localLookaheadPositionOf] ৬ [LookaheadLayoutCoordinates.localPositionOf] ܳ ࢎਊೞৈ ঳ਸ ࣻ ੓णפ׮. * ੉ܳ ా೧ ҅࢑ػ intermediate layout ੄ য়೐ࣇਸ ӝ߈ਵ۽ ஹನ੷࠶੄ ߓ஖ܳ ઑ੿ೡ ࣻ ੓णפ׮. * * [onPlaced ۈ׮ ੋ੗] * * @param lookaheadScopeCoordinates [LookaheadLayout] ੉ ࢎਊೞח [LookaheadLayoutCoordinates] * @param layoutCoordinates ੉ modifier ੄ ஹನ੷࠶੉ ࢎਊೞח [LookaheadLayoutCoordinates] */ fun Modifier.onPlaced( onPlaced: ( lookaheadScopeCoordinates: LookaheadLayoutCoordinates, layoutCoordinates: LookaheadLayoutCoordinates ) -> Unit ): Modifier fun Modifier.intermediateLayout( measure: MeasureScope.( measurable: Measurable, constraints: Constraints, lookaheadSize: IntSize
  8. /** * ۨ੉ইਓী ؀೧ measure ػ bounds ੄ ഓ؊ੑפ׮. */

    @JvmDefaultWithCompatibility interface LayoutCoordinates { // … ࢤۚ /** * [sourceCoordinates] ҕр੄ [relativeToSource] ܳ ۽ஸ coordinate ۽ ߸ജ೤פ׮. * [sourceCoordinates] ח زੌೠ ۨ੉ইਓ ҅கী ࣘೞח ݽٚ [LayoutCoordinates] ੌ ࣻ ੓णפ׮. * * @param sourceCoordinates ߸ജೡ [Offset] ੉ ੓ח [LayoutCoordinates] * @param relativeToSource ߸ഝೡ [Offset] * * @return ۽ஸ coordinate ۽ ߸ജػ [Offset] */ fun localPositionOf(sourceCoordinates: LayoutCoordinates, relativeToSource: Offset): Offset }
  9. /** * lookhead ױ҅ ૓೯ ੹җ റ੄ ۨ੉ইਓ ݽف੄ [LayoutCoordinates]

    ܳ ࠁਬ೤פ׮. */ @ExperimentalComposeUiApi sealed interface LookaheadLayoutCoordinates : LayoutCoordinates { /** * [sourceCoordinates] ҕр੄ [relativeToSource] ܳ ۽ஸ coordinate ۽ ߸ജ೤פ׮. * [sourceCoordinates] ח زੌೠ ۨ੉ইਓ ҅கী ࣘೞח ݽٚ [LookaheadLayoutCoordinates] ੌ ࣻ ੓णפ׮. * * [localPositionOf] ৬ ׳ܻ [localLookaheadPositionOf] ח coordinate ҅࢑ਸ ਤ೧ lookahead ਤ஖ܳ ࢎਊ೤פ׮. * * @param sourceCoordinates ߸ജೡ [Offset] ੉ ੓ח [LookaheadLayoutCoordinates] * @param relativeToSource ߸ഝೡ [Offset] * * @return ۽ஸ coordinate ۽ ߸ജػ [Offset] */ fun localLookaheadPositionOf( sourceCoordinates: LookaheadLayoutCoordinates, relativeToSource: Offset = Offset.Zero ): Offset }
  10. // interface LookaheadLayoutScope /** * lookahead ױ҅ীࢲ ҅࢑ػ ੿ࠁܳ ӝ߈ਵ۽

    intermediate layout ܳ ߓ஖೤פ׮. * intermediate layout ੄ ௼ӝо ઁҕغח ۈ׮ੋ [measure] ੋ੗ܳ ా೧ intermediate layout ܳ morph ೡ ࣻ ੓णפ׮. * * morph: അ੤ ݽনਸ ׮ܲ ݽনਵ۽ ߄Բח Ѫ * * [measure ۈ׮ ੋ੗] * * @param measurable intermediate layout ੄ measurable * @param constraints intermediate layout ੄ constraints * @param lookaheadSize intermediate layout ੄ ௼ӝ */ fun Modifier.intermediateLayout( measure: MeasureScope.( measurable: Measurable, constraints: Constraints, lookaheadSize: IntSize ) -> MeasureResult ): Modifier }
  11. start fi nish Lookahead 1st intermediate layout 2nd intermediate layout

    will be 3rd intermediate layout OEJOUFSNFEJBUFMBZPVUীࢲप೯غח࢚ട Modifier.intermediateLayoutਵ۽ߓ஖ೡҔ LookaheadLayoutCoordinates.localLookaheadPositionOf੄য়೐ࣇ LookaheadLayoutCoordinates.localPositionOf੄য়೐ࣇ
  12. ௼ӝઑ੿.PEJGJFSJNUFSNFEJBUF-BZPVU /** * lookahead ױ҅ীࢲ ҅࢑ػ ੿ࠁܳ ӝ߈ਵ۽ intermediate layout

    ܳ ߓ஖೤פ׮. * intermediate layout ੄ ௼ӝо ઁҕغח ۈ׮ੋ [measure] ੋ੗ܳ ా೧ intermediate layout ܳ morph ೡ ࣻ ੓णפ׮. */ fun Modifier.intermediateLayout( measure: MeasureScope.( measurable: Measurable, constraints: Constraints, lookaheadSize: IntSize ) -> MeasureResult ): Modifier
  13. য়೐ࣇઑ੿.PEJGJFSPO1MBDFE /** * [LookaheadLayoutCoordinates] о ઱য૑ݶ ҅࢑ػ intermediate layout ੄

    য়೐ࣇҗ അ੤ ߓ஖ظ ੓ח ஹನ੷࠶੄ য়೐ࣇਸ * [LookaheadLayoutCoordinates.localLookaheadPositionOf] ৬ [LookaheadLayoutCoordinates.localPositionOf] ܳ ࢎਊೞৈ ঳ਸ ࣻ ੓णפ׮. * ੉ܳ ా೧ ҅࢑ػ intermediate layout ੄ য়೐ࣇਸ ӝ߈ਵ۽ ஹನ੷࠶੄ ߓ஖ܳ ઑ੿ೡ ࣻ ੓णפ׮. */ fun Modifier.onPlaced( onPlaced: ( lookaheadScopeCoordinates: LookaheadLayoutCoordinates, layoutCoordinates: LookaheadLayoutCoordinates ) -> Unit ): Modifier
  14. fun Modifier.movement(lookaheadScope: LookaheadLayoutScope) = composed { var targetOffset: IntOffset? by

    remember { mutableStateOf(null) } // ߓ஖ೡ য়೐ࣇ var placementOffset by remember { mutableStateOf(IntOffset.Zero) } // അ੤ য়೐ࣇ with(lookaheadScope) { this@composed .onPlaced { lookaheadScopeCoordinates, layoutCoordinates -> // LookaheadLayout ੄ ۽ஸ coordinates ীࢲ ੉ modifier ੄ lookahead ਤ஖ܳ ߈ജ targetOffset = lookaheadScopeCoordinates .localLookaheadPositionOf(sourceCoordinates = layoutCoordinates) .round() // о੢ оө਍ IntOffset чਵ۽ য়೐ࣇ ߈ৢܿ // LookaheadLayout ੄ ۽ஸ coordinates ীࢲ ੉ modifier ੄ അ੤ ਤ஖ܳ ߈ജ placementOffset = lookaheadScopeCoordinates .localPositionOf( sourceCoordinates = layoutCoordinates, relativeToSource = Offset.Zero ) .round() } .intermediateLayout { measurable, constraints, _ -> val placeable = measurable.measure(constraints) layout(width = placeable.width, height = placeable.height) { // ੉زೠ য়೐ࣇী ߓ஖
  15. targetOffset = lookaheadScopeCoordinates .localLookaheadPositionOf(sourceCoordinates = layoutCoordinates) .round() // о੢ оө਍

    IntOffset чਵ۽ য়೐ࣇ ߈ৢܿ // LookaheadLayout ੄ ۽ஸ coordinate ীࢲ ੉ modifier ੄ അ੤ ਤ஖ܳ ߈ജ placementOffset = lookaheadScopeCoordinates .localPositionOf( sourceCoordinates = layoutCoordinates, relativeToSource = Offset.Zero ) .round() } .intermediateLayout { measurable, constraints, _ -> val placeable = measurable.measure(constraints) layout(width = placeable.width, height = placeable.height) { // ੉زೠ য়೐ࣇী ߓ஖ val (x, y) = targetOffset!! - placementOffset placeable.place(x = x, y = y) } } } }
  16. fun Modifier.transformation(lookaheadScope: LookaheadLayoutScope) = with(lookaheadScope) { intermediateLayout { measurable, _,

    lookaheadSize -> val (width, height) = lookaheadSize // lookahead ௼ӝ۽ width, height Ѿ੿ val animatedConstraints = Constraints.fixed( width = width.coerceAtLeast(0), // ୭ࣗ 0 ਵ۽ ࢸ੿ height = height.coerceAtLeast(0) ) val placeable = measurable.measure(animatedConstraints) layout(width = placeable.width, height = placeable.height) { // lookahead ௼ӝী ݏѱ ߓ஖ placeable.place(x = 0, y = 0) } } }
  17. LookaheadLayout( modifier = modifier .fillMaxSize() .navigationBarsPadding() .padding(16.dp), content = {

    Fab( modifier = Modifier .size( size = FabDefaults.size( isExpanded = isExpanded, maxWidthDp = screenMaxWidth.dp ) ) .movement(lookaheadScope = this) .transformation(lookaheadScope = this) .noRippleClickable { isExpanded = !isExpanded }, isExpanded = isExpanded ) }, measurePolicy = DefaultMeasurePolicy )
  18. Column( modifier = Modifier.fillMaxSize().background(color = Color.BackgroundWhite), verticalArrangement = Arrangement.SpaceBetween )

    { TabContainer { TabDefaults.Items.forEach { tab -> TabItem( title = tab.shortname, backgroundColor = tabBackgroundColor(selectedTab = selectedTabState, nowTab = tab), textColor = tabTextColor(selectedTab = selectedTabState, nowTab = tab), onTabClick = { selectedTabState = tab } ) } } MovieContainer { MovieName(fullname = selectedTabState.fullname) MoviePoster(posterDrawable = selectedTabState.poster) } }
  19. @Stable private fun tabBackgroundColor( selectedTab: Int, nowTab: Int ): Color

    = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedBackground false -> TabDefaults.Color.defaultBackground } @Stable private fun tabTextColor( selectedTab: Int, nowTab: Int ): Color = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedText false -> TabDefaults.Color.defaultText } @Composable private fun tabBackgroundColorWithAnimation( selectedTab: Tab, nowTab: Tab ): Color = animateColorAsState( targetValue = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedBackground false -> TabDefaults.Color.defaultBackground } ).value @Composable private fun tabTextColorWithAnimation( selectedTab: Tab, nowTab: Tab ): Color = animateColorAsState( targetValue = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedText false -> TabDefaults.Color.defaultText } ).value
  20. /** * [Color] ী ؀ೠ Fire-and-Forget গפݫ੉࣌ ӝמਸ ઁҕ೤פ׮. *

    animate*AsState ח [Float], [Color], [Dp], [Size], [Offset], [Rect], [Int], [IntOffset] ӒܻҊ [IntSize] ী ࢎ੹ ੿੄ظ ੓णפ׮. * ઁҕػ [targetValue] о ߸҃غݶ গפݫ੉࣌੉ ੗زਵ۽ प೯ؾפ׮. * [targetValue] о ߸҃ؼ ٸ ૓೯ ઺ੋ গפݫ੉࣌੉ ੉޷ ੓ח ҃਋ ૓೯ ઺ੋ গפݫ੉࣌਷ ࢜۽਍ [targetValue] ܳ ೱ೧ গפݫ੉࣌غب۾ ௏झܳ ઑ੿೤פ׮. * * animate*AsState ח গפݫ੉࣌੉ ੸ਊغҊ ੓ח [State] ܳ ߈ജ೤פ׮. * * @param targetValue গפݫ੉࣌੄ ఋѶ * @param animationSpec दрী ٮۄ чਸ ߸҃ೞח ؘ ࢎਊೡ গפݫ੉࣌ * @param finishedListener গפݫ੉࣌੉ ৮ܐؼ ٸ ঌܿਸ ߉ח ࢶఖ੸ ܻझց */ @Composable fun animateColorAsState( targetValue: Color, animationSpec: AnimationSpec<Color> = colorDefaultSpring, finishedListener: ((Color) -> Unit)? = null ): State<Color>
  21. /** * द੘ чҗ ՘ ч ࢎ੉ী ޛܻ೟ ӝ߈ গפݫ੉࣌ਸ

    ੸ਊ೤פ׮. * * @param DampingRatio х࣭࠺ (఍ࢿ) * @param stiffness ъࢿ (ઙܐ чਵ۽ ੉زೞח ࣘب) * @param visibleThreshold оदࢿ ੐҅ч * গפݫ੉࣌੉ ؀࢚ী ߈ৢܿೞӝী ୽࠙൤ दп੸ਵ۽ оө਍ Ѫਵ۽ р઱غযঠ ೞח दӝܳ ੿੄೤פ׮. * * @return ઱য૓ ২࣌ਸ ࢎਊೞח [SpringSpec] */ @Stable fun <T> spring( dampingRatio: Float = Spring.DampingRatioNoBouncy, stiffness: Float = Spring.StiffnessMedium, visibilityThreshold: T? = null ): SpringSpec<T> = SpringSpec( dampingRatio = dampingRatio, stiffness = stiffness, visibilityThreshold = visibilityThreshold )
  22. /** * ੉૚ ҋࢶਸ ࢎਊೞৈ ૑੿ػ [durationMillis] زউ द੘ чҗ

    ՘ ч рী গפݫ੉࣌ਸ ੸ਊ೤פ׮. * * @param durationMillis গפݫ੉࣌ ૑ࣘ दр (޻ܻୡ) * @param delayMillis গפݫ੉࣌੉ द੘غӝ ੹ী ؀ӝೞח दр (޻ܻୡ) * @param easing द੘җ ՘ ࢎ੉ܳ ࠁрೞח ؘ ࢎਊغח ੉૚ ҋࢶ * * @return ઱য૓ ২࣌ਸ ࢎਊೞח [TweenSpec] */ @Stable fun <T> tween( durationMillis: Int = AnimationConstants.DefaultDurationMillis, delayMillis: Int = 0, easing: Easing = FastOutSlowInEasing ): TweenSpec<T> = TweenSpec( durationMillis = durationMillis, delay = delayMillis, easing = easing )
  23. /** * গפݫ੉࣌੄ ࠙ࣻ(fraction)ܳ ઑ੿ೞח ߑߨੑפ׮. * ੉૚ਸ ࢎਊೞݶ গפݫ੉࣌੉

    ੌ੿ೠ ࣘب۽ ૓೯غח ؀न ࣘبܳ ֫੉Ѣա ծ୹ ࣻ ੓णפ׮. * * ࠙ࣻח গפݫ੉࣌੄ അ੤ ૑੼ਸ աఋղח 0 ীࢲ 1.0 ࢎ੉੄ чੑפ׮. ৈӝࢲ 0 ਷ द੘ਸ աఋղҊ 1.0 ਷ ՘ਸ աఋշפ׮. */ @Stable fun interface Easing { fun transform(fraction: Float): Float }
  24. // ࡅܰѱ ࣘبܳ ֫੉Ҋ ੼ର੸ਵ۽ ו۰૘פ׮. val FastOutSlowInEasing: Easing =

    CubicBezierEasing(0.4f, 0.0f, 0.2f, 1.0f) // ٜযয়ח ਃࣗח ઁੌ ࡅܲ ࣘبীࢲ ੼੼ ו۰૘פ׮. val LinearOutSlowInEasing: Easing = CubicBezierEasing(0.0f, 0.0f, 0.2f, 1.0f) // աоח ਃࣗח ઁੌ וܽ ࣘبীࢲ ੼੼ ࡈۄ૘פ׮. val FastOutLinearInEasing: Easing = CubicBezierEasing(0.4f, 0.0f, 1.0f, 1.0f) // ࣻ੿غ૑ ঋ਷ ࠙ࣻܳ ߈ജ೤פ׮. ੉૚੉ ೙ਃೞ૑݅ ࣻ੿ػ ੉૚੉ ೙ਃೞ૑ ঋ਷ ҃਋ ӝࠄчਵ۽ ਬਊ೤פ׮. val LinearEasing: Easing = Easing { fraction -> fraction } // 3ର ߬૑য ҋࢶਸ ҳഅ೤פ׮. @Immutable class CubicBezierEasing( private val a: Float, private val b: Float, private val c: Float, private val d: Float ) : Easing
  25. /** * গפݫ੉࣌ ӝрী ৈ۞ ఋ੐झఙ೐ীࢲ ૑੿ػ झշࢫ чਸ ӝ߈ਵ۽

    গפݫ੉࣌ਸ ୊ܻ೤פ׮. * ঱ઁա গפݫ੉࣌ ч਷ ف ః೐ۨ੐ ч ࢎ੉ী ࠁрؾפ׮. * ః೐ۨ੐݃׮ ࢎਊೡ [Easing] ਸ ૑੿ೡ ࣻ ੓णפ׮. */ @Stable fun <T> keyframes( init: KeyframesSpec.KeyframesSpecConfig<T>.() -> Unit ): KeyframesSpec<T> = KeyframesSpec( config = KeyframesSpec.KeyframesSpecConfig<T>().apply(init) ) @Composable fun KeyframesExample(target: Int) { val value by animateIntAsState( targetValue = target, // 0 ~ 100 animationSpec = keyframes { durationMillis = 1000 // 1000 ms زউ গפݫ੉࣌ ૓೯ 100 at 50 with LinearEasing // LinearEasing ਸ ࢎਊೞݴ 100 ms উী 50 ө૑ গפݫ੉࣌ 500 at 80 with FastOutSlowInEasing // FastOutSlowInEasing ਸ ࢎਊೞݴ 500 ms উী 80 ө૑ গפݫ੉࣌ // ੉റ 501 ~ 1000 ms زউ ӝઓী ࢸ੿ೠ ੉૚ੋ FastOutSlowInEasing ܳ ҅ࣘ ࢎਊೞৈ 100 ө૑ গפݫ੉࣌ } ) }
  26. /** * ૑੿ػ ߈ࠂ പࣻী ب׳ೡ ٸө૑ ӝр ӝ߈ গפݫ੉࣌(৘:

    [tween] ژח [keyframes])ਸ ߈ࠂ੸ਵ۽ प೯೤פ׮. * * @param iterations ߈ࠂೡ പࣻ * @param animation ߈ࠂೡ গפݫ੉࣌ * @param repeatMode ߈ࠂೡ ݽ٘ - [RepeatMode.Restart] ژח [RepeatMode.Reverse] * @param initialStartOffset গפݫ੉࣌ਸ द੘ೡ য়೐ࣇ */ @Stable fun <T> repeatable( iterations: Int, animation: DurationBasedAnimationSpec<T>, repeatMode: RepeatMode = RepeatMode.Restart, initialStartOffset: StartOffset = StartOffset(0) ): RepeatableSpec<T> = RepeatableSpec( iterations = iterations, animation = animation, repeatMode = repeatMode, initialStartOffset = initialStartOffset )
  27. /** * [repeatable] җ زੌೞҊ, ਬੌೠ ର੉੼਷ ޖೠ ߈ࠂ੉ۄ iterations

    ੋ੗о হणפ׮. */ @Stable fun <T> infiniteRepeatable( animation: DurationBasedAnimationSpec<T>, repeatMode: RepeatMode = RepeatMode.Restart, initialStartOffset: StartOffset = StartOffset(0) ): InfiniteRepeatableSpec<T> = InfiniteRepeatableSpec( animation = animation, repeatMode = repeatMode, initialStartOffset = initialStartOffset )
  28. /** * snap ਷ чਸ ૊द ઙܐ чਵ۽ ߸ജೞח ౠࣻ

    ݾ੸੄ [AnimationSpec] ੑפ׮. * * @param delayMillis গפݫ੉࣌ द੘ਸ ૑োೡ दр (޻ܻୡ) */ @Stable fun <T> snap(delayMillis: Int = 0): SnapSpec<T> = SnapSpec( delay = delayMillis )
  29. MovieContainer { MovieName(fullname = selectedTabState.fullname) MoviePoster(posterDrawable = selectedTabState.poster) } MovieContainer

    { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp), targetState = selectedTabState ) { tab -> MovieName(fullname = tab.fullname) } AnimatedContent( modifier = Modifier.wrapContentSize(), targetState = selectedTabState ) { tab -> MoviePoster(posterDrawable = tab.poster) } }
  30. /** * [targetState] о ߸҃ؼ ٸ [content] ী ੗زਵ۽ গפݫ੉࣌ਸ

    ੸ਊೞח ஶప੉ցੑפ׮. * * @param targetState ߸҃ؼ ࢚క * @param modifier ੸ਊೡ [Modifier] * @param transitionSpec ੸ਊೡ গפݫ੉࣌ * @param contentAlignment [content] о ߓ஖ؼ [Alignment] * @param content ߓ஖ೡ ஹನ੷࠶ */ @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { /* … */ }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  31. @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier =

    Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn( animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) + scaleIn( initialScale = 0.92f, animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) with fadeOut( animationSpec = tween(durationMillis = 90) ) }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  32. /** * ஹನ੷࠶੉ [AnimatedContent] ী ٜযоҊ աоח ߑߨਸ ੿੄೤פ׮. *

    * @param targetContentEnter ࢜۽਍ ஹನ੷࠶੉ ٜযয়ח গפݫ੉࣌ * @param initialContentExit ӝઓ ஹನ੷࠶੉ աоח গפݫ੉࣌ * @param targetContentZIndex ஹನ੷࠶੉ ٜযয়Ҋ աт ٸ ࢎਊೡ zIndex * @param sizeTransform ஹನ੷࠶੉ ٜযয়Ҋ աт ٸ ࢎ੉ૉо ߸ೡ ҃਋ ੉ܳ ҙܻೞӝ ਤೠ ২࣌ */ @ExperimentalAnimationApi class ContentTransform( val targetContentEnter: EnterTransition, val initialContentExit: ExitTransition, targetContentZIndex: Float = 0f, sizeTransform: SizeTransform? = SizeTransform() ) { var targetContentZIndex by mutableStateOf(targetContentZIndex) var sizeTransform: SizeTransform? = sizeTransform internal set }
  33. @ExperimentalAnimationApi class ContentTransform( val targetContentEnter: EnterTransition, val initialContentExit: ExitTransition, targetContentZIndex:

    Float = 0f, sizeTransform: SizeTransform? = SizeTransform() ) { /** * ஶప੉ցী ٜযт ٸ ࢜۽਍ ؀࢚ ஹನ੷࠶੄ zIndex ܳ ੄޷೤פ׮. * ӝࠄч਷ 0f ੑפ׮. zIndex о ֫਷ ஹನ੷࠶਷ zIndex о ծ਷ ஹನ੷࠶ ਤী Ӓ۰૘פ׮. * ੋؙझо э਷ ஹನ੷࠶਷ ఋѶ ஹನ੷࠶੉ ݔ ਤী ߓ஖ؾפ׮. */ var targetContentZIndex by mutableStateOf(targetContentZIndex) /** * ࢜ ஹನ੷࠶੉ AnimatedContent ী ٜযоҊ ੉੹ ஹನ੷࠶੉ աт ٸ ௼ӝо ߸҃غח ҃਋ ஶప੉ց੄ ഛ੢ ߂ ୷ࣗܳ ҙܻ೤פ׮. * ӝࠄ੸ਵ۽ [spring][SpringSpec] ਷ ݽٚ ௼ӝ ߸҃ਸ গפݫ੉࣌ೞח ؘ ࢎਊغݴ [AnimatedContent] ח গפݫ੉࣌ػ ௼ӝ۽ ੜ݀פ׮. * ࢎਊؼ [SizeTransform] ܳ ૒੽ ࢸ੿ೡ ࣻ ੓णפ׮. ௼ӝ গפݫ੉࣌੉ ೙ਃೞ૑ ঋਵݶ [sizeTransform] ਸ null ۽ ࢸ੿ೞࣁਃ. */ var sizeTransform: SizeTransform? = sizeTransform internal set }
  34. /** * ஹನ੷࠶੉ ಴दؼ ٸ ૓೯غח গפݫ੉࣌ਸ ੿੄೤פ׮. * ࢎਊೡ

    ࣻ ੓ח [EnterTransition] ੄ 4о૑ ஠పҊܻח ׮਺җ эणפ׮. * * 1. fade: [fadeIn] * 2. scale: [scaleIn] * 3. slide: [slideIn], [slideInHorizontally], [slideInVertically] * 4. expand: [expandIn], [expandHorizontally], [expandVertically] */ @Immutable sealed class EnterTransition { internal abstract val data: TransitionData @Stable operator fun plus(enter: EnterTransition): EnterTransition { // … } }
  35. @Immutable sealed class EnterTransition { internal abstract val data: TransitionData

    // ղࠗীࢲ ࢎਊೞח ੿ࠁܳ ࠁҙೣ /** * ׮ܲ [EnterTransition] ਸ Ѿ೤೤פ׮. [EnterTransition] ח زदী द੘غ޲۽ Ѿ೤غח ࣽࢲח ઺ਃೞ૑ ঋणפ׮. * গפݫ੉࣌ਸ ੸ਊೞח ࣽࢲח ঌ౵ ߂ ߓਯਸ ݢ੷ ઑ੿ೞҊ ୷ࣗ ژח ഛ؀ೠ ׮਺ ठۄ੉٘೤פ׮. * * @param enter Ѿ೤ೡ ׮ܲ [EnterTransition] * * @return 2ѐ੄ [EnterTransition] ੉ Ѿ೤ػ ࢜۽਍ [EnterTransition] */ @Stable operator fun plus(enter: EnterTransition): EnterTransition { return EnterTransitionImpl( TransitionData( fade = data.fade ?: enter.data.fade, slide = data.slide ?: enter.data.slide, changeSize = data.changeSize ?: enter.data.changeSize, scale = data.scale ?: enter.data.scale ) ) } }
  36. /** * ஹನ੷࠶੉ ࢎۄ૕ ٸ ૓೯غח গפݫ੉࣌ਸ ੿੄೤פ׮. * ࢎਊೡ

    ࣻ ੓ח [ExitTransition] ੄ 4о૑ ஠పҊܻח ׮਺җ эणפ׮. * * 1. fade: [fadeOut] * 2. scale: [scaleOut] * 3. slide: [slideOut], [slideOutHorizontally], [slideOutVertically] * 4. shrink: [shrinkOut], [shrinkHorizontally], [shrinkVertically] */ @Immutable sealed class ExitTransition { internal abstract val data: TransitionData @Stable operator fun plus(exit: ExitTransition): ExitTransition { // … } }
  37. @Immutable sealed class ExitTransition { internal abstract val data: TransitionData

    // ղࠗীࢲ ࢎਊೞח ੿ࠁܳ ࠁҙೣ /** * ׮ܲ [ExitTransition] ਸ Ѿ೤೤פ׮. [ExitTransition] ח زदী द੘غ޲۽ Ѿ೤غח ࣽࢲח ઺ਃೞ૑ ঋणפ׮. * গפݫ੉࣌ਸ ੸ਊೞח ࣽࢲח ঌ౵ ߂ ߓਯਸ ݢ੷ ઑ੿ೞҊ ୷ࣗ ژח ഛ؀ೠ ׮਺ ठۄ੉٘೤פ׮. * * @param exit Ѿ೤ೡ ׮ܲ [ExitTransition] * * @return 2ѐ੄ [ExitTransition] ੉ Ѿ೤ػ ࢜۽਍ [ExitTransition] */ @Stable operator fun plus(exit: ExitTransition): ExitTransition { return ExitTransitionImpl( TransitionData( fade = data.fade ?: exit.data.fade, slide = data.slide ?: exit.data.slide, changeSize = data.changeSize ?: exit.data.changeSize, scale = data.scale ?: exit.data.scale ) ) } }
  38. @ExperimentalAnimationApi class ContentTransform( val targetContentEnter: EnterTransition, val initialContentExit: ExitTransition, targetContentZIndex:

    Float = 0f, sizeTransform: SizeTransform? = SizeTransform() ) { var targetContentZIndex by mutableStateOf(targetContentZIndex) var sizeTransform: SizeTransform? = sizeTransform internal set }
  39. /** * ஹನ੷࠶੄ ௼ӝо ߸҃ؼ ٸ ೠ ௼ӝীࢲ ׮ܲ ௼ӝ۽

    ߸ജೞח ߑߨਸ ੿੄೤פ׮. */ @ExperimentalAnimationApi interface SizeTransform { /** * ࢎ੉ૉ ઑ੺ গפݫ੉࣌ীࢲ ஹನ੷࠶੄ ҃҅ী ݏѱ clip ೧ঠ ೞח૑ ৈࠗੑפ׮. */ val clip: Boolean /** * গפݫ੉࣌ ੸ਊ ੹ ࢎ੉ૉੋ [initialSize] ৬ গפݫ੉࣌ ੸ਊ റ ࢎ੉ૉੋ [targetSize] ܳ ӝ߈ਵ۽ [AnimationSpec] ਸ ٜ݅ ࣻ ੓णפ׮. */ fun createAnimationSpec(initialSize: IntSize, targetSize: IntSize): FiniteAnimationSpec<IntSize> }
  40. @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier =

    Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn( animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) + scaleIn( initialScale = 0.92f, animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) with fadeOut( animationSpec = tween(durationMillis = 90) ) }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  41. /** * ઁҕػ [this][EnterTransition] ߂ [exit] ܳ ࢎਊೞৈ [ContentTransform] ਸ

    ࢤࢿೞݴ, * [EnterTransition] ߂ [ExitTransition] গפݫ੉࣌੉ زदী प೯ؾפ׮. * * @param exit [this][EnterTransition] ৬ ೤ச [ExitTransition] * * @return [this][EnterTransition] ৬ [ExitTransition] ੉ ೤୛૓ [ContentTransform] */ @ExperimentalAnimationApi infix fun EnterTransition.with(exit: ExitTransition) = ContentTransform( targetContentEnter = this, initialContentExit = exit )
  42. @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier =

    Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { /* … */ }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  43. /** * [AnimatedContent] ীࢲ݅ ಞܻೞѱ ੸ਊೡ ࣻ ੓ח ӝמਸ ઁҕ೤פ׮.

    */ @ExperimentalAnimationApi class AnimatedContentScope<S> internal constructor( internal val transition: Transition<S>, internal var contentAlignment: Alignment, internal var layoutDirection: LayoutDirection ) : Transition.Segment<S> { override val initialState: S get() = transition.segment.initialState // গפݫ੉࣌੉ द੘غӝ ੹ ୡӝ ч override val targetState: S get() = transition.segment.targetState // গפݫ੉࣌੉ ੸ਊؼ ч, ૊ গפݫ੉࣌੄ ઙܐ ч /** * അ੤ [this][ContentTransform] ੄ [sizeTransform] ܳ ੋ੗۽ ߉਷ [sizeTransform] ۽ ࢸ੿೤פ׮. * * @param sizeTransform ࢜۽ ੸ਊೡ [SizeTransform] * * @return [sizeTransform] ਸ ࢜۽ ੸ਊೠ [ContentTransform] */ @ExperimentalAnimationApi infix fun ContentTransform.using(sizeTransform: SizeTransform?): ContentTransform = apply { this.sizeTransform = sizeTransform } // …
  44. /** * ஶప੉ց੄ о੢੗ܻীࢲ [AnimatedContent] ী ౠ੿ೠ ࣻಣ/ࣻ૒ slide-in ਸ

    ੿੄೤פ׮. * [slideInHorizontally] ߂ [slideInVertically] ৬ ׳ܻ द੘ য়೐ࣇ੉ [AnimatedContent] ੄ * അ੤ ௼ӝ৬ ੿۳ ২࣌ਸ ӝ߈ਵ۽ ੗ز ҅࢑ؾפ׮. * * @param towards ठۄ੉٘ ߑೱ * ஹನ੷࠶਷ח [SlideDirection.Left], [SlideDirection.Right], [SlideDirection.Up] ߂ [SlideDirection.Down] * ߑೱਵ۽ ஶప੉ցܳ ೱ೧ slide ೡ ࣻ ੓णפ׮. * @param animationSpec ࢎਊೡ গפݫ੉࣌ * @param initialOffset द੘ য়೐ࣇ. ੗زਵ۽ ҅࢑غ૑݅ ৉द ࣻزਵ۽ ૑੿ೡ ࣻب ੓णפ׮. */ fun slideIntoContainer( towards: AnimatedContentScope.SlideDirection, animationSpec: FiniteAnimationSpec<IntOffset> = spring(visibilityThreshold = IntOffset.VisibilityThreshold), initialOffset: (offsetForFullSlide: Int) -> Int = { it } ): EnterTransition // slideIntoContainer ৬ زੌ, ױ slide-in ؀न slide-out fun slideOutOfContainer( towards: AnimatedContentScope.SlideDirection, animationSpec: FiniteAnimationSpec<IntOffset> = spring(visibilityThreshold = IntOffset.VisibilityThreshold), targetOffset: (offsetForFullSlide: Int) -> Int = { it } ): ExitTransition }
  45. @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier =

    Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { /* … */ }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  46. @Composable private fun tabBackgroundColorWithAnimation( selectedTab: Tab, nowTab: Tab ): Color

    = animateColorAsState( targetValue = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedBackground false -> TabDefaults.Color.defaultBackground } ).value @Composable private fun tabTextColorWithAnimation( selectedTab: Tab, nowTab: Tab ): Color = animateColorAsState( targetValue = when (selectedTab == nowTab) { true -> TabDefaults.Color.selectedText false -> TabDefaults.Color.defaultText } ).value MovieContainer { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp), targetState = selectedTabState ) { tab -> MovieName(fullname = tab.fullname) } AnimatedContent( modifier = Modifier.wrapContentSize(), targetState = selectedTabState, contentAlignment = Alignment.Center ) { tab -> MoviePoster(posterDrawable = tab.poster) } }
  47. @ExperimentalAnimationApi @Composable fun <S> AnimatedContent( targetState: S, modifier: Modifier =

    Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { fadeIn( animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) + scaleIn( initialScale = 0.92f, animationSpec = tween( durationMillis = 220, delayMillis = 90 ) ) with fadeOut( animationSpec = tween(durationMillis = 90) ) }, contentAlignment: Alignment = Alignment.TopStart, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  48. MovieContainer { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp),

    targetState = selectedTabState ) { tab -> MovieName(fullname = tab.fullname) } AnimatedContent( modifier = Modifier.wrapContentSize(), targetState = selectedTabState, contentAlignment = Alignment.Center ) { tab -> MoviePoster(posterDrawable = tab.poster) } } MovieContainer { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp), targetState = selectedTabState, transitionSpec = { fadeIn() with fadeOut() using SizeTransform(clip = false) } ) { tab -> MovieName(fullname = tab.fullname) } // … }
  49. MovieContainer { // … AnimatedContent( modifier = Modifier.wrapContentSize(), targetState =

    selectedTabState, contentAlignment = Alignment.Center, transitionSpec = { val targetIndex = targetState.index // targetState == Tab val initialIndex = initialState.index // initialState == Tab if (targetIndex > initialIndex) { // ׮਺ చ slideIntoContainer( towards = AnimatedContentScope.SlideDirection.Start ) with fadeOut() using SizeTransform(clip = false) } else { // ੉੹ చ fadeIn() with slideOutOfContainer( towards = AnimatedContentScope.SlideDirection.End ) using SizeTransform(clip = false) }.apply { targetContentZIndex = targetIndex.toFloat() } } ) { tab -> MoviePoster(posterDrawable = tab.poster) } } MovieContainer { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp), targetState = selectedTabState ) { tab -> MovieName(fullname = tab.fullname) } AnimatedContent( modifier = Modifier.wrapContentSize(), targetState = selectedTabState, contentAlignment = Alignment.Center ) { tab -> MoviePoster(posterDrawable = tab.poster) } }
  50. /** * ࢚క ࣻળীࢲ ݽٚ গפݫ੉࣌ਸ ҙܻ೤פ׮. * গפݫ੉࣌਷ [Transition.animateFloat],

    [Transition.animateColor], [Transition.animateValue] ١ਸ * ࢎਊೞৈ ࢶ঱੸ੋ ߑधਵ۽ ٜ݅ ࣻ ੓णפ׮. * * @param initialState গפݫ੉࣌ ୡӝ ч * @param label Android Studio Animation Inspector ীࢲ [Transition] ਸ ҳ߹ೞח ؘ ࢎਊೡ కӒ */ @Stable class Transition<S> @PublishedApi internal constructor( initialState: S, label: String? )
  51. /** * [Transition] ਸ ࢤࢿೞҊ [targetState] чਵ۽ ୡӝ ࢚కܳ ૑੿೤פ׮.

    * [targetState] о ߸҃غݶ [Transition] ਷ ࢜ [targetState] чਵ۽ ٜ݅য૓ ݽٚ গפݫ੉࣌ਸ ઑ੿೤פ׮. * * @param targetState ߸҃ਸ х૑ೡ ؀࢚ * @param label Android Studio Animation Inspector ীࢲ [Transition] ਸ ҳ߹ೞח ؘ ࢎਊೡ కӒ * * @return [targetState] чਵ۽ ୡӝ ࢚కо ૑੿ػ [Transition] */ @Composable fun <T> updateTransition( targetState: T, label: String? = null ): Transition<T>
  52. /** * ૑੿ػ [Transition] ী ࢝ӭ গפݫ੉࣌ਸ ୶о೤פ׮. * ੉ח

    ੉ গפݫ੉࣌੄ ࢤݺ ઱ӝо [Transition] ী ੄೧ ҙܻؽਸ ੄޷೤פ׮. * * @param transitionSpec ੸ਊೡ গפݫ੉࣌ * @param label Android Studio Animation Inspector ীࢲ [Transition] ਸ ҳ߹ೞח ؘ ࢎਊೡ కӒ * @param targetValueByState ૑੿ػ [Transition] ੄ গפݫ੉࣌੉ द੘عਸ ٸ ઁҕೡ ч * * @return গפݫ੉࣌੉ ੸ਊػ [Color] ੄ [State] */ @Composable inline fun <S> Transition<S>.animateColor( noinline transitionSpec: @Composable Transition.Segment<S>.() -> FiniteAnimationSpec<Color> = { spring() }, label: String = "ColorAnimation", targetValueByState: @Composable (state: S) -> Color ): State<Color>
  53. /** * ࢚ਤ [Transition] ੄ ࢚క৬ ೞਤ ࢚క р੄ ݒೝਸ

    ӝ߈ਵ۽ ೞਤ [Transition] ਸ ٟ݅פ׮. * ੉ח ׮਺җ э਷ ਊب۽ ࢎਊؾפ׮: * * 1. ೞਤ [Transition] ࢚కܳ ࢚ਤ [Transition] ਵ۽ ഐ੉झ౴ * ࢚ਤ [Transition] ਷ زੌೠ ؀࢚੄ ࢚క ߸҃ਵ۽ ੋ೧ ૓೯ ઺ੋ গפݫ੉࣌੉ ੓ח૑ ৈࠗܳ ੋध೤פ׮. * ੉ۧѱ ೞݶ ࢎ੹ী ૓೯઺੉؍ গפݫ੉࣌੉ ৮ܐغ঻ਸ ٸ ࣽର੸ਵ۽ ׮ܲ গפݫ੉࣌ਸ ࢸ੿ೡ ࣻ ੓णפ׮. * * 2. ҙबࢎ ܻ࠙ * ࢚ਤ [Transition] ীࢲ ߉਷ ࢚కীࢲ ࢎਊೞח ੿ࠁ݅ ೙ఠ݂ೞৈ ೞਤ۽ ੹׳ೞҊ रਸ ٸ ਬਊೞѱ ॳੌ ࣻ ੓णפ׮. * * @param label Android Studio Animation Inspector ীࢲ [Transition] ਸ ҳ߹ೞח ؘ ࢎਊೡ కӒ * @param transformToChildState ࢚ਤ [Transition] ীࢲ чਸ ߉Ҋ ࢜۽ ݅ٚ ࢚క ч * * @return ࢜۽਍ ࢚కܳ о૑ח ೞਤ [Transition] */ @ExperimentalTransitionApi @Composable inline fun <S, T> Transition<S>.createChildTransition( label: String = "ChildTransition", transformToChildState: @Composable (parentState: S) -> T ): Transition<T>
  54. var selectedTabState by remember { mutableStateOf(TabDefaults.Items.first()) } val selectedTabTypeTransition =

    updateTransition( targetState = selectedTabState.type, label = "selected tab" ) // … TabContainer { TabDefaults.Items.forEach { tab -> val backgroundColor by selectedTabTypeTransition.animateColor( transitionSpec = { defaultTween() }, label = "background color" ) { movie -> when (movie == tab.type) { true -> TabDefaults.Color.selectedBackground false -> TabDefaults.Color.defaultBackground } } val textColor by selectedTabTypeTransition.animateColor( transitionSpec = { defaultTween() }, label = "text color" ) { movie -> when (movie == tab.type) { true -> TabDefaults.Color.selectedText
  55. ) { movie -> when (movie == tab.type) { true

    -> TabDefaults.Color.selectedBackground false -> TabDefaults.Color.defaultBackground } } val textColor by selectedTabTypeTransition.animateColor( transitionSpec = { defaultTween() }, label = "text color" ) { movie -> when (movie == tab.type) { true -> TabDefaults.Color.selectedText false -> TabDefaults.Color.defaultText } } TabItem( title = tab.shortname, backgroundColor = backgroundColor, textColor = textColor, onTabClick = { selectedTabState = tab } ) } }
  56. MovieContainer { AnimatedContent( modifier = Modifier .align(Alignment.CenterHorizontally) .padding(horizontal = 30.dp),

    targetState = selectedTabState, // … ӝઓҗ زੌ ) { tab -> MovieName(fullname = tab.fullname) } AnimatedContent( modifier = Modifier.wrapContentSize(), targetState = selectedTabState, // … ӝઓҗ زੌ ) { tab -> MoviePoster(posterDrawable = tab.poster) } }
  57. /** * [this][Transition] ੄ targetState о ߸҃ؼ ٸ ੗زਵ۽ [content]

    ী গפݫ੉࣌ਸ ੸ਊೞח ஶప੉ցੑפ׮. * * @param modifier ੸ਊೡ [Modifier] * @param transitionSpec ੸ਊೡ গפݫ੉࣌ * @param contentAlignment [content] о ߓ஖ؼ [Alignment] * @param contentKey [Transition] ੄ targetState ী ؀ೠ ః. زੌೠ ఃܳ ҕਬೞח ߸҃ੌ ҃਋ গפݫ੉࣌੉ ૓೯غ૑ ঋणפ׮. * @param content ߓ஖ೡ ஹನ੷࠶ */ @ExperimentalAnimationApi @Composable fun <S> Transition<S>.AnimatedContent( modifier: Modifier = Modifier, transitionSpec: AnimatedContentScope<S>.() -> ContentTransform = { // ӝઓ AnimatedContent ৬ زੌ }, contentAlignment: Alignment = Alignment.TopStart, contentKey: (targetState: S) -> Any? = { it }, content: @Composable AnimatedVisibilityScope.(targetState: S) -> Unit )
  58. MovieContainer { selectedTabTypeTransition .createChildTransition(label = "selected tab fullname") { movie

    -> movie.fullname } .AnimatedContent(/* ӝઓҗ زੌ */) { tabFullname -> MovieName(fullname = tabFullname) } selectedTabTypeTransition .AnimatedContent(/* ӝઓҗ زੌ */) { movie -> MoviePoster(posterDrawable = movie.poster) } }
  59. @Composable private fun MovieTab( selectedTabTypeTransition: Transition<Movie>, updateSelectedTab: (tab: Tab) ->

    Unit ) { BoxWithConstraints( modifier = Modifier .fillMaxWidth() .wrapContentHeight() .clip( RoundedCornerShape( bottomStartPercent = DefaultCornerUnit, bottomEndPercent = DefaultCornerUnit ) ) ) { val backgroundBoxWidth = remember { maxWidth / TabDefaults.Items.count() } val backgroundOffsetTransition by selectedTabTypeTransition.animateIntOffset( transitionSpec = { defaultTween() }, label = "background offset" ) { movie -> IntOffset( x = with(LocalDensity.current) { (eachItemWidth * movie.index).toPx() }.toInt(),
  60. ) ) { val eachItemWidth = remember { maxWidth /

    TabDefaults.Items.count() } val backgroundOffsetTransition by selectedTabTypeTransition.animateIntOffset( transitionSpec = { defaultTween() }, label = "background offset" ) { movie -> IntOffset( x = with(LocalDensity.current) { (backgroundBoxWidth * movie.index).toPx() // dp -> float }.toInt(), // float -> int y = 0 ) } val backgroundShapeTransition by selectedTabTypeTransition.animateValue( transitionSpec = { defaultTween() }, label = "background shape", typeConverter = TwoWayConverter( convertToVector = { corner -> AnimationVector4D( v1 = corner.topStart.toPercent(), v2 = corner.topEnd.toPercent(), v3 = corner.bottomStart.toPercent(), v4 = corner.bottomEnd.toPercent() ) }, convertFromVector = { vector ->
  61. /** * গפݫ੉࣌ী ࢎਊغח ݽٚ ؘ੉ఠ ਬഋ਷ ରਗী ٮۄ *

    [AnimationVector1D], [AnimationVector2D], [AnimationVector3D] ژח [AnimationVector4D] ۽ ߸ജؾפ׮. * ٮۄࢲ ё୓੄ ৈ۞ ҳࢿਃࣗܳ пп ੗୓ ࣘب ୶੸ ӝמਸ ࢎਊೞৈ ة݀੸ਵ۽ গפݫ੉࣌ ୊ܻೡ ࣻ ੓णפ׮. * * ৘ܳ ٜয [Color] ח A, R, G, B 4ѐ੄ ҳࢿ ਃࣗ۽ ੉ܖযઉ ੓णפ׮. * ٮۄࢲ [AnimationVector4D] ܳ ࢎਊ೤פ׮. */ sealed class AnimationVector { internal abstract fun reset() internal abstract fun newVector(): AnimationVector internal abstract operator fun get(index: Int): Float internal abstract operator fun set(index: Int, value: Float) internal abstract val size: Int }
  62. /** * ੐੄੄ ఋੑ [T] ীࢲ [AnimationVector] ۽ ߸ജೞҊ [AnimationVector]

    ܳ ׮द ఋੑ [T] ۽ ߸ജೞח ߑߨী ؀ೠ ੿੄о ನೣغয ੓णפ׮. * ੉ܳ ా೧ গפݫ੉࣌ਸ ݽٚ ఋੑ੄ ѐ୓ীࢲ ҳഅೡ ࣻ ੓णפ׮. */ interface TwoWayConverter<T, V : AnimationVector> { /** * ఋੑ [T] ਸ [AnimationVector] ఋੑਵ۽ ߸ജೞח ߑߨਸ ੿੄೤פ׮. */ val convertToVector: (T) -> V /** * [AnimationVector] ఋੑਸ ׮द ఋੑ [T] ۽ ߸ജೞח ߑߨਸ ੿੄೤פ׮. */ val convertFromVector: (V) -> T }
  63. ) } val backgroundShapeTransition by selectedTabTypeTransition.animateValue( transitionSpec = { defaultTween()

    }, label = "background shape", typeConverter = TwoWayConverter( convertToVector = { corner -> AnimationVector4D( v1 = corner.topStart.getPercent(), v2 = corner.topEnd.getPercent(), v3 = corner.bottomStart.getPercent(), v4 = corner.bottomEnd.getPercent() ) }, convertFromVector = { vector -> RoundedCornerShape( topStartPercent = vector.v1.toInt(), topEndPercent = vector.v2.toInt(), bottomStartPercent = vector.v3.toInt(), bottomEndPercent = vector.v4.toInt() ) } ) ) { movie -> when (movie) { Movie.Thor -> RoundedCornerShape(bottomStartPercent = 30) Movie.Spider -> RoundedCornerShape(percent = 0)
  64. ) ) { movie -> when (movie) { Movie.Thor ->

    RoundedCornerShape(bottomStartPercent = 30) // ৽ଃ -> ৽ଃ ೞױ݅ ۄ਍٬ Movie.Spider -> RoundedCornerShape(percent = 0) // о਍ؘ -> ۄ਍٬ X Movie.Doctor -> RoundedCornerShape(bottomEndPercent = 30) // য়ܲଃ -> য়ܲଃ ೞױ݅ ۄ਍٬ } } TabContainer { TabDefaults.Items.forEach { tab -> val textColor by selectedTabTypeTransition.animateColor( transitionSpec = { defaultTween() }, label = "text color" ) { movie -> when (movie == tab.type) { true -> TabDefaults.Color.selectedText false -> TabDefaults.Color.defaultText } } TabItem( title = tab.shortname, backgroundColor = TabDefaults.Color.defaultBackground, textColor = textColor, onTabClick = {
  65. false -> TabDefaults.Color.defaultText } } TabItem( title = tab.shortname, backgroundColor

    = TabDefaults.Color.defaultBackground, textColor = textColor, onTabClick = { updateSelectedTab(tab) } ) } } Box( modifier = Modifier .width(backgroundBoxWidth) .height(TabDefaults.Height) .offset { backgroundOffsetTransition } .clip(backgroundShapeTransition) .background(color = TabDefaults.Color.selectedBackground) ) } }
  66. fun Modifier.movement(lookaheadScope: LookaheadLayoutScope) = composed { var targetOffset: IntOffset? by

    remember { mutableStateOf(null) } var placementOffset by remember { mutableStateOf(IntOffset.Zero) } // 1. ӝࠄч੉ হ׮: animateAsState X // 2. ׮਺ গפݫ੉࣌ чਵ۽ ੿೧૓ ч੉ হ׮: Transition X with(lookaheadScope) { this@composed .onPlaced { lookaheadScopeCoordinates, layoutCoordinates -> targetOffset = lookaheadScopeCoordinates .localLookaheadPositionOf(sourceCoordinates = layoutCoordinates) .round() placementOffset = lookaheadScopeCoordinates .localPositionOf( sourceCoordinates = layoutCoordinates, relativeToSource = Offset.Zero ) .round() } .intermediateLayout { measurable, constraints, _ -> val placeable = measurable.measure(constraints) layout(width = placeable.width, height = placeable.height) { val (x, y) = targetOffset!! - placementOffset
  67. fun Modifier.transformation(lookaheadScope: LookaheadLayoutScope) = with(lookaheadScope) { intermediateLayout { measurable, _,

    lookaheadSize -> val (width, height) = lookaheadSize // 1. ӝࠄч੉ হ׮: animateAsState X // 2. ׮਺ গפݫ੉࣌ чਵ۽ ੿೧૓ ч੉ হ׮: Transition X val animatedConstraints = Constraints.fixed( width = width.coerceAtLeast(0), height = height.coerceAtLeast(0) ) val placeable = measurable.measure(animatedConstraints) layout(width = placeable.width, height = placeable.height) { placeable.place(x = 0, y = 0) } } }
  68. /** * [animateTo] ܳ ా೧ ч੉ ߸҃ؼ ٸ ੗زਵ۽ чী

    গפݫ੉࣌ਸ ੸ਊೞח ч ഓ؊ੑפ׮. */ class Animatable<T, V : AnimationVector>( initialValue: T, // ୡӝ ч val typeConverter: TwoWayConverter<T, V>, // গפݫ੉࣌ ё୓ী ࢎਊೡ AnimationVector private val visibilityThreshold: T? = null // оदࢿ ੐҅ч ) { // গפݫ੉࣌੄ അ੤ ч val value: T get() = internalState.value // അ੤ গפݫ੉࣌੄ ؀࢚. গפݫ੉࣌੉ ઺ױ হ੉ ՘աݶ ੉ ݾ಴ чী ب׳೤פ׮. var taretValue: T by mutableStateOf(initialValue) private set // targetValue чਸ ೱ೧ গפݫ੉࣌ਸ द੘೤פ׮. block ੋ੗ח ݒ গפݫ੉࣌ ೐ۨ੐ ݃׮ ഐ୹ؾפ׮. suspend fun animateTo( targetValue: T, animationSpec: AnimationSpec<T> = defaultSpringSpec, initialVelocity: T = velocity, block: (Animatable<T, V>.() -> Unit)? = null ): AnimationResult<T, V> }
  69. fun Modifier.animateMovement( lookaheadScope: LookaheadLayoutScope, animationSpec: AnimationSpec<IntOffset> = defaultSpring() ) =

    composed { var placementOffset by remember { mutableStateOf(IntOffset.Zero) } var targetOffset: IntOffset? by remember { mutableStateOf(null) } var targetOffsetAnimation: Animatable<IntOffset, AnimationVector2D>? by remember { mutableStateOf(null) } LaunchedEffect(Unit) { snapshotFlow { targetOffset }.collect { target -> if (target != null && target != targetOffsetAnimation?.targetValue) { targetOffsetAnimation?.run { launch { animateTo( targetValue = target, animationSpec = animationSpec ) } } ?: Animatable( initialValue = target, typeConverter = IntOffset.VectorConverter ).let { offsetAnimatable ->
  70. animationSpec = animationSpec ) } } ?: Animatable( initialValue =

    target, typeConverter = IntOffset.VectorConverter ).let { offsetAnimatable -> targetOffsetAnimation = offsetAnimatable } } } } with(lookaheadScope) { this@composed .onPlaced { /* ӝઓҗ زੌ */ } .intermediateLayout { measurable, constraints, _ -> val placeable = measurable.measure(constraints) layout(width = placeable.width, height = placeable.height) { val (x, y) = (targetOffsetAnimation?.value ?: targetOffset!!) - placementOffset placeable.place(x = x, y = y) } } } }
  71. fun Modifier.animateTransformation( lookaheadScope: LookaheadLayoutScope, animationSpec: AnimationSpec<IntSize> = defaultSpring() ) =

    composed { var targetSize: IntSize? by remember { mutableStateOf(null) } var targetSizeAnimation: Animatable<IntSize, AnimationVector2D>? by remember { mutableStateOf(null) } LaunchedEffect(Unit) { snapshotFlow { targetSize }.collect { target -> if (target != null && target != targetSizeAnimation?.targetValue) { targetSizeAnimation?.run { launch { animateTo( targetValue = target, animationSpec = animationSpec ) } } ?: Animatable( initialValue = target, typeConverter = IntSize.VectorConverter ).let { sizeAnimatable -> targetSizeAnimation = sizeAnimatable }
  72. } ?: Animatable( initialValue = target, typeConverter = IntSize.VectorConverter ).let

    { sizeAnimatable -> targetSizeAnimation = sizeAnimatable } } } } with(lookaheadScope) { [email protected] { measurable, _, lookaheadSize -> targetSize = lookaheadSize val (width, height) = targetSizeAnimation?.value ?: lookaheadSize val animatedConstraints = Constraints.fixed( width = width.coerceAtLeast(0), height = height.coerceAtLeast(0) ) val placeable = measurable.measure(animatedConstraints) layout(width = placeable.width, height = placeable.height) { placeable.place(x = 0, y = 0) } } }
  73.  ৈ۞ѐ੄"OJNBUJPO4QFDоמ  ৈ۞ѐ੄গפݫ੉࣌оמ  ೞա੄ఋੑ݅оמ "OJNBUBCMF <5> 4UBUF <5>

    PCKFDU PCKFDU BOJNBUF5P  BOJNBUF5P  "OJNBUJPO 4QFD "OJNBUJPO 4QFD "OJNBUBCMF
  74.  ৈ۞ѐ੄"OJNBUJPO4QFDоמ  ৈ۞ѐ੄গפݫ੉࣌оמ  ৈ۞ѐ੄ఋੑоמ  উ٘۽੉٘झౚ٣য়JOTQFDUоמ 5SBOTJUJPO 


    <5> 4UBUF <5> 5SBOTJUJPO PCKFDU PCKFDU BOJNBUF <5> BOJNBUF <5> "OJNBUJPO 4QFD "OJNBUJPO 4QFD
  75. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  76. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  77. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  78. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  79. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  80. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  81. fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt { var `String$arg-0$call-print$fun-earth`

    = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  82. private val liveLiteralCache = HashMap<String, MutableState<Any?>>() @InternalComposeApi @ComposeCompilerApi fun <T>

    liveLiteral(key: String, value: T): State<T> { return liveLiteralCache.getOrPut(key) { mutableStateOf(value) } as State<T> }
  83. @InternalComposeApi fun updateLiveLiteralValue(key: String, value: Any?) { var needToUpdate =

    true val stateObj = liveLiteralCache.getOrPut(key) { needToUpdate = false mutableStateOf(value) } if (needToUpdate) { stateObj.value = value } }
  84. private fun findEffectiveRecomposeScope(group: Int): RecomposeScopeImpl? { var current = group

    while (current > 0) { for (data in DataIterator(this, current)) { if (data is RecomposeScopeImpl) { return data } } current = groups.parentAnchor(current) } return null }
  85. // @Composable XXX fun earth() { print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`()) } object LiveLiterals$EarthKt

    { var `String$arg-0$call-print$fun-earth` = "Bye World" var `State$String$arg-0$call-print$fun-earth`: MutableState<String>? = null fun `getString$arg-0$call-print$fun-earth`(): String { val field = this.`String$arg-0$call-print$fun-earth` val state = if (field == null) { val tmp = liveLiteral( "String$arg-0$call-print$fun-earth", this.`String$arg-0$call-print$fun-earth` ) this.`String$arg-0$call-print$fun-earth` = tmp tmp } else field return field.value } }
  86. /** * LiveLiteral ੉ ெઉ ੓ח ҃਋ীب ੸ਊغח ߧਤ ղীࢲ

    LiveLiteral ਸ ࢤࢿೞ૑ ঋب۾ * ஹನૉ ஹ౵ੌ۞ী ಴दೞח ؘ ࢎਊؾפ׮. */ @Target( AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.FILE ) @Retention(AnnotationRetention.SOURCE) annotation class NoLiveLiterals @NoLiveLiterals fun earth() { print("Bye World") }
  87. /** * উ੿ ࢚కח ௼ѱ 3о૑੄ ઑѤਸ ٮܵפ׮. * *

    - ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮. (૊, [State] ۽ ੘زظঠ ೣ) * - ੉੹ ੋझఢझ৬ അ੤ ੋझఢझо زੌ೧ঠ ೤פ׮. * - ݽٚ ҕѐ ೙ٜ٘ب ׮ উ੿ ࢚కৈঠ ೤פ׮. * * ੉ 3о૑ ઑѤਸ ٮܲ׮ݶ ஹನૉח ೧׼ ೙٘о উ੿੸ੋ ࢚కۄҊ ౵ঈೞҊ, * ೧׼ ೙٘੄ ч੉ ߄Շ૑ ঋও׮ݶ ࢎਊػ ஹನ੷࠶੄ ܻஹನ૑࣌ਸ ࢤۚ೤פ׮. * * @see Immutable * @see Stable */ @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) annotation class StableMarker
  88. /** * ࢤࢿػ ੉റ۽ ݽٚ ҕѐ੸ੋ ೙٘о ੺؀ ߸ೞ૑ ঋח׮ח

    Ѫਸ աఋղݴ ௿ېझী ੸ਊؼ ࣻ ੓णפ׮. * * ࢤࢿ ੉റ ч੉ ߸҃غ૑ ঋਵ޲۽ উ੿੄ ୐ ߣ૩ ӏ஗ੋ * "ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮." о ޖदؾפ׮. */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Immutable fun main() { val list = mutableListOf(1) list.add(2) // ੋझఢझח زੌೞ૑݅ ч੉ ߄Չ ࣻ ੓णפ׮. // @Immutable ਷ ੉۞ೠ ߸҃ب ೲਊೞ૑ ঋणפ׮. }
  89. /** * ч੉ ߸҃ؼ ࣻ ੓ח ࢚క੉ݴ, ੸ਊ ؀࢚ী ٮۄ

    ডрঀ ৉ೡ੉ ׳ۄ૘פ׮. * * ఋੑী ੸ਊػ׮ݶ ୶о ৉ೡ হ੉ [StableMarker] ੄ ৉ೡਸ Ӓ؀۽ оઉцפ׮. * * ೣࣻա ೐۽ಌ౭ী ੸ਊػ׮ݶ [StableMarker] ੄ ৉ೡী ୶оغח ৉ೡ੉ ࢤӤפ׮. * э਷ input ী ੓যࢲח ೦࢚ زੌೠ output ਸ ٜ݅যղݴ(ࣽࣻ ೣࣻ), ೣࣻ੄ ҃਋ ੋ੗ٜ ৉द ݽف উ੿੸ੋ ࢚కۄח Ѫਸ ডࣘ೤פ׮. * ݅ড input ੉ زੌೞ׮ݶ output ژೠ زੌೡ Ѫ੉ӝ ٸޙী ܻஹನ૑࣌ਸ झఈೞѱ ؾפ׮. */ @Target( AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY ) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Stable
  90. /** * ஹ౵ੌ द੼ী ݽٚ ௿ېझٜ੄ উ੿ࢿਸ ੗زਵ۽ ୶ۿ೤פ׮. *

    * @param parameters উ੿ࢿਸ ୶ۿೞחؘ ب਑੉ ؼ ࣻ ੓ѱ ੋ੗ٜ੄ ࠺౟݃झ௼ܳ աఋշפ׮. */ @ComposeCompilerApi @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) annotation class StabilityInferred(val parameters: Int) // ਬੌೠ ೙٘ੋ value о উ੿ ࢚క੉Ҋ ࠛ߸ೞӝ ٸޙী Name ௿ېझח উ੿ ࢚క۽ ౸ױؾפ׮. class Name(val value: String) // ਬੌೠ ೙٘ੋ value ੄ ఋੑ੉ ઁ֎ܼ੉ۄ ఋੑਸ ౸ױೡ ࣻ হӝী ࢎ੹ী ࠺౟݃झఊػ чਸ ӝળਵ۽ উ੿ ࢚కܳ ୶ۿ೤פ׮. // ೞ૑݅ ೙٘о о߸ ࢚క੉ӝ ٸޙী T ఋੑ੉ উ੿੉ৈب NameWithGeneric ௿ېझח ࠛউ੿ ࢚క۽ ౸ױؾפ׮. class NameWithGeneric<T>(var value: T)
  91. CVJMENFUSJDT // ஹ౵ੌ۞о х૑ೠ ஹನ੷࠶ ೣٜࣻ੄ ੿ࠁܳ աఋշפ׮. kotlinOptions {

    freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + "${rootProject.file(".").absolutePath}/report/compose-metrics" ] }
  92. <ݽٕݺ>@<࠽٘ఋੑ>NPEVMFKTPO { { "skippableComposables": 48, // skippable ࢚కੋ ஹನ੷࠶ ѐࣻ

    "restartableComposables": 67, "readonlyComposables": 0, "totalComposables": 67, // ੹୓ ஹನ੷࠶ ѐࣻ "restartGroups": 67, "totalGroups": 72, "staticArguments": 130, "certainArguments": 28, "knownStableArguments": 793, // উ੿ ࢚క۽ ঌ۰૓ ੋ੗ ѐࣻ "knownUnstableArguments": 30, // ࠛউ੿ ࢚క۽ ঌ۰૓ ੋ੗ ѐࣻ "unknownStableArguments": 8, "totalArguments": 831, "markedStableClasses": 0, "inferredStableClasses": 16, // উ੿ ࢚క۽ ୶ۿػ ௿ېझ ѐࣻ "inferredUnstableClasses": 2, // ࠛউ੿ ࢚క۽ ୶ۿػ ௿ېझ ѐࣻ "inferredUncertainClasses": 0, "effectivelyStableClasses": 16, "totalClasses": 18, "memoizedLambdas": 54, "singletonLambdas": 5, "singletonComposableLambdas": 8, "composableLambdas": 31, "totalLambdas": 89 }
  93. CVJMESFQPSUT // ஹ౵ੌ۞о х૑ೠ ஹನ੷࠶ ೣٜࣻ੉ ࢎਊ઺ੋ ௿ېझ৬ ੋ੗ী ؀ೠ

    উ੿ࢿ ੿ࠁܳ աఋշפ׮. kotlinOptions { freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + "${rootProject.file(".").absolutePath}/report/compose-reports" ] }
  94. <ݽٕݺ>@<࠽٘ఋੑ>DMBTTFTUYU /* ——— classes ——— */ sealed class UiState {

    object Loading : UiState() object Done : UiState() data class Exception(val throwable: Throwable) : UiState() } /* ——— reports ——— */ stable class Loading { <runtime stability> = Stable } stable class Done { <runtime stability> = Stable } unstable class Exception { unstable val throwable: Throwable <runtime stability> = Unstable }
  95. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTDTW /* ——— composables ——— */ @Composable fun DisplayText(text: String

    = "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { Text(text = texts.joinToString()) } /* ——— reports ——— */
  96. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @Composable fun DisplayText(text: String

    = "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { Text(text = texts.joinToString()) } /* ——— reports ——— */ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText( stable text: String? = @dynamic LiveLiterals$MainActivityKt.String$param-text$fun-DisplayText() ) restartable /* skippable ੉ হ਺ */ scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts( unstable texts: List<String> )
  97. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @Composable fun DisplayText(text: String

    = "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { Text(text = texts.joinToString()) } /* ——— reports ——— */ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText( stable text: String? = @dynamic LiveLiterals$MainActivityKt.String$param-text$fun-DisplayText() ) restartable scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts( unstable texts: List<String> )
  98. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @NoLiveLiterals // new @Composable

    fun DisplayText(text: String = "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { Text(text = texts.joinToString()) } /* ——— reports ——— */ restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText( stable text: String? = @static "Hi" ) restartable scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts( unstable texts: List<String> )
  99. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* ܻஹನ૑࣌੉ য٣ী ૓೯ؼөਃ? 1, setContent content ৔৉ 2. Text 3. setContent content ৔৉ + Text */
  100. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* [first-composition] setContent recomposition Text recomposition [re-composition] setContent recomposition Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ */
  101. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } /* [first-composition] setContent recomposition Text recomposition Button recomposition [re-composition] Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ */
  102. EPOVUIPMFTLJQQJOH // ஹ౵ੌ ੹ @Composable fun TextWrapper(text: String) { Text(text

    = text) } // ஹ౵ੌ റ (೨ब ࠗ࠙݅ ಴द) @Composable fun TextWrapper(composer: Composer, text: String) { composer.startRestartGroup(-199242123) // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ૑࣌ झ௏೐ܳ ୶оೞҊ ੓਺ Text(text = text) composer.endRestartGroup() }
  103. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } setContent { var number by remember { mutableStateOf(0) } println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Text RecomposeScope 

  104. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Button RecomposeScope
  105. 4UBCJMJUZ4ZTUFN setContent { var number by remember { mutableStateOf(1) }

    TextWrapper( modifier = Modifier.clickable { number++ }, text = number.toString() ) } @Composable fun TextWrapper( modifier: Modifier = Modifier, text: String ) { Text(modifier = modifier, text = text) }
  106. 4UBCJMJUZ4ZTUFN setContent { var number by remember { mutableStateOf(1) }

    TextWithLambda( modifier = Modifier.clickable { number++ }, text = { number.toString() } ) } @Composable fun TextWithLambda( modifier: Modifier = Modifier, text: () -> String // Function0<String> ) { Text(modifier = modifier, text = text()) }
  107. NPWBCMF$POUFOU0G val content = remember<@Composable () -> Unit> { {

    repeat(2) { Box(modifier = Modifier.size(100.dp).background(color = Color.Green)) } } } Column { Button(onClick = { isRow = !isRow }) { Text(text = "Switch") } if (isRow) { Row { content() } } else { Column { content() } } }
  108. NPWBCMF$POUFOU0G val content = remember { movableContentOf { // movableContentWithReceiverOf

    repeat(2) { Box(modifier = Modifier.size(100.dp).background(color = Color.Green)) } } } Column { Button(onClick = { isRow = !isRow }) { Text(text = "Switch") } if (isRow) { Row { content() } } else { Column { content() } } }
  109. !3FBE0OMZ$PNQPTBCMF object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get()

    = LocalColors.current val typography: Typography @Composable @ReadOnlyComposable get() = LocalTypography.current // … } @Composable @ReadOnlyComposable fun stringResource(@StringRes id: Int): String { val resources = resources() return resources.getString(id) }
  110. !/PO3FTUBSUBCMF$PNQPTBCMF @Composable @NonRestartableComposable fun DisposableEffect(key1: Any?, effect: DisposableEffectScope.() -> DisposableEffectResult)

    { remember(key1) { DisposableEffectImpl(effect) } } @Composable @NonRestartableComposable fun Image( modifier: Modifier = Modifier, colorFilter: ColorFilter? = null, imageVector: ImageVector, contentDescription: String?, alpha: Float = DefaultAlpha, alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit ) { Image( modifier = modifier, alignment = alignment, contentScale = contentScale, alpha = alpha, colorFilter = colorFilter, painter = rememberVectorPainter(imageVector), contentDescription = contentDescription ) }
  111. TUBUJD$PNQPTJUJPO-PDBM0G val LocalContext = staticCompositionLocalOf<Context> { noLocalProvidedFor("LocalContext") } val LocalClipboardManager

    = staticCompositionLocalOf<ClipboardManager> { noLocalProvidedFor("LocalClipboardManager") } val LocalDensity = staticCompositionLocalOf<Density> { noLocalProvidedFor("LocalDensity") } val LocalFocusManager = staticCompositionLocalOf<FocusManager> { noLocalProvidedFor("LocalFocusManager") }
  112. #BTFMJOFQSPGJMFT .BDSPCFODINBSLݽٕࢸ੿ ௏٘੘ࢿ CBTFMJOFQSPGJMFTࢤࢿ  @ExperimentalBaselineProfilesApi @RunWith(AndroidJUnit4::class) class BaselineProfileGenerator {

    @get:Rule val baselineProfileRule = BaselineProfileRule() @Test fun startup() = baselineProfileRule.collectBaselineProfile( packageName = AppPackageName ) { pressHome() startActivityAndWait() } }
  113. #BTFMJOFQSPGJMFT .BDSPCFODINBSLݽٕࢸ੿ ௏٘੘ࢿ CBTFMJOFQSPGJMFT੸ਊ੹റஏ੿  @RunWith(AndroidJUnit4::class) class BaselineProfileBenchmark { @get:Rule

    val benchmarkRule = MacrobenchmarkRule() // … private fun startup(compilationMode: CompilationMode) { benchmarkRule.measureRepeated( packageName = AppPackageName, metrics = listOf(StartupTimingMetric()), iterations = 10, startupMode = StartupMode.COLD, compilationMode = compilationMode ) { pressHome() startActivityAndWait() } } }
  114. #BTFMJOFQSPGJMFT .BDSPCFODINBSLݽٕࢸ੿ ௏٘੘ࢿ CBTFMJOFQSPGJMFT੸ਊ੹റஏ੿  @RunWith(AndroidJUnit4::class) class BaselineProfileBenchmark { @get:Rule

    val benchmarkRule = MacrobenchmarkRule() @Test fun startupNoCompilation() { startup(compilationMode = CompilationMode.None()) // JIT ஹ౵ੌ } @Test fun startupBaselineProfile() { startup(compilationMode = CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require)) // AOT ஹ౵ੌ } // … }