Slide 1

Slide 1 text

߅ળࣻ, ইಕ۽ ӭՔೠ ࢶ঱ഋ UI with Jetpack Compose ࢶ঱ഋ UIܳ ঠޖ૑ѱ ׮ܖח ߑߨী ؀ೠ ੋࢎ੉౟ܳ ٘݀פ׮.

Slide 2

Slide 2 text

߅ળࣻ, ইಕ۽ litt.ly/juns

Slide 3

Slide 3 text

ৈ۞࠙੄ UI?

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

ࢶ঱ഋ UI = ‘যڌѱ?’о ইצ ‘যڃ Ѫ?’

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

ݾର ஹನք౟੄ ܻ࠙ द੼ ೣࣻ ֎੉߁ о੉٘ ౵ۄ޷ఠ ࢶ঱ о੉٘

Slide 11

Slide 11 text

ߊ಴ ੗ܐ ࢠ೒ ೐۽ં౟

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Composable ஹನք౟੄ ܻ࠙ द੼ ঱ઁ যڌѱ UI ஹನք౟ܳ ܻ࠙೧ঠೡөਃ?

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

fun TopAppBar() fun GenreRecommendation() fun BrowseAll()

Slide 16

Slide 16 text

Text() Column() fun RecommendationCardList() fun GenreRecommendation()

Slide 17

Slide 17 text

Row() fun RecommendationCard() fun RecommendationCardList()

Slide 18

Slide 18 text

Box() Image() Text() fun RecommendationCard()

Slide 19

Slide 19 text

ೞա੄ ஹನք౟ = ೞա੄ ޙઁ

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

@Composable fun Button( onClick: () -> Unit, modi fi er: Modi fi er = Modi fi er, enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ }

Slide 22

Slide 22 text

@Composable fun Button( onClick: () -> Unit, modi fi enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun OutlinedButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.OutlinedButtonContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )

Slide 23

Slide 23 text

@Composable fun Button( onClick: () -> Unit, modi fi enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun TextButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.TextButtonContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )

Slide 24

Slide 24 text

@Composable fun Button( onClick: () -> Unit, modi fi enabled: Boolean = true, shape: Shape = ButtonDefaults.shape, colors: ButtonColors = ButtonDefaults.buttonColors(), elevation: ButtonElevation? = ButtonDefaults.buttonElevation(), border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, content: @Composable RowScope.() -> Unit ) { /* ... */ } @Composable fun ElevatedButton( onClick: () -> Unit, /* ... */ contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ) = Button( /* ... */ )

Slide 25

Slide 25 text

Ҋࣻળ ஹನք౟ੌࣻ۾ ز੘੄ ੿੄ח ݺഛ೧૑Ҋ ழझథ੄ ৈ૑ח ੸য૓׮

Slide 26

Slide 26 text

ز੘ ୶о, ࢚ഐ ഒ೤ = ੷ࣻળ ஹನք౟

Slide 27

Slide 27 text

ة݀੸ਵ۽ ز੘ೞח UI ઁҕ = Ҋࣻળ ஹನք౟

Slide 28

Slide 28 text

Composable ೣࣻ ֎੉߁ о੉٘ ਬഋ߹ Composable ೣࣻ੄ ֎੉߁ী ؀೧ ঌইࠇפ׮

Slide 29

Slide 29 text

UIܳ о૑ח Composable
 (Unitਸ ߈ജೞח Composable)

Slide 30

Slide 30 text

@Composable fun fancyButton(text: String, onClick: () -> Unit) { /* ... */ }

Slide 31

Slide 31 text

@Composable fun FancyButton(text: String, onClick: () -> Unit) { /* ... */ } @Composable fun fancyButton(text: String, onClick: () -> Unit) { /* ... */ }

Slide 32

Slide 32 text

