Slide 1

Slide 1 text

:VLJ"O[BJ !ZBO[N %SPJE,BJHJ ϓϩμΫτϨϕϧͰඞཁʹͳΔ +FUQBDL$PNQPTFςΫχοΫ

Slide 2

Slide 2 text

:VLJ"O[BJ w (PPHMF%FWFMPQFS&YQFSUGPS"OESPJE w UXJUUFS!ZBO[N w CMPHZBO[NCMPHTQPUDPN w גࣜձࣾ΢ϑΟΧ

Slide 3

Slide 3 text

͔ΒGVMM+FUQBDL$PNQPTFͰ։ൃ IUUQTCKQ

Slide 4

Slide 4 text

໨࣍ w ൚༻తͳςΫχοΫ w ը໘ભҠΛͲ͏͢Δ͔/BWJHBUJPO$PNQPTFͱ7JFX.PEFM w ཉ͍͠$PNQPTBCMF΍ػೳͷ୳͠ํ w ࡉ͔͍5JQT w ͍Ζ͍Ζ঺հ͠·͢

Slide 5

Slide 5 text

ը໘ભҠΛͲ͏͢Δ͔

Slide 6

Slide 6 text

ը໘ભҠΛͲ͏͢Δʁ ը໘ભҠ DPNQPTFTBNQMFT ෳ਺"DUJWJUZ "DUJWJUZભҠ 'SBHNFOUࠩ͠ସ͑ $SBOF "DUJWJUZ 'SBHNFOUࠩ͠ସ͑ +FUDIBU +FUTVSWFZ "DUJWJUZ $PNQPTF +FU/FXT +FUDBTUFS 3BMMZ 0XM

Slide 7

Slide 7 text

ը໘ભҠͲ͏͢Δʁ ը໘ભҠ DPNQPTFTBNQMFT ෳ਺"DUJWJUZ "DUJWJUZભҠ 'SBHNFOUࠩ͠ସ͑ $SBOF "DUJWJUZ 'SBHNFOUࠩ͠ସ͑ +FUDIBU +FUTVSWFZ "DUJWJUZ $PNQPTF +FU/FXT +FUDBTUFS 3BMMZ 0XM

Slide 8

Slide 8 text

ը໘ભҠͲ͏͢Δʁ ը໘ભҠ DPNQPTFTBNQMFT ෳ਺"DUJWJUZ "DUJWJUZભҠ 'SBHNFOUࠩ͠ସ͑ $SBOF "DUJWJUZ 'SBHNFOUࠩ͠ସ͑ +FUDIBU +FUTVSWFZ "DUJWJUZ $PNQPTF /BWJHBUJPO$PNQPTF +FU/FXT +FUDBTUFS 3BMMZ 0XM ͦͷଞ

Slide 9

Slide 9 text

ը໘ભҠͲ͏͢Δʁ ը໘ભҠ DPNQPTFTBNQMFT ෳ਺"DUJWJUZ "DUJWJUZભҠ 'SBHNFOUࠩ͠ସ͑ $SBOF "DUJWJUZ 'SBHNFOUࠩ͠ସ͑ +FUDIBU +FUTVSWFZ "DUJWJUZ $PNQPTF /BWJHBUJPO$PNQPTF +FU/FXT +FUDBTUFS 3BMMZ 0XM ͦͷଞ

Slide 10

Slide 10 text

"DUJWJUZ௚Լͷ7JFX.PEFMͷϥΠϑαΠΫϧ "DUJWJUZ 4DSFFO" 4DSFFO# 4DSFFO" 4DSFFO# 4DSFFO" 7JFX.PEFM 4DSFFO# 7JFX.PEFM 4DSFFO" @Composable fun ScreenA( viewModel:ScreenAViewModel = viewModel() ) { … } @Composable fun ScreenB( viewModel:ScreenBViewModel = viewModel() ) { … }

Slide 11

Slide 11 text

/BWJHBUJPO$PNQPTFͰͷ7JFX.PEFMͷϥΠϑαΠΫϧ 4DSFFO" 4DSFFO# 4DSFFO" 4DSFFO# 4DSFFO" 7JFX.PEFM 4DSFFO# 7JFX.PEFM 4DSFFO" /BW)PTU "DUJWJUZ 4DSFFO# 7JFX.PEFM NavHost( … ) { composable("ScreenA") { ScreenA( … ) } composable("ScreenB") { ScreenB( … ) } }

Slide 12

Slide 12 text

ཉ͍͠$PNQPTBCMF΍ػೳͷ ୳͠ํ

Slide 13

Slide 13 text

SFGFSFODFΛΈΔ w BOESPJEYDPNQPTFͷ5PQMFWFM GVODUJPOTTVNNBSZ w BOESPJEYDPNQPTFNBUFSJBMͷ $PNQPOFOUT IUUQTEFWFMPQFSBOESPJEDPNSFGFSFODFLPUMJOBOESPJEYDPNQPTFNBUFSJBMQBDLBHFTVNNBSZDPNQPOFOUT

Slide 14

Slide 14 text

BDDPNQBOJTUʹͳ͍͔ௐ΂Δ w IUUQTHJUIVCDPNHPPHMFBDDPNQBOJTU w +FUQBDL$PNQPTFͷศརϥΠϒϥϦू w 4XJQFUP3FGSFTI w 1BHFS w ʜ

Slide 15

Slide 15 text

,PUMJO4MBDLͷDPNQPTFνϟϯωϧͰݕࡧ͢Δ w IUUQTLPUMJOMBOHTMBDLDPN w IUUQTLPUMJOMBOHPSHDPNNVOJUZ͔Βট଴ΛϦΫΤετͰ͖·͢ ΩʔϘʔυ͕දࣔ͞Εͨͱ͖ʹɺϑΥʔΧε͞ Ε͍ͯΔ5FYU'JFME͕ΩʔϘʔυͷԼʹӅΕͳ ͍Α͏ʹࣗಈͰεΫϩʔϧ͢Δํ๏͕͋Δ͔ʁ ˣ TDSPMMGPDVTͰݕࡧ ˣ 3FMPDBUJPO3FRVFTUFSͱ͍͏ͷ͕͋ΔΒ͍͠

Slide 16

Slide 16 text

$PNQPTFͷαϯϓϧίʔυΛಡΉ w $PNQPTFͷػೳ͸αϯϓϧ͕༻ҙ͞Ε͍ͯΔ͜ͱ͕ଟ͍ w ,PUMJOTMBDL΍TUBDLPWFS fl PXͰݟ͚ͭͨػೳͷαϯϓϧ͕ͳ͍͔ "OESPJE$PEF4FBSDIͰௐ΂Δ w IUUQTDTBOESPJEDPNBOESPJEYQMBUGPSNGSBNFXPSLTTVQQPSU BOESPJEYNBJODPNQPTF w

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

+FUQBDL$PNQPTF3PBENBQΛνΣοΫ͢Δ w ࠓޙͷ࣮૷༧ఆ͕ެ։͞Ε͍ͯΔ w ࣗ෼Ͱ࣮૷͢Δ͔ɺ༻ҙ͞ΕΔ·Ͱ଴͔ͭͷ൑அࡐྉʹͳΔ IUUQTEFWFMPQFSBOESPJEDPNKFUQBDLBOESPJEYDPNQPTFSPBENBQ

Slide 19

Slide 19 text

ࣗ෼Ͱ࣮૷͢ΔSEQBSUZMJCSBSZΛ୳͢ w ࣗ෼Ͱ࣮૷͢Δ w άϥϑͱ͔ˠ$BOWBTDPNQPTBCMF .PEJ fi FSESBX99 w ಠࣗͷαΠζɾ഑ஔˠ-BZPVUDPNQPTBCMF .PEJ fi FSMBZPVU w SEQBSUZMJCSBSZ w ྫʣඇಉظը૾ಡΈࠐΈˠDPJM

Slide 20

Slide 20 text

/BWJHBUJPO$PNQPTFؔ܎ͷ ςΫχοΫ

Slide 21

Slide 21 text

ભҠઌͷࢦఆΛUZQFTBGFʹ͍ͨ͠ NavHost ( … ) { … composable("item/{id}") { val arguments = requireNotNull(it.arguments ) val itemId = ItemId(requireNotNull(arguments.getString("id")) ) ItemDetailScreen ( … ) } } navController.navigate("item/${item.id.value}") /BWJHBUJPO$PNQPTFͰ͸ભҠઌΛจࣈྻͰࢦఆ͢Δ Ͳ͔͜Ͱ

Slide 22

Slide 22 text

ભҠઌͷࢦఆΛUZQFTBGFʹ͍ͨ͠ NavHost ( … ) { … composable("item/{id}") { val arguments = requireNotNull(it.arguments ) val itemId = ItemId(requireNotNull(arguments.getString("id")) ) ItemDetailScreen ( … ) } } navController.navigate("item/${item.id.value}") /BWJHBUJPO$PNQPTFͰ͸ભҠઌΛจࣈྻͰࢦఆ͢Δ Ͳ͔͜Ͱ ભҠઌͷจࣈྻΛͭ͘Δͱ͖ʹؒҧ͑ͦ͏ JEͷܕΛ੍ݶͰ͖ͳ͍

Slide 23

Slide 23 text

fun NavHostController.navigateToItemDetail(id: ItemId) { navigate("item/${id.value}" ) } fun NavGraphBuilder.itemDetailNavGraph ( navController: NavHostControlle r ) { composable("item/{id}") { val arguments = requireNotNull(it.arguments ) val itemId = ItemId(requireNotNull(arguments.getString("id")) ) ItemDetailScreen ( // .. . ) } } ભҠઌͷࢦఆΛUZQFTBGFʹ͍ͨ͠ /BW)PTUͱ͸ผͷϑΝΠϧͰ ભҠઌͷจࣈྻͷੜ੒ॲཧΛભҠઌͷఆٛͱಉ͡ͱ͜Ζʹॻ͘ ͜ͷϝιουܦ༝ͰભҠ͢ΔͷͰ ભҠઌͷจࣈྻΛؒҧ͑ͳ͍ JEͷܕΛ੍ݶͰ͖Δ

Slide 24

Slide 24 text

ભҠઌͷࢦఆΛUZQFTBGFʹ͍ͨ͠ NavHost ( … ) { … itemDetailNavGraph(navController ) } Ͳ͔͜Ͱ navController.navigateToItemDetail(itemId) *UFN*Eܕ͔͠౉ͤͳ͍

Slide 25

Slide 25 text

ॳճ͚ͩνϡʔτϦΞϧը໘Λग़͍ͨ͠ OBWJHBUF5P6TFS νϡʔτϦΞϧ ະදࣔ νϡʔτϦΞϧදࣔࡁΈ νϡʔτϦΞϧ ը໘ Ϣʔβʔ ը໘

Slide 26

Slide 26 text

ॳճ͚ͩνϡʔτϦΞϧը໘Λग़͍ͨ͠ OBWJHBUF5P6TFS νϡʔτϦΞϧ ະදࣔ νϡʔτϦΞϧදࣔࡁΈ લͷը໘ લͷը໘ νϡʔτϦΞϧ ը໘ લͷը໘ Ϣʔβʔ ը໘

Slide 27

Slide 27 text

ॳճ͚ͩνϡʔτϦΞϧը໘Λग़͍ͨ͠ OBWJHBUF5P6TFS νϡʔτϦΞϧ ະදࣔ νϡʔτϦΞϧදࣔࡁΈ લͷը໘ લͷը໘ νϡʔτϦΞϧ ը໘ લͷը໘ Ϣʔβʔ ը໘ ͜͜ΛͲ͏͢Δ͔

Slide 28

Slide 28 text

ॳճ͚ͩνϡʔτϦΞϧը໘Λग़͍ͨ͠ OBWJHBUF5P6TFS νϡʔτϦΞϧ ະදࣔ νϡʔτϦΞϧදࣔࡁΈ લͷը໘ લͷը໘ νϡʔτϦΞϧ ը໘ લͷը໘ Ϣʔβʔ ը໘ લͷը໘ ൑ఆ༻ը໘

Slide 29

Slide 29 text

ॳճ͚ͩνϡʔτϦΞϧը໘Λग़͍ͨ͠ OBWJHBUF5P6TFS νϡʔτϦΞϧ ະදࣔ νϡʔτϦΞϧදࣔࡁΈ લͷը໘ νϡʔτϦΞϧ ը໘ Ϣʔβʔ ը໘ લͷը໘ ൑ఆ༻ը໘ OFTUFEHSBQI

Slide 30

Slide 30 text

fun NavGraphBuilder.userNavGraph( navController: NavHostController ) { navigation( route = "user", startDestination = "user/top" ) { composable("user/top") { LaunchedEffect(Unit) { val isTutorialShown = … if (isTutorialShown) { navController.navigate("user/main") { popUpTo("user") } } else { navController.navigate("user/tutorial") { popUpTo("user") } } } } composable("user/tutorial") { … } composable("user/main") { … } OFTUFEHSBQI OBWJHBUF lVTFSz ͕ݺ͹ΕΔͱlVTFSUPQz൑ఆ༻ը໘͕ දࣔ͞ΕΔ ൑ఆ༻ը໘͔ΒνϡʔτϦΞϧը໘΍ Ϣʔβʔը໘ʹߦ͘ͱ͖ʹ QPQ6Q5P lVTFSz ͢Δ͜ͱͰ ൑ఆ༻ը໘͕όοΫελοΫ͔Β QPQ͞ΕΔ

Slide 31

Slide 31 text

ެࣜυΩϡϝϯτʹ ʢ͋·Γʣॻ͍ͯͳ͍5JQT

Slide 32

Slide 32 text

.BUFSJBM5IFNFؔ܎

Slide 33

Slide 33 text

จࣈ৭͕൒ಁ໌ʹͳΒͳ͍Α͏ʹ͍ͨ͠ w എܠ্ͷจࣈ৭΍ΞΠίϯͷ৭͸গ͠ಁ໌ʹͳ͍ͬͯΔ MaterialTheme { Surface(color = MaterialTheme.colors.background) { // contentColor ͸ MaterialTheme.colors.onBackground (= Color.Black ) Column { // Color.Black.copy(alpha = 0.87f) ~= #21212 1 Text("Jetpack" ) // Color.Blac k Text(“Compose", color = MaterialTheme.colors.onBackground ) } } }

Slide 34

Slide 34 text

-PDBM$POUFOU"MQIB͕ద༻͞Ε͍ͯΔ w .BUFSJBM5IFNFͰ-PDBM$POUFOU"MQIBʹ$POUFOU"MQIBIJHI͕ηοτ͞ Ε͍ͯΔ

Slide 35

Slide 35 text

-PDBM$POUFOU"MQIB͕ద༻͞Ε͍ͯΔ w .BUFSJBM5IFNFͰ-PDBM$POUFOU"MQIBʹ$POUFOU"MQIBIJHI͕ηοτ͞ Ε͍ͯΔ w $POUFOU"MQIBʹ͸IJHI NFEJVN EJTBCMFE͕͋Δ w $POUFOU"MQIBͷ֤஋͸-PDBM$POUFOU$PMPSͷMVNJOBODFʢ໌Δ͞ʣͱ UIFNF MJHIUPSEBSL Ͱܾ·Δ

Slide 36

Slide 36 text

UIFNF -PDBM$POUFOU$PMPS $POUFOU"MQIB IJHI NFEJVN EJTBCMFE MJHIU MVNJOBODF G G G MVNJOBODF G G G EBSL MVNJOBODF G G G MVNJOBODF G G G

Slide 37

Slide 37 text

MaterialTheme { Surface(color = MaterialTheme.colors.background) { // contentColor ͸ MaterialTheme.colors.onBackground (= Color.Black ) Column { // Color.Black.copy(alpha = 0.87f) ~= #21212 1 Text("Jetpack" ) // Color.Blac k Text(“Compose", color = MaterialTheme.colors.onBackground ) } } } ྫʣMJHIUUIFNFͰDPOUFOU$PMPS͕ࠇʢ$PMPS#MBDLʣͷ৔߹ɺMVNJOBODF͸ҎԼ ˠ4VSGBDF಺ͷจࣈʢ5FYUʣ΍ΞΠίϯʢ*DPOʣ͸গ͠ʢGʣಁ໌ʹͳΔ

Slide 38

Slide 38 text

MaterialTheme { CompositionLocalProvider ( LocalContentAlpha provides 1f , ) { Surface(color = MaterialTheme.colors.background) { Column { // Color.Blac k Text("Jetpack" ) // Color.Blac k Text("Compose", color = MaterialTheme.colors.onBackground ) } } } } $PNQPTJUJPO-PDBM1SPWJEFSͰ-PDBM$POUFOU"MQIBΛ্ॻ͖͢Δͱෆಁ໌ʹͰ͖Δ

Slide 39

Slide 39 text

@Composabl e fun MyAppTheme(…) { MaterialTheme ( … ) { CompositionLocalProvider ( LocalContentAlpha provides 1f , ) { content( ) } } } ΞϓϦͷςʔϚʹ૊ΈࠐΉͷ΋͋Γ ͨͩ͠ɺಛఆͷ$PNQPTBCMF಺Ͱ -PDBM$POUFOU"MQIBʹ໌ࣔతʹ $POUFOU"MQIBIJHIͳͲΛࢦఆ͍ͯ͠Δ΋ ͷʢྫ5PQ"QQ#BSʣ͕͋ΔͷͰɺશͯͰ ஔ͖׵͑ΒΕΔΘ͚Ͱ͸ͳ͍ MyAppTheme { Surface(color = MaterialTheme.colors.background) { Column { // LocalContentAlpha ͸ 1 f Text("Hello" ) } } }

Slide 40

Slide 40 text

5PQ"QQ#BSͷBDUJPOTΛUJUMFͱಉ͡ಁ໌౓ʹ͍ͨ͠ OBWJHBUJPO*DPO΍UJUMFʹൺ΂ͯബ͍ ͳΜͰʁ

Slide 41

Slide 41 text

5PQ"QQ#BSͷBDUJPOTΛUJUMFͱಉ͡ಁ໌౓ʹ͍ͨ͠ @Composabl e fun TopAppBar ( … ) { AppBar ( … ) { … Row ( … ) { ProvideTextStyle(value = MaterialTheme.typography.h6) { CompositionLocalProvider ( LocalContentAlpha provides ContentAlpha.high , content = titl e ) } } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Row ( Modifier.fillMaxHeight() , horizontalArrangement = Arrangement.End , verticalAlignment = Alignment.CenterVertically , content = action s ) } OBWJHBUJPO*DPO΍UJUMF͸ $POUFOU"MQIBIJHI BDUJPOT͸ $POUFOU"MQIBNFEJVN

Slide 42

Slide 42 text

5PQ"QQ#BSͷBDUJPOTΛUJUMFͱಉ͡ಁ໌౓ʹ͍ͨ͠ TopAppBar ( title = { Text("AppBarSample") } , navigationIcon = { IconButton(onClick = { }) { Icon ( Icons.Default.ArrowBack , contentDescription = "back" , ) } } , actions = { CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) { IconButton(onClick = { }) { Icon ( Icons.Default.Share , contentDescription = "share" , ) } } } ) $POUFOU"MQIBIJHI ʹࠩ͠ସ͑Δ

Slide 43

Slide 43 text

3JQQMFؔ܎

Slide 44

Slide 44 text

TFMFDUBCMF*UFN#BDLHSPVOE#PSEFSMFTT w SFNFNCFS3JQQMFͰCPVOEFEʹGBMTFΛࢦఆͨ͠ ΋ͷΛ.PEJ fi FSDMJDLBCMFͷJOEJDBUJPOʹࢦఆ͢Δ Modifier.clickable ( interactionSource = remember { MutableInteractionSource() } , indication = rememberRipple(bounded = false, radius = 40.dp ) ) { }

Slide 45

Slide 45 text

3JQQMFͷ৭Λม͍͑ͨ w SFNFNCFS3JQQMFͰDPMPSΛࢦఆͨ͠΋ͷΛ .PEJ fi FSDMJDLBCMFͷJOEJDBUJPOʹࢦఆ͢Δ Modifier.clickable ( interactionSource = remember { MutableInteractionSource() } , indication = rememberRipple(color = MaterialTheme.colors.secondary ) ) { }

Slide 46

Slide 46 text

w $PNQPTJUJPO-PDBM1SPWJEFSͰ-PDBM3JQQMF5IFNF Λࠩ͠ସ͑Δ 3JQQMFͷBMQIB΋ม͍͑ͨ private val MyRippleTheme = object : RippleTheme { @Composabl e override fun defaultColor(): Color { return MaterialTheme.colors.secondar y } @Composabl e override fun rippleAlpha(): RippleAlpha { return RippleAlpha(… ) } } @Composabl e fun Sample() { CompositionLocalProvider(LocalRippleTheme provides MyRippleTheme) { … } }

Slide 47

Slide 47 text

3JQQMFͷσϑΥϧτͷ৭ͱBMQIB UIFNF -PDBM$POUFOU$PMPS DPMPS BMQIB MJHIU MVNJOBODF -PDBM$POUFOU$PMPS -JHIU5IFNF)JHI$POUSBTU3JQQMF"MQIB MVNJOBODF -JHIU5IFNF-PX$POUSBTU3JQQMF"MQIB EBSL MVNJOBODF $PMPS8IJUF %BSL5IFNF3JQQMF"MQIB MVNJOBODF -PDBM$POUFOU$PMPS

Slide 48

Slide 48 text

$PNQPTBCMFؔ܎

Slide 49

Slide 49 text

Լʹ͋Δཁૉ͕λοϓΛरΘͳ͍Α͏ʹ͍ͨ͠ w ॏͶ͚ͨͩͩͱλοϓ͸Լʹൈ͚Δ Box(contentAlignment = Alignment.Center) { Button(onClick = { /*TODO*/ }) { Text("Button" ) } Box ( modifier = Modifie r .size(200.dp ) .background(color = Color.Red.copy(alpha = 0.3f) ) ) { … } }

Slide 50

Slide 50 text

Լʹ͋Δཁૉ͕λοϓΛरΘͳ͍Α͏ʹ͍ͨ͠ w .PEJ fi FSQPJOUFS*OQVU ͰλοϓΛर͏ Box(contentAlignment = Alignment.Center) { Button(onClick = { /*TODO*/ }) { Text("Button" ) } Box ( modifier = Modifie r .size(200.dp ) .background(color = Color.Red.copy(alpha = 0.3f) ) .pointerInput(Unit) { } ) { … } }

Slide 51

Slide 51 text

Լʹ͋Δཁૉ͕λοϓΛरΘͳ͍Α͏ʹ͍ͨ͠ w .PEJ fi FSQPJOUFS*OQVU ͰλοϓΛर͏ w ·ͨ͸ɺ্ʹ͘ΔཁૉΛ4VSGBDFͰғΉ @Composabl e fun Surface ( … ) { Surface ( … clickAndSemanticsModifier = Modifie r .semantics(mergeDescendants = false) { } .pointerInput(Unit) { } ) }

Slide 52

Slide 52 text

Լʹ͋Δཁૉ͕λοϓΛरΘͳ͍Α͏ʹ͍ͨ͠ w .PEJ fi FSQPJOUFS*OQVU ͰλοϓΛर͏ w ·ͨ͸ɺ্ʹ͘ΔཁૉΛ4VSGBDFͰғΉ Box(contentAlignment = Alignment.Center) { Button(onClick = { /*TODO*/ }) { Text("Button" ) } Surface ( color = Color.Red.copy(alpha = 0.3f) , modifier = Modifie r .size(200.dp ) ) { … } }

Slide 53

Slide 53 text

ԣฒͼͷ5FYUΛCBTFMJOFͰἧ͍͑ͨ w 3PX4DPQFͷ.PEJ fi FSBMJHO#Z#BTFMJOF Λ࢖͏ Row { Text ( text = "Hello" , modifier = Modifier.alignByBaseline( ) ) Text ( text = "Jetpack" , fontSize = 32.sp , modifier = Modifier.alignByBaseline( ) ) Text ( text = "Compose" , fontSize = 20.sp , modifier = Modifier.alignByBaseline( ) ) }

Slide 54

Slide 54 text

4FMFDUJPO$POUBJOFSͷબ୒࣌ͷ৭Λม͍͑ͨ w .BUFSJBM5IFNFͰDPMPSTͷQSJNBSZΛ্ॻ͖͢Δ val primary = MaterialTheme.colors.primar y val secondary = MaterialTheme.colors.secondar y MaterialTheme ( colors = MaterialTheme.colors.copy(primary = secondary ) ) { SelectionContainer { Text ( text = "Hello DroidKaigi" , fontSize = 32.sp , color = primar y ) } } બ୒จࣈͷCBDLHSPVOEDPMPSͷBMQIB஋Λ QSJNBSZͷ৭ʹԠ͍͍ͯ͡ײ͡ʹܭࢉͯ͘͠ΕΔ

Slide 55

Slide 55 text

4FMFDUJPO$POUBJOFSͷબ୒࣌ͷ৭Λม͍͑ͨ w -PDBM5FYU4FMFDUJPO$PMPSTΛࠩ͠ସ͑Δ val textSelectionColors = TextSelectionColors ( handleColor = MaterialTheme.colors.secondary , backgroundColor = MaterialTheme.colors.secondary.copy(alpha = 0.2f ) ) CompositionLocalProvider(LocalTextSelectionColors provides textSelectionColors) { SelectionContainer { Text ( text = "Hello DroidKaigi" , fontSize = 32.sp , color = MaterialTheme.colors.primar y ) } } બ୒จࣈͷCBDLHSPVOEDPMPSͷBMQIB஋΋ ࣗ෼Ͱௐ੔͢Δ

Slide 56

Slide 56 text

4QJOOFSͬͯͳ͍ͷʁˠ%SPQEPXO.FOV var selected by remember { mutableStateOf("not selected") } var expanded by remember { mutableStateOf(false) } OutlinedButton(onClick = { expanded = true }) { … } DropdownMenu ( expanded = expanded , onDismissRequest = { expanded = false } ) { DropdownMenuItem ( onClick = { selected = "Cupcake " expanded = fals e } ) { Text("Cupcake" ) } … }

Slide 57

Slide 57 text

0WFSGMPXNFOVͬͯͳ͍ͷʁˠ%SPQEPXO.FOV Scaffold ( topBar = { TopAppBar ( … , actions = { var expanded by remember { mutableStateOf(false) } IconButton(onClick = { expanded = true }) { Icon ( Icons.Default.MoreVert , contentDescription = "more" , ) } DropdownMenu ( expanded = expanded , onDismissRequest = { expanded = false } ) { DropdownMenuItem(… ) … } } ) }

Slide 58

Slide 58 text

ಠࣗσβΠϯͷ5FYU'JFMEΛ࡞Γ͍ͨ w #BTJD5FYU'JFMEΛ࢖͏ BasicTextField ( value = value , onValueChange = onValueChange , modifier = modifier , decorationBox = { Box ( modifier = Modifie r .background ( Color.LightGray , RoundedCornerShape(4.dp ) ) .padding(16.dp ) ) { it( ) } } )

Slide 59

Slide 59 text

4IBEPXʹ৭Λ͚͍ͭͨ w .PEJ fi FSTIBEPX Ͱ͸৭ΛࢦఆͰ͖ͳ͍ w 1BJOUBT'SBNFXPSL1BJOU ͱTFU4IBEPX-BZFS Λ࢖͏ val modifier = Modifier.drawBehind { drawIntoCanvas { val paint = Paint( ) val frameworkPaint = paint.asFrameworkPaint( ) frameworkPaint.color = paintColo r frameworkPaint.setShadowLayer ( shadowRadius.toPx() , offsetX.toPx() , offsetY.toPx() , shadowColo r ) it.drawRect ( 0f, 0f, size.width, size.height, pain t ) } }

Slide 60

Slide 60 text

%JBMPHؔ܎

Slide 61

Slide 61 text

"MFSU%JBMPHΛग़͍ͨ͠ var showAlertDialog by remember { mutableStateOf(false) } … if (showAlertDialog) { AlertDialog ( title = { Text("Dialog title") } , text = { Text("Dialog body text") } , confirmButton = { TextButton(onClick = { … }) { Text("Accept" ) } } , dismissButton = { TextButton(onClick = { showAlertDialog = false }) { Text("Cancel" ) } } , onDismissRequest = { showAlertDialog = false } ) }

Slide 62

Slide 62 text

೚ҙͷ%JBMPHΛग़͍ͨ͠ Dialog(onDismissRequest = { showDialog = false }) { Surface ( modifier = Modifier , shape = MaterialTheme.shapes.medium , color = MaterialTheme.colors.surface , ) { Column(modifier = Modifier.padding(vertical = 8.dp)) { Text ( text = "ΞϧόϜ͔Βબ୒" , modifier = … ) Text ( text = "ը૾Λ࡟আ" , modifier = … ) } } }

Slide 63

Slide 63 text

%BUF1JDLFS5JNF1JDLFS%JBMPHΛग़͍ͨ͠ Button ( onClick = { DatePickerDialog ( context , { _, year, monthOfYear, dayOfMonth - > … } , date.year , date.month - 1 , date.day , ) .show( ) } ) { Text(date.dateText ) } ৭͸5IFNFͷ DPMPS4FDPOEBSZ

Slide 64

Slide 64 text

%BUF1JDLFS5JNF1JDLFS%JBMPHΛग़͍ͨ͠ @Composabl e fun DatePickerDialog ( onDismissRequest: () -> Unit , date: MyDate , onDateChange: (MyDate) -> Unit , ) { Dialog(onDismissRequest = onDismissRequest) { Surface(shape = MaterialTheme.shapes.medium) { Column { var editDate by remember { mutableStateOf(date) } AndroidView(factory = { DatePicker(it).apply { updateDate(editDate.year, … ) setOnDateChangedListener { … - > editDate = … } } }) … } } }

Slide 65

Slide 65 text

ϨΠΞ΢τؔ܎

Slide 66

Slide 66 text

#PUUPN4IFFUΛग़͍ͨ͠ w NBUFSJBMJPDPNQPOFOUTTIFFUTCPUUPNͷछྨʹରԠͨ͠$PNQPTBCMF w 4UBOEBSECPUUPNTIFFUˠ#PUUPN4IFFU4DB ff PME w .PEBMCPUUPNTIFFUˠ.PEBM#PUUPN4IFFU-BZPVU w #PUUPN%SBXFS͸#PUUPN"QQ#BSͰ࢖͏ w NBUFSJBMJPDPNQPOFOUTOBWJHBUJPOESBXFSCPUUPNESBXFS

Slide 67

Slide 67 text

.PEBM#PUUPN4IFFU-BZPVU ModalBottomSheetLayout ( sheetState = sheetState , sheetContent = { … } , modifier = Modifier.fillMaxSize( ) ) { Scaffold { … } } EJN͞ΕΔͷ͸DPOUFOUʹࢦఆͨ͠$PNQPTBCMF෦෼͚ͩ TUBUVT#BS΋EJN͢ΔͳΒ WindowCompat.setDecorFitsSystemWindows(window, false )

Slide 68

Slide 68 text

TUBUVTCBS OBWJHBUJPOCBSͷαΠζΛ࢖͍͍ͨ w BDDPNQBOJTUͷJOTFUTΛ࢖͏ w HPPHMFHJUIVCJPBDDPNQBOJTUJOTFUT w QBEEJOH.PEJ fi FSͰऔಘ w .PEJ fi FSTZTUFN#BST1BEEJOH .PEJ fi FSTUBUVT#BST1BEEJOH .PEJ fi FSOBWJHBUJPO#BST1BEEJOH w IFJHIUͰऔಘ w .PEJ fi FSTUBUVT#BS)FJHIU .PEJ fi FSOBWJHBUJPO#BST)FJHIU .PEJ fi FSOBWJHBUJPO#BST8JEUI w 1BEEJOH7BMVFTͰऔಘ w SFNFNCFS*OTFUT1BEEJOH7BMVFT rememberInsetsPaddingValues ( insets = LocalWindowInsets.current.navigationBars )

Slide 69

Slide 69 text

$PMMBQTJOH5PPMCBS͕΄͍͠ʢ$PMVNOʣ Box(modifier = Modifier.fillMaxSize()) { val scrollState = rememberScrollState( ) Column(modifier = Modifier.verticalScroll(scrollState)) { Spacer(modifier = Modifier.height(expandedHeight) ) … } Surface ( … modifier = Modifie r .height(expandedHeight ) .offset { val y = (-scrollState.value ) .coerceIn(-(expandedHeight - appBarHeight).roundToPx(), 0) IntOffset(0, y ) } ) { Box(modifier = Modifier.fillMaxSize()) { TopAppBar(…) } } }

Slide 70

Slide 70 text

$PMMBQTJOH5PPMCBS͕΄͍͠ʢ-B[Z$PMVNOʣ Box(modifier = Modifier.fillMaxSize()) { val listState = rememberLazyListState( ) LazyColumn(state = listState) { item { Spacer(modifier = Modifier.height(expandedHeight) ) } … } Surface ( … modifier = Modifie r .height(expandedHeight ) .offset { val scrollValue = if (listState.firstVisibleItemIndex == 0) { listState.firstVisibleItemScrollOffse t } else { Int.MAX_VALU E } val y = (-scrollValue ) .coerceIn(-(expandedHeight - appBarHeight).roundToPx(), 0 ) IntOffset(0, y ) } {

Slide 71

Slide 71 text

ԼʹεΫϩʔϧͨ͠ͱ͖ʹग़ͯ͘Δ5PPMCBS͕΄͍͠ val toolbarOffsetY = remember { mutableStateOf(0f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val newOffset = toolbarOffsetY.value + available.y toolbarOffsetY.value = newOffset.coerceIn(-toolbarHeightPx, 0f ) return Offset.Zer o } } } Box(modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection)) { LazyColumn(contentPadding = PaddingValues(top = toolbarHeight)) { … } TopAppBar ( title = { Text("NestedScrollConnectionSample") } , modifier = Modifie r .height(toolbarHeight ) .offset { IntOffset(x = 0, y = toolbarOffsetY.value.roundToInt()) } ) }

Slide 72

Slide 72 text

ԼʹεΫϩʔϧͨ͠ͱ͖ʹग़ͯ͘Δ5PPMCBS͕΄͍͠ val toolbarOffsetY = remember { mutableStateOf(0f) } val nestedScrollConnection = remember { object : NestedScrollConnection { override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { val newOffset = toolbarOffsetY.value + available.y toolbarOffsetY.value = newOffset.coerceIn(-toolbarHeightPx, 0f ) return available } } } Box(modifier = Modifier.fillMaxSize().nestedScroll(nestedScrollConnection)) { LazyColumn(contentPadding = PaddingValues(top = toolbarHeight)) { … } TopAppBar ( title = { Text("NestedScrollConnectionSample") } , modifier = Modifie r .height(toolbarHeight ) .offset { IntOffset(x = 0, y = toolbarOffsetY.value.roundToInt()) } ) } εΫϩʔϧΛ͜͜ͰফඅͰ͖Δ

Slide 73

Slide 73 text

GMFYCPYMBZPVU͕΄͍͠ w ຊମʹੲ͋ͬͨ'MPX$PMVNOͱ'MPX3PX͸EFQSFDBUFEʹͳͬͨ w BDDPNQBOJTUͷ fl PXMBZPVUΛ࢖͏ w HPPHMFHJUIVCJPBDDPNQBOJTU fl PXMBZPVU FlowRow ( mainAxisSpacing = 8.dp , modifier = Modifier.fillMaxWidth( ) ) { repeat(10) { Button(onClick = { /*TODO*/ }) { Text("Button $it" ) } } }

Slide 74

Slide 74 text

ͦͷଞ

Slide 75

Slide 75 text

ը໘දࣔ࣌ʹΩʔϘʔυΛग़͍ͨ͠ w 'PDVT3FRVFTUFSΛ࢖͏ val focusRequester = remember { FocusRequester() } TextField ( … modifier = Modifie r .focusOrder(focusRequester) // or .focusRequester(focusRequester ) ) LaunchedEffect(Unit) { focusRequester.requestFocus( ) }

Slide 76

Slide 76 text

ΩʔϘʔυΛ։ด͍ͨ͠ʢࠓ·Ͱʣ w 5FYU*OQVU4FSWJDFΛ࢖͏ w 5FYU'JFMEͳͲςΩετೖྗΛड͚औΕΔDPNQPTBCMF ͕ϑΥʔΧε͞Ε͍ͯͳ͍ͱΩʔϘʔυ͸ग़ͳ͍ val textInputService = LocalTextInputService.curren t Button(onClick = { textInputService?.showSoftwareKeyboard( ) }) { … } Button(onClick = { textInputService?.hideSoftwareKeyboard( ) }) { … } !*OUFSOBM5FYU"1*͕͍ͭͯ %FQSFDBUFEʹͳΔ༧ఆ IUUQTJTTVFUSBDLFSHPPHMFDPNJTTVFT

Slide 77

Slide 77 text

ΩʔϘʔυΛ։ด͍ͨ͠ʢ͜Ε͔Βʣ w -PDBM4PGUXBSF,FZCPBSE$POUSPMMFSΛ࢖͏ w 5FYU'JFMEͳͲςΩετೖྗΛड͚औΕΔDPNQPTBCMF ͕ϑΥʔΧε͞Ε͍ͯͳ͍ͱΩʔϘʔυ͸ग़ͳ͍ val keyboardController = LocalSoftwareKeyboardController.curren t Button(onClick = { keyboardController?.show( ) }) { … } Button(onClick = { keyboardController?.hide( ) }) { … }

Slide 78

Slide 78 text

ΤϯλʔΩʔ͕ԡ͞Εͨͱ͖ʹϑΥʔΧεҠಈ͍ͨ͠ val focusRequester1 = remember { FocusRequester() } val focusRequester2 = remember { FocusRequester() } val focusManager = LocalFocusManager.curren t TextField ( … singleLine = true , keyboardActions = KeyboardActions { focusManager.moveFocus(FocusDirection.Next ) } , modifier = Modifie r .focusOrder(focusRequester1) { next = focusRequester 2 down = focusRequester 2 } , ) TextField ( … modifier = Modifie r .focusOrder(focusRequester2 ) )

Slide 79

Slide 79 text

PO3FTVNFͷλΠϛϯάͰॲཧΛ͍ͨ͠ @Composabl e fun DoOnResume(action: () -> Unit) { val currentAction by rememberUpdatedState(action ) val lifecycle = LocalLifecycleOwner.current.lifecycl e val lifecycleObserver = remember { LifecycleEventObserver { _, event - > if (event == Lifecycle.Event.ON_RESUME) { currentAction( ) } } } DisposableEffect(lifecycle, lifecycleObserver) { lifecycle.addObserver(lifecycleObserver ) onDispose { lifecycle.removeObserver(lifecycleObserver ) } } }

Slide 80

Slide 80 text

PO3FTVNFͷλΠϛϯάͰॲཧΛ͍ͨ͠ @Composabl e fun DoOnResume(action: () -> Unit) { val currentAction by rememberUpdatedState(action ) val lifecycle = LocalLifecycleOwner.current.lifecycl e val lifecycleObserver = remember { LifecycleEventObserver { _, event - > if (event == Lifecycle.Event.ON_RESUME) { currentAction( ) } } } DisposableEffect(lifecycle, lifecycleObserver) { lifecycle.addObserver(lifecycleObserver ) onDispose { lifecycle.removeObserver(lifecycleObserver ) } } }

Slide 81

Slide 81 text

$BNFSB9Λ࢖͍͍ͨ w "OESPJE7JFXDPNQPTBCMFΛ࢖ͬͯ1SFWJFX7JFXΛ૊ΈࠐΉ w 1SFWJFX7JFXͷJNQMFNFOUBUJPO.PEFͰ$0.1"5*#-&Λࢦఆ͢Δ AndroidView ( modifier = Modifier.fillMaxSize() , factory = { context - > PreviewView(context).apply { implementationMode = PreviewView.ImplementationMode.COMPATIBL E … } } , update = { … } )

Slide 82

Slide 82 text

7JFX.PEFMؔ܎

Slide 83

Slide 83 text

7JFX.PEFMͷίϯετϥΫλͰJEΛ౉͍ͨ͠ w %BHHFSͷ"TTJTUFE*OKFDUػೳΛ࢖͏ IUUQZBO[NCMPHTQPUDPNKFUQBDLDPNQPTFWJFXNPEFMOBWJHBUJPOIUNM class ItemDetailViewModel @AssistedInject constructor ( @Assisted val id: ItemId , … , ) : ViewModel() { @AssistedFactor y interface Factory { fun create(id: ItemId): ItemDetailViewMode l } @EntryPoin t @InstallIn(ActivityComponent::class ) interface ActivityCreatorEntryPoint { fun getItemDetailViewModelFactory(): Factor y } companion object { {

Slide 84

Slide 84 text

.VUBCMF4UBUFͷঢ়ଶΛ4BWFE4UBUF)BOEMFʹอଘ͍ͨ͠ w 4BWFE4UBUF)BOEMFTFU4BWFE4UBUF1SPWJEFS Λ࢖͏ͱศར @HiltViewMode l class MyViewModel @Inject constructor ( … savedStateHandle: SavedStateHandl e ) : ViewModel() { private val _uiState = mutableStateOf(UiState.Initial ) … init { savedStateHandle.setSavedStateProvider("[KEY]") { _uiState.value.toBundle( ) } savedStateHandle.get("[KEY]")?.let { _uiState.value = it.toUiState( ) }

Slide 85

Slide 85 text

·ͱΊ w ը໘ભҠ΋+FUQBDL$PNQPTFͰ΍Δ৔߹ɺ/BWJHBUJPO$PNQPTFΛ࢖͏ ͷ͕͓͢͢Ί w "OESPJE$PEF4FBSDI DTBOESPJEDPN Ͱ$PNQPTBCMFͷ࣮૷ίʔυ΍ αϯϓϧίʔυΛಡ΋͏ w +FUQBDL$PNQPTF࢖ͬͯΈΑ͏ʂ

Slide 86

Slide 86 text

͓ΘΓ