Slide 1

Slide 1 text

2024೥ͷ φϏήʔγϣϯɾϑΥʔΧεରԠ ComposeͰΩʔϘʔυɾφϏήʔγϣϯΛ αϙʔτ͠Α͏ DroidKaigi 2024 Tiphaine (ςΟϑΣϯ)

Slide 2

Slide 2 text

● AndroidΤϯδχΞ @ DeNA (Pococha) ● DroidKaigiελοϑ ● Mobile Dev JapanӡӦ 2 Tiphaine (ςΟϑΣϯ)

Slide 3

Slide 3 text

3 ࢿྉ & ಈըɿ2021೥ͦ͜ΞΫηγϏϦςΟͱ޲͖߹͓͏ هࣄԽ΍Δ΍Δ࠮ٗ ൓লதͰʔ͢ ࠓ೔͸͜Εͷଓฤ

Slide 4

Slide 4 text

4 01 Introduction to Focus Common Problems 02 Focus in Compose 03

Slide 5

Slide 5 text

01 5 Introduction to Focus

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

8 Focus Navigation

Slide 9

Slide 9 text

9 ● ը໘Λ৮Βͣʹ εϚϗΛૢ࡞͢Δ୅ସํ๏ ● 1ཁૉͣͭҠಈ͢Δ Focus Navigation Focus ཁૉ ࢀߟɿGoogle Play Store

Slide 10

Slide 10 text

Focus Focusʢয఺ʣ 10 ● UIͰͷݱࡏҐஔ ● ΞϓϦͱͷ།Ұͷ઀఺ ● ཁૉͷݟͨ໨ʹมԽ͕ى͖Δ Focus ࢀߟɿGoogle Play Store

Slide 11

Slide 11 text

Focus 11 ● Accessibility Focus ≠ Navigation Focus ● Nav Focus(ུ)͸ʮΩʔϘʔυɾφϏήʔγϣϯʯ ͱ΋ݺ͹ΕΔ ● ͲͪΒ΋Focus NavigationͷҰछ 😵💫 Focusͷछྨ

Slide 12

Slide 12 text

12 Focusͷҧ͍

Slide 13

Slide 13 text

ը໘Λ৮Βͳ͍ 👋 👀 ը໘Λݟͳ͍ 13 Navigation Focus Accessibility Focus

Slide 14

Slide 14 text

ΞΫγϣϯΛىͤ͜ΔཁૉͷΈ ͢΂ͯͷཁૉ Navigation Focus Accessibility Focus 14

Slide 15

Slide 15 text

focusableଐੑ focusableଐੑͱ importantForAccessibilityଐੑ 15 ΞΫγϣϯΛىͤ͜ΔཁૉͷΈ ͢΂ͯͷཁૉ Navigation Focus Accessibility Focus

Slide 16

Slide 16 text

16 ΩʔϘʔυɺεΠονΞΫηεɺ ेࣈΩʔʢ໼ҹΩʔʣͳͲ εΫϦʔϯϦʔμʔ ʢTalkBackͳͲʣ focusableଐੑ focusableଐੑͱ importantForAccessibilityଐੑ ΞΫγϣϯΛىͤ͜ΔཁૉͷΈ ͢΂ͯͷཁૉ Navigation Focus Accessibility Focus

Slide 17

Slide 17 text

17 ● ෺ཧΩʔϘʔυ ʢλϒϨοτɺChromebookʣ ● ιϑτ΢ΣΞɾΩʔϘʔυ ʢΦϯεΫϦʔϯɾΩʔϘʔυʣ ը૾ɿSergi Kabrera Nav FocusΛ࢖͏σόΠε

Slide 18

Slide 18 text

18 ● Android TVͷϦϞίϯ ● εϚʔτ΢Υονͷϕθϧ ● ήʔϜίϯτϩʔϥʔ ● εΠονɾΞΫηε Nav FocusΛ࢖͏σόΠε ը૾ɿErik Mclean

Slide 19

Slide 19 text

19 Πϯδέʔλ͸ΧελϚΠζՄೳ σϑΥϧτͷΠϯδέʔλͷΈ ΩʔϘʔυɺεΠονΞΫηεɺ ेࣈΩʔʢ໼ҹΩʔʣͳͲ εΫϦʔϯϦʔμʔ ʢTalkBackͳͲʣ focusableଐੑ focusableଐੑͱ importantForAccessibilityଐੑ ΞΫγϣϯΛىͤ͜ΔཁૉͷΈ ͢΂ͯͷཁૉ Navigation Focus Accessibility Focus