UIܳ о૑ח Composable ೣࣻ ֎੉߁ ӏ஗ - ౵झணா੉झ - ݺࢎ ࢎਊ - زࢎ, ੹஖ࢎ ߂ ࠗࢎ ࢎਊ ૑ন - ࢲࣿ੸ ਊߨ੄ ഋਊࢎ ೣԋ ࢎਊ оמ

Slide 33

Slide 33 text

чਸ ߈ജೞח Composable

Slide 34

Slide 34 text

@Composable fun DefaultStyle(): Style { /* ... */ }

Slide 35

Slide 35 text

@Composable fun defaultStyle(): Style { /* ... */ } @Composable fun DefaultStyle(): Style { /* ... */ }

Slide 36

Slide 36 text

чਸ ߈ജೞח Composable ೣࣻ ֎੉߁ ӏ஗ - ஠ݮா੉झ - ੌ߈੸ੋ ௏ౣܽ ೣࣻ ֎੉߁ ஶ߮࣌ਸ ٮܴ - ੌ߈ ೣࣻ - ஠ݮா੉झ - ಂషܻ ೣࣻ - ৘৻੸ਵ۽ ஠ݮா੉झ + ௿ېझݺ ࢎਊ - ౵झணா੉झח UIܳ о૑ח Composable ೣࣻ৬ ഒز੄ ৈ૑о ੓ӝ ٸޙ

Slide 37

Slide 37 text

чਸ remember {} ೞח Composable

Slide 38

Slide 38 text

remember = Recompositionਵ۽ࠗఠ ؘ੉ఠ ࠁഐ

Slide 39

Slide 39 text

@Composable fun createCoroutineScope(): CoroutineScope = remember { /* ... */ }

Slide 40

Slide 40 text

@Composable fun rememberCoroutineScope(): CoroutineScope = remember { /* ... */ } @Composable fun createCoroutineScope(): CoroutineScope = remember { /* ... */ }

Slide 41

Slide 41 text

чਸ remember {} ೞח Composable ೣࣻ
 ֎੉߁ ӏ஗ - rememberܳ ੹஖೧ঠ ೣ - ഐ୹੗ীѱ Recomposition, ࠗࣻ ബҗ١ਵ۽ࠗఠ ਬ૑ؼ ࣻ ੓ח ч੐ਸ ݺद੸ਵ۽ ಴അ

Slide 42

Slide 42 text

UIܳ о૑ח Composable fun FancyButton() fun fancyButton() - ౵झணா੉झ - ݺࢎ ࢎਊ чਸ ߈ജೞח Composable fun defaultStyle(): Style fun DefaultStyle(): Style - ஠ݮா੉झ - ಂషܻ ೣࣻ - ౵झணா੉झ чਸ remember {} ೞח Composable fun rememberCoroutineScope(): CoroutineScope fun createCoroutineScope(): CoroutineScope - remember ੹஖

Slide 43

Slide 43 text

Composable ೣࣻ ౵ۄ޷ఠ ࢶ঱ о੉٘ Composable ೣࣻ ౵ۄ޷ఠ੄ ࢶ঱ ࣽࢲ, ഋకী ؀೧ ঌইࠇפ׮

Slide 44

Slide 44 text

ݺद੸ੋ ੄ઓࢿҗ ঐद੸ੋ ੄ઓࢿ

Slide 45

Slide 45 text

ݺद੸ = ౵ۄ޷ఠ۽ ੹׳

Slide 46

Slide 46 text

ঐद੸ = Context۽ ੹׳

Slide 47

Slide 47 text

@Composable fun Button( onClick: () -> Unit, ) { } val border = LocalButtonBorder.current

Slide 48

Slide 48 text

@Composable fun Button( onClick: () -> Unit, ) { } LocalButtonBorder.current border: BorderStroke = @Composable fun Button( onClick: () -> Unit, ) { val border = LocalButtonBorder.current }

Slide 49

Slide 49 text

ݺद੸ੋ ੄ઓࢿҗ ঐद੸ੋ ੄ઓࢿ - ݺद੸ੋ ੄ઓࢿ - ೣࣻ ౵ۄ޷ఠ۽ ੄ઓࢿਸ ࢸ੿ - ஹನք౟੄ ز੘ਸ ৘ஏೞӝ ए਑ - ࢎਊࢿ੉ જҊ పझ౟ ਊ੉ - ঐद੸ੋ ੄ઓࢿ - CompositionLocal۽ ૑੿ - ழझథ ӝמਸ ࢎਊೞӝ ਤ೧ ௏٘ ୶੸੉ ೙ো੸ - Ӗ۽ߥೠ झఋੌ݂੉ ೙ਃೡ ҃਋ ࢎਊ

Slide 50

Slide 50 text

Modifier

Slide 51

Slide 51 text

@Composable fun Icon( bitmap: ImageBitmap, // no modi fi er parameter tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi er is not the fi rst optional parameter // 2: padding will be lost as soon as the user sets its own modi fi er modi fi er: Modi fi er = Modi fi er.padding(8.dp) ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi er is intended to specify the external behavior of // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi er: Modi fi er = Modi fi er, checkboxModi fi er: Modi fi er = Modi fi er ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { Box(Modi fi er.padding(16.dp)) { Icon( buttonBitmap, // modi fi er should be applied to the outer-most layout // and be the fi rst one in the chain modi fi er = Modi fi er.aspectRatio(1f).then(modi fi er), tint = tint ) } }

Slide 52

Slide 52 text

@Composable fun Icon( bitmap: ImageBitmap, // no modi fi er parameter tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } }

Slide 53

Slide 53 text

@Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi er is not the fi rst optional parameter // 2: padding will be lost as soon as the user sets its own modi fi er modi fi er: Modi fi er = Modi fi er.padding(8.dp) ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } }

Slide 54

Slide 54 text

@Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi tint: Color = Color.Black ) { Box(Modi fi Icon( buttonBitmap, // modi fi // and be the fi modi fi tint = tint ) } } @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi er is intended to specify the external behavior of // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi er: Modi fi er = Modi fi er, checkboxModi fi er: Modi fi er = Modi fi er )

Slide 55

Slide 55 text

@Composable fun Icon( bitmap: ImageBitmap, // no modi fi tint: Color = Color.Black ) @Composable fun Icon( bitmap: ImageBitmap, tint: Color = Color.Black, // 1: modi fi // 2: padding will be lost as soon as the user sets its own modi fi modi fi ) @Composable fun CheckboxRow( checked: Boolean, onCheckedChange: (Boolean) -> Unit, // DON'T - modi fi // the CheckboxRow itself, not its subparts. Make them slots instead rowModi fi checkboxModi fi ) @Composable fun IconButton( buttonBitmap: ImageBitmap, modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { Box(Modi fi er.padding(16.dp)) { Icon( buttonBitmap, // modi fi er should be applied to the outer-most layout // and be the fi rst one in the chain modi fi er = Modi fi er.aspectRatio(1f).then(modi fi er), tint = tint ) } }

Slide 56

Slide 56 text

@Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi rst optional parameter, single of its kind modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.padding(16.dp)) { Icon(buttonBitmap, modi fi er = Modi fi er.aspectRatio(1f), tint = tint) } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi ers modi fi er: Modi fi er, color: Color = Color.White, ... ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.background(color)) { ... } }

Slide 57

Slide 57 text

@Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi rst optional parameter, single of its kind modi fi er: Modi fi er = Modi fi er, tint: Color = Color.Black ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.padding(16.dp)) { Icon(buttonBitmap, modi fi er = Modi fi er.aspectRatio(1f), tint = tint) } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi modi fi color: Color = Color.White, ... ) { // good: applied before other modi fi Box(modi fi ... } }

Slide 58

Slide 58 text

@Composable fun IconButton( buttonBitmap: ImageBitmap, // good: fi modi fi tint: Color = Color.Black ) { // good: applied before other modi fi Box(modi fi Icon(buttonBitmap, modi fi } } @Composable fun ColoredCanvas( // ok: canvas has no intrinsic size, asking for size modi fi ers modi fi er: Modi fi er, color: Color = Color.White, ... ) { // good: applied before other modi fi ers to the outer layout Box(modi fi er.background(color)) { ... } }

Slide 59

Slide 59 text

Modifier - Optional ౵ۄ޷ఠ੄ ୐ ߣ૩ ࣽࢲী ਤ஖ - ইޖ۠ ബҗо ੸ਊغ૑ ঋ਷ Modifier ӝࠄчਸ о૗ - ೞա੄ ஹನք౟ח ױ ೞա੄ Modifier݅ਸ о૗ - ৻ࠗীࢲ ೠ ஹನք౟੄ ز੘җ ഋకܳ ߸҃ೞӝ ਤೠ ݾ੸ - ஹನք౟ ղࠗ੄ ׮ܲ ஹನք౟ܳ ࣻ੿ೞӝ ਤ೧ࢲ Slot ࢎਊ - Composable ࠶۾ীࢲ о੢ ߄Ӵীࢲ ഐ୹غח ஹನք౟ী Modifier ೡ׼

Slide 60

Slide 60 text

౵ۄ޷ఠ ࢶ঱ ࣽࢲ

Slide 61

Slide 61 text

౵ۄ޷ఠ ࢶ঱ ࣽࢲ - ೙ࣻ ౵ۄ޷ఠ - Modifier - Optional ౵ۄ޷ఠ - @Composable ೣࣻ - content ١

Slide 62

Slide 62 text

ठ܃

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

@Composable fun Button( onClick: () -> Unit, text: String? = null, icon: ImageBitmap? = null ) {}

Slide 65

Slide 65 text

@Composable fun Button( onClick: () -> Unit, text: String? = null, icon: ImageBitmap? = null ) {} @Composable fun Button( onClick: () -> Unit, text: @Composable () -> Unit, icon: @Composable () -> Unit ) {}

Slide 66

Slide 66 text

ठ܃ - ೠ ஹನք౟੄ ஹನք౟ ҅க ಴അਸ ਤೠ ۈ׮ ౵ۄ޷ఠ - ഐ୹ೞח ଃীࢲ ੗ਬ܂ѱ ழझథ оמ

Slide 67

Slide 67 text

Stateless ૑ೱ

Slide 68

Slide 68 text

@Composable fun Checkbox( initialValue: Boolean, onChecked: (Boolean) -> Unit ) { var checkedState by remember { mutableStateOf(initialValue) } // ... // Usage: (Checkbox owns the checked state, caller noti fi ed of changes) // Caller cannot easily implement a validation policy. Checkbox(false, onToggled = { callerCheckedState = it })

Slide 69

Slide 69 text

@Composable fun Checkbox( isChecked: Boolean, onToggle: () -> Unit ) { // ... // Usage: (caller mutates optIn and owns the source of truth) Checkbox( myState.optIn, onToggle = { myState.optIn = !myState.optIn } ) @Composable fun Checkbox( initialValue: Boolean, onChecked: (Boolean) -> Unit ) { var checkedState by remember { mutableStateOf(initialValue) } // ... // Usage: (Checkbox owns the checked state, caller noti fi // Caller cannot easily implement a validation policy. Checkbox(false, onToggled = { callerCheckedState = it })

Slide 70

Slide 70 text

Stateless ૑ೱ - SSOT(Single Source of Truth) - ࢚ਤ ஹನք౟۽ ࢚క ਤ੐ - mutableStateOf()۽ State ࢎਊ - ೞਤ ஹನք౟ীࢲ ࢚క ҙ଴ ߂ UI সؘ੉౟ - ࢚క ୶੸ী ਊ੉

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

No content