Slide 20

Slide 20 text

20 ϑΥʔΧεɾΠϯδέʔλ Navigation Focus Accessibility Focus

Slide 21

Slide 21 text

21 ੍࣌ؒݶͷͨΊɺ ࠓ೔͸Nav Focusͷ࿩ͷΈ

Slide 22

Slide 22 text

22 Accessibility Focusͷ࿩͸ 2021೥ͷొஃͰ 💁 ࢿྉ & ಈըɿ2021೥ͦ͜ΞΫηγϏϦςΟͱ޲͖߹͓͏

Slide 23

Slide 23 text

23 (Nav Focusͷ) φϏήʔγϣϯͷछྨ

Slide 24

Slide 24 text

24 ● ेࣈΩʔɺ·ͨ͸໼ҹΩʔ ● 2࣍ݩφϏήʔγϣϯ ● ϑΥʔΧε͸্Լࠨӈʹಈ͚Δ Directional Navigation

Slide 25

Slide 25 text

25 ● TabΩʔɺεΠονɾΞΫηεͳͲ ● 1࣍ݩφϏήʔγϣϯʢ໭Δ/ਐΉʣ ● ϑΥʔΧε͸ϨΠΞ΢τ಺Ͱ ཁૉ͕දࣔ͞ΕΔॱংʹಈ͚Δ Tab Navigation 1 2 3

Slide 26

Slide 26 text

26 Focus Navigation Accessibility Focus Navigation Focus Directional Navigation Tab Navigation

Slide 27

Slide 27 text

01 27 Introduction to Focus Common Problems Focus in Compose 02 03 Introduction to Focus

Slide 28

Slide 28 text

02 28 Common Problems

Slide 29

Slide 29 text

1/3 29 શͯͷΞΫγϣϯ͕ Ͱ͖Δ͔ʁ

Slide 30

Slide 30 text

30 ● ΞΫγϣϯͷ͋ΔཁૉΛFocusՄೳʹ͢Δ ʢOnClickListenerΛ࢖Θͳ͍৔߹ʣ // Accessibility FocusͰ΋ // FocusՄೳʹͳΔ android:focusable="true" ΞΫγϣϯ͕Ͱ͖ΔΑ͏ʹ͢Δ

Slide 31

Slide 31 text

myView.setOnLongClickListener { // Կ͔ॲཧ͢Δ true } 31 ཁૉΛબ୒Ͱ͖ͳ͍ 🥲

Slide 32

Slide 32 text

myView.setOnLongClickListener { // Կ͔ॲཧ͢Δ true } // setOnClickListenerͷ৔߹Ҏ֎ɺ 
 // ઃఆ͢΂͖⚠ myView.isFocusable = true 32

Slide 33

Slide 33 text

33 δΣενϟʔ͸Ͳ͏͢Δʁ 🤔

Slide 34

Slide 34 text

34 બ୒Ͱ͖Δ͚ͲɺԿ΋ى͖ͳ͍😢 true } myView.isFocusable = true view.performClick() myView.setOnTouchListener { view, event -> // ΠϕϯτΛϋϯυϦϯά͢Δ

Slide 35

Slide 35 text

35 ● Nav Focus͸OnTouchListenerΛݺ΂ͳ͍ 😣 ● Πϕϯτൃੜͷݕ஌͸ผͷ΍Γํ͕ඞཁ 👉 ୅ΘΓʹKeyEventsΛ؂ࢹ͢Δ δΣενϟʔͷ৔߹

Slide 36

Slide 36 text

myView.setOnKeyListener { view, keyCode, event -> 36 } // αϙʔτ͍ͨ͠ΠϕϯτΛఆٛ͢Δ KeyEvent.KEYCODE_ENTER, ... -> { // Կ͔ॲཧ͢Δ true } else -> false when (keyCode) { ࢀߟɿAndroid Developers }

Slide 37

Slide 37 text

myView.setOnKeyListener { view, keyCode, event -> } when (keyCode) { } 37 ΠϕϯτͷॏෳഉআΛ๨Εͳ͍ ⚠ when (keyCode) { ... } // ACTION_UP·Ͱ͸ಉ͡Πϕϯτ͕਺ճདྷͯ͠·͏ if (event.action != KeyEvent.ACTION_UP) { return@setOnKeyListener false }

Slide 38

Slide 38 text

override fun onKeyUp(keyCode: Int, event: KeyEvent?)
 : Boolean { } return when (keyCode) { KeyEvent.KEYCODE_ENTER -> performClick() else -> ... } 38 ΧελϜɾϏϡʔͷ৔߹͸ɺ onKeyUp()ΛΦʔόʔϥΠυ͢Δ🚀

Slide 39

Slide 39 text

39 Ҡಈͱૢ࡞͕༰қʹ Ͱ͖Δ͔ʁ 2/3

Slide 40

Slide 40 text

40 ● ΞΫγϣϯͷͳ͍ཁૉΛεΩοϓ͢Δ ● ΞΫγϣϯΛͨ͠৔߹ͷΈUIΛมԽͤ͞Δ // ͜ͷཁૉΛεΩοϓ͢Δ android:focusable=“false” Ϣʔβʔͷظ଴Λཪ੾Βͳ͍

Slide 41

Slide 41 text

41 ● Focus͕͍ͭͰ΋֎ͤΒΕΔ͔͔֬ΊΔ ● EscΩʔͰͲ͔͜ΒͰ΋ൈ͚ग़ͤΔΑ͏ʹ͢Δ ● WebViewɺεΫϩʔϧɺ·ͨ͸υϩοϓμ΢ϯ Ϧετ͸৻ॏʹݕূ͢Δ ΩʔϘʔυɾτϥοϓΛ๷͙

Slide 42

Slide 42 text

42 ༷ʑͳϝʔΧʔͷ୺຤Ͱ ݕূΛ͠Α͏ 😇

Slide 43

Slide 43 text

43 ● ࿦ཧతͰҰ؏ͨ͠ॱংΛอͭ // Tab navigation
 android:nextFocusForward=“@id/...” // Directional navigation // ্ɺԼɺࠨ΋ผͷଐੑͰઃఆͰ͖Δ android:nextFocusRight=“@id/...” ༧૝͞ΕͨྲྀΕΛҡ࣋͢Δ

Slide 44

Slide 44 text

44 खؒΛল͘ ࢀߟɿAndroid Developers // API 26+ android:keyboardNavigationCluster ● ClusterͰཁૉΛάϧʔϓԽ͢Δ 1 2 3 4 10 11 12 ϝΠϯ ίϯςϯπ Bottom nav bar Top tabs

Slide 45

Slide 45 text

45 ● ඞཁͳ৔߹ɺը໘ͷ࢝఺ʹFocusΛ͋ͯΔ // API 26+ // ιϑτ΢ΣΞɾΩʔϘʔυ͸ݺ͹Εͳ͍ खؒΛল͘

Slide 46

Slide 46 text

46 ࢀߟɿAlex Zlatkus ͔͜͜Β ࢝Ί͍ͨ

Slide 47

Slide 47 text

47 Focus͕Ͳ͜ʹ͋Δ͔ ৗʹ෼͔Δ͔ʁ 3/3

Slide 48

Slide 48 text

48 ● Focus͕ৗʹݟ͑ΔΑ͏ʹ͢Δ ● ඞཁͳ৔߹͸Πϯδέʔλͷ ৭΍ܗΛΧελϚΠζ͢Δ ໎ࢠΛ๷ࢭ͢Δ ࢀߟɿ2021೥ͷGoogle Play StoreΞϓϦ

Slide 49

Slide 49 text

ΧελϜΠϯδέʔλɿશΞϓϦϨϕϧ 49 ... <item name="colorControlHighlight">...</item> ΧελϜΠϯδέʔλ

Slide 50

Slide 50 text

ΧελϜΠϯδέʔλɿཁૉϨϕϧ ᶃ ΧελϜΠϯδέʔλɿཁૉϨϕϧ 50 ... ΧελϜΠϯδέʔλ

Slide 51

Slide 51 text

51 ΧελϜΠϯδέʔλɿཁૉϨϕϧ ᶄ ΧελϜΠϯδέʔλɿཁૉϨϕϧ ΧελϜΠϯδέʔλ

Slide 52

Slide 52 text

// ΫϦοΫΤϑΣΫτ͸state_pressedͱͯ͠ఆٛͰ͖Δ 52 ΧελϜΠϯδέʔλɿཁૉϨϕϧ ᶅ ΧελϜΠϯδέʔλɿཁૉϨϕϧ ΧελϜΠϯδέʔλ

Slide 53

Slide 53 text

53 ιϑτ΢ΣΞɾΩʔϘʔυͷ࿩͸ ·ͨࠓ౓… 🥲

Slide 54

Slide 54 text

01 54 Introduction to Focus Common Problems Focus in Compose 02 03 Introduction to Focus Common Problems

Slide 55

Slide 55 text

03 55 Focus in Compose

Slide 56

Slide 56 text

56 Compose specificities

Slide 57

Slide 57 text

57 ● FocusͷྲྀΕ͸Composable ͷએݴॱংʹͳΔ ● ಉ͡Ϩϕϧʹ͋Δཁૉ͕ ༏ઌ͞ΕΔ Tab NavigationͷྲྀΕ // Ϩϕϧ ᶃ // Ϩϕϧ ᶄ // Ϩϕϧ ᶄ Column { Row { Row { } } } ... ...

Slide 58

Slide 58 text

58 CustomButton("1") CustomButton("2") CustomButton("3") CustomButton("4") Column { } Row { Row { } }

Slide 59

Slide 59 text

59 Row { Column { } Column { } } CustomButton("1") CustomButton("2") CustomButton("3") CustomButton("4")

Slide 60

Slide 60 text

60 Modifierͷॱং͕େࣄ 🧐

Slide 61

Slide 61 text

61 ΞΫγϣϯͷϋϯυϦϯά (in Compose)

Slide 62

Slide 62 text

Modifier.focusable() 62 ● Πϯδέʔλͷ࡞੒ͱͦͷදࣔϩδοΫ͸ ࣗ෼Ͱॻ͔͘͠ͳ͍ 😭 ཁૉΛFocusՄೳʹ͢Δ

Slide 63

Slide 63 text

63 Modifier var color by remember { .focusable() mutableStateOf(transparent) } // FocusՄೳʹ͍ͨ͠ComposableͷModifier focusableͷModifier͸Ұ൪࠷ޙʹઃఆ͢΂͖ ⚠

Slide 64

Slide 64 text

64 Modifier .border(5.dp, indicatorColor) .onFocusChanged { focusState -> } color = if (focusState.isFocused) { black } else { transparent } var color by remember { ...} .focusable() 🎉

Slide 65

Slide 65 text

65 Modifier.clickable { // Կ͔ॲཧ͢Δ } ● ͜ͷModifierͳΒɺComposable͸ࣗಈͰ FocusՄೳʹͳΔ ✅ ΫϦοΫɾΞΫγϣϯΛॲཧ͢Δ

Slide 66

Slide 66 text

66 👀 ͦͷଞͷΞΫγϣϯ͸ʁ

Slide 67

Slide 67 text

67 ͦͷଞͷΞΫγϣϯΛॲཧ͢Δ 👉 ୅ΘΓʹKeyEventsΛ؂ࢹ͢Δ ʢ·ͨʣ ● ௕ԡؚ͠Ίͯɺશ෦τϦΨʔͰ͖ͳ͍ 😢

Slide 68

Slide 68 text

68 KeyEventsΛ؂ࢹ͢Δ Modifier.onPreviewKeyEvent {} Modifier.onKeyEvent {} // ਌ͷίʔϧόοΫ͕ઌʹݺ͹ΕΔ // ͍ͭ΋௨ΓʹࢠͷίʔϧόοΫ͔Β࢝·Δ

Slide 69

Slide 69 text

69 Modifier.onKeyEvent { keyEvent -> } when (keyEvent.key) { Modifier.onKeyEvent Key.Enter -> { // Կ͔ॲཧΛ͢Δ true } else -> false }

Slide 70

Slide 70 text

70 // ॏෳഉআ if (keyEvent.type != KeyEventType.KeyUp) { return@onPreviewKeyEvent false } when (keyEvent.key) { } when (keyEvent.key) { ... } Modifier.onKeyEvent { keyEvent -> } Modifier.onKeyEvent

Slide 71

Slide 71 text

71 ૢ࡞ͱҠಈ (in Compose)

Slide 72

Slide 72 text

● TouchModeͰ͸λοϓՄೳͷ·· 👋 72 Modifier.focusProperties { ཁૉΛεΩοϓ͢Δ canFocus = false }

Slide 73

Slide 73 text

73 ॱংΛม͑Δ - Tab Navigation Modifier.focusProperties { } next = } previous = ... ... ● FocusPropertiesͱFocusRequesterΛ࢖͏ ॱংΛม͑Δ

Slide 74

Slide 74 text

74 val (first, second) = remember { FocusRequester.createRefs() }

Slide 75

Slide 75 text

Modifier 75 val (first, second) = remember { FocusRequester.createRefs() } first // ̍൪໨ͷComposable // ̎൪໨ͷComposableʢߦ͖ઌʣ Modifier.focusRequester( ) ) second .focusRequester(

Slide 76

Slide 76 text

76 val (first, second) = remember { FocusRequester.createRefs() } Modifier .focusProperties { } second next = first) .focusRequester(

Slide 77

Slide 77 text

77 // ͪ͜Β͸࢖ΘΕͳ͍ Ұ൪্ͷModifier͕উͭ ⚠ Modifier .focusProperties { } .focusProperties { previous = second } next = second

Slide 78

Slide 78 text

78 Modifier.focusProperties { left = ... right = ... up = ... down = ... } ॱংΛม͑Δ - Directional Nav ॱংΛม͑Δ

Slide 79

Slide 79 text

79 val focusManager = LocalFocusManager.current FocusΛҠಈͤ͞Δ focusManager.moveFocus( ) FocusDirection.Down // Ұ൪ۙ͘ʹ͋ΔFocusՄೳͳཁૉ΁ߦ͘ // ߦ͖͍ͨํ޲

Slide 80

Slide 80 text

80 ● FocusՄೳͰ͋Ε͹ɺͲ͜΁Ͱ΋ߦ͚Δ val requester = remember { FocusRequester() } requester.requestFocus() FocusΛ౰ͯΔ

Slide 81

Slide 81 text

Button( onClick = { ) { ... } TextField(... // ΫϦοΫͨ͠ΒɺTextFieldʹҠಈ͢Δ 81 ) }

Slide 82

Slide 82 text

82 Button( onClick = { ) { ... } TextField(... } ) val requester = remember { FocusRequester() } Modifier.focusRequester(requester) // ΫϦοΫͨ͠ΒɺTextFieldʹҠಈ͢Δ

Slide 83

Slide 83 text

83 Button( onClick = { ) { ... } TextField(... } ) val requester = remember { FocusRequester() } Modifier.focusRequester(requester) requester.requestFocus() // ΫϦοΫͨ͠ΒɺTextFieldʹҠಈ͢Δ

Slide 84

Slide 84 text

84 ࢼͯ͠ΈΑ͏ʂ

Slide 85

Slide 85 text

85 ᶃ εϚϗʹ઀ଓ͢Δ ᶄ ೚ҙͷΩʔΛԡ͢ ᶅ Πϯδέʔλ͕දࣔ͞ΕΔ ʢଞͷ֎෦πʔϧ΋ಉ༷ 🎮ʣ ֎෦ΩʔϘʔυͷ৔߹

Slide 86

Slide 86 text

86 1⃣ ΤϛϡϨʔλʔΛ࢖͏ OR 2⃣ σόΠεɾϛϥʔϦϯάΛ࢖͏ ύιίϯͷΩʔϘʔυͷ৔߹

Slide 87

Slide 87 text

87 ● ϓϦηοτɾϓϩϑΟʔϧͳΒɺ σϑΥϧτͰઃఆࡁΈ ✅ ● ΧελϜɾϓϩϑΟʔϧͷ৔߹͸ɺ σόΠε࡞੒࣌ʹઃఆ͢Δඞཁ͕͋Δ 1⃣ ΤϛϡϨʔλʔΛ࢖͏

Slide 88

Slide 88 text

88

Slide 89

Slide 89 text

89

Slide 90

Slide 90 text

90

Slide 91

Slide 91 text

91 ύιίϯͷΩʔϘʔυܦ༝Ͱૢ࡞Մೳʹ🥳

Slide 92

Slide 92 text

92 ● AndroidStudioܦ༝Ͱ࣮ػΛ࢖͏ ● Preferences → Tools → Device mirroring 2⃣ σόΠεɾϛϥʔϦϯάΛ࢖͏

Slide 93

Slide 93 text

93

Slide 94

Slide 94 text

94 ίʔυΛॻ͖ͳ͕Β࣮ػͰಈ࡞֬ೝ͢Δ 🎉

Slide 95

Slide 95 text

95 ● Focus in Compose ɹ- Android DevelopersެࣜυΩϡϝϯτ ● Android App Development: Accessibility ɹ(LinkedIn Learning) - Renato Iwashima ࢀߟ

Slide 96

Slide 96 text

Thanks! ςʔϚɿSlidesCarnival ɹ tahia910 Special thanks: ● Shogo Takasaki ● Hidenari Tajima