Skip to content

Commit c89a96e

Browse files
committed
Add snippets for focus management in Compose
1 parent 81b5d86 commit c89a96e

File tree

8 files changed

+934
-7
lines changed

8 files changed

+934
-7
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import android.os.StrictMode
2222
import androidx.activity.ComponentActivity
2323
import androidx.activity.compose.setContent
2424
import androidx.compose.foundation.layout.fillMaxSize
25+
import androidx.compose.foundation.layout.padding
2526
import androidx.compose.material3.MaterialTheme
2627
import androidx.compose.material3.Surface
2728
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.unit.dp
2830
import androidx.navigation.compose.NavHost
2931
import androidx.navigation.compose.composable
3032
import androidx.navigation.compose.rememberNavController
@@ -60,7 +62,15 @@ import com.example.compose.snippets.images.ImageExamplesScreen
6062
import com.example.compose.snippets.landing.LandingScreen
6163
import com.example.compose.snippets.layouts.PagerExamples
6264
import com.example.compose.snippets.navigation.Destination
65+
import com.example.compose.snippets.navigation.FocusExamplesDestination
6366
import com.example.compose.snippets.navigation.TopComponentsDestination
67+
import com.example.compose.snippets.touchinput.focus.FocusExamples
68+
import com.example.compose.snippets.touchinput.focus.FocusTargetExamples
69+
import com.example.compose.snippets.touchinput.focus.FocusedStateExamples
70+
import com.example.compose.snippets.touchinput.focus.MoveFocusExamples
71+
import com.example.compose.snippets.touchinput.focus.OneDimensionalFocusTraversalExamples
72+
import com.example.compose.snippets.touchinput.focus.RequestFocusExamples
73+
import com.example.compose.snippets.touchinput.focus.TwoDimensionalFocusTraversalExamples
6474
import com.example.compose.snippets.ui.theme.SnippetsTheme
6575

6676
class SnippetsActivity : ComponentActivity() {
@@ -94,6 +104,9 @@ class SnippetsActivity : ComponentActivity() {
94104
Destination.ShapesExamples -> ApplyPolygonAsClipImage()
95105
Destination.SharedElementExamples -> PlaceholderSizeAnimated_Demo()
96106
Destination.PagerExamples -> PagerExamples()
107+
Destination.FocusExamples -> FocusExamples() {
108+
navController.navigate(it.route)
109+
}
97110
}
98111
}
99112
}
@@ -128,6 +141,34 @@ class SnippetsActivity : ComponentActivity() {
128141
}
129142
}
130143
}
144+
FocusExamplesDestination.entries.forEach { destination ->
145+
composable(destination.route) {
146+
when (destination) {
147+
FocusExamplesDestination.FocusTarget -> {
148+
FocusTargetExamples(modifier = Modifier.padding(32.dp))
149+
}
150+
FocusExamplesDestination.OneDimensionalFocusTraversal -> {
151+
OneDimensionalFocusTraversalExamples(
152+
modifier = Modifier.padding(32.dp)
153+
)
154+
}
155+
FocusExamplesDestination.TwoDimensionalFocusTraversal -> {
156+
TwoDimensionalFocusTraversalExamples(
157+
modifier = Modifier.padding(32.dp)
158+
)
159+
}
160+
FocusExamplesDestination.RequestFocus -> {
161+
RequestFocusExamples(modifier = Modifier.padding(32.dp))
162+
}
163+
FocusExamplesDestination.FocusedState -> {
164+
FocusedStateExamples(modifier = Modifier.padding(32.dp))
165+
}
166+
FocusExamplesDestination.MoveFocus -> {
167+
MoveFocusExamples(modifier = Modifier.padding(32.dp))
168+
}
169+
}
170+
}
171+
}
131172
}
132173
}
133174
}

compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ enum class Destination(val route: String, val title: String) {
2424
ScreenshotExample("screenshotExample", "Screenshot Examples"),
2525
ShapesExamples("shapesExamples", "Shapes Examples"),
2626
SharedElementExamples("sharedElement", "Shared elements"),
27-
PagerExamples("pagerExamples", "Pager examples")
27+
PagerExamples("pagerExamples", "Pager examples"),
28+
FocusExamples("focusExamples", "Focus Examples")
2829
}
2930

3031
// Enum class for compose components navigation screen.
@@ -53,3 +54,13 @@ enum class TopComponentsDestination(val route: String, val title: String) {
5354
SwipeToDismissBoxExamples("swipeToDismissBoxExamples", "Swipe to dismiss box examples"),
5455
SearchBarExamples("searchBarExamples", "Search bar")
5556
}
57+
58+
// Enum class for focus examples navigation screen.
59+
enum class FocusExamplesDestination(val route: String, val title: String) {
60+
FocusTarget("focusTarget", "Focus target"),
61+
OneDimensionalFocusTraversal("focusTraversal", "Focus traversal: 1D"),
62+
TwoDimensionalFocusTraversal("focusTraversal2D", "Focus traversal 2D"),
63+
RequestFocus("requestFocus", "Request focus"),
64+
FocusedState("focusableState", "Focusable state"),
65+
MoveFocus("moveFocus", "Move focus"),
66+
}

compose/snippets/src/main/java/com/example/compose/snippets/touchinput/focus/FocusSnippets.kt

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,20 @@ import androidx.compose.foundation.focusable
2525
import androidx.compose.foundation.interaction.FocusInteraction
2626
import androidx.compose.foundation.interaction.InteractionSource
2727
import androidx.compose.foundation.interaction.MutableInteractionSource
28+
import androidx.compose.foundation.layout.Arrangement
2829
import androidx.compose.foundation.layout.Box
2930
import androidx.compose.foundation.layout.Column
3031
import androidx.compose.foundation.layout.Row
3132
import androidx.compose.foundation.layout.fillMaxSize
33+
import androidx.compose.foundation.lazy.LazyColumn
3234
import androidx.compose.foundation.lazy.grid.GridCells
3335
import androidx.compose.foundation.lazy.grid.GridItemSpan
3436
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
37+
import androidx.compose.foundation.lazy.items
3538
import androidx.compose.material3.Button
3639
import androidx.compose.material3.Card
40+
import androidx.compose.material3.ListItem
41+
import androidx.compose.material3.MaterialTheme
3742
import androidx.compose.material3.Switch
3843
import androidx.compose.material3.Text
3944
import androidx.compose.material3.TextButton
@@ -52,10 +57,13 @@ import androidx.compose.ui.focus.FocusDirection.Companion.Right
5257
import androidx.compose.ui.focus.FocusRequester
5358
import androidx.compose.ui.focus.FocusRequester.Companion.Cancel
5459
import androidx.compose.ui.focus.FocusRequester.Companion.Default
60+
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1
61+
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2
62+
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component3
63+
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component4
5564
import androidx.compose.ui.focus.focusProperties
5665
import androidx.compose.ui.focus.focusRequester
5766
import androidx.compose.ui.focus.onFocusChanged
58-
import androidx.compose.ui.graphics.Color
5967
import androidx.compose.ui.graphics.Color.Companion.Blue
6068
import androidx.compose.ui.graphics.Color.Companion.Green
6169
import androidx.compose.ui.graphics.Color.Companion.Red
@@ -70,10 +78,68 @@ import androidx.compose.ui.node.DelegatableNode
7078
import androidx.compose.ui.node.DrawModifierNode
7179
import androidx.compose.ui.node.invalidateDraw
7280
import androidx.compose.ui.platform.LocalFocusManager
81+
import androidx.compose.ui.text.TextStyle
7382
import androidx.compose.ui.tooling.preview.Preview
7483
import androidx.compose.ui.unit.dp
84+
import com.example.compose.snippets.navigation.FocusExamplesDestination
7585
import kotlinx.coroutines.launch
7686

87+
@Composable
88+
fun FocusExamples(
89+
modifier: Modifier = Modifier,
90+
onNavigation: (FocusExamplesDestination) -> Unit = {}
91+
) {
92+
LazyColumn(
93+
verticalArrangement = Arrangement.spacedBy(8.dp),
94+
modifier = modifier
95+
) {
96+
items(FocusExamplesDestination.entries.toList()) {
97+
ListItem(
98+
modifier = Modifier.clickable(onClick = {
99+
onNavigation(it)
100+
}),
101+
headlineContent = {
102+
Text(it.title)
103+
}
104+
)
105+
}
106+
}
107+
}
108+
109+
@Composable
110+
internal fun Section(
111+
title: String,
112+
modifier: Modifier = Modifier,
113+
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(16.dp),
114+
style: TextStyle = MaterialTheme.typography.headlineMedium,
115+
content: @Composable () -> Unit = {}
116+
) {
117+
Column(
118+
modifier = modifier.focusGroup(),
119+
verticalArrangement = verticalArrangement
120+
) {
121+
Text(title, style = style)
122+
content()
123+
}
124+
}
125+
126+
@Composable
127+
internal fun SubSection(
128+
title: String,
129+
modifier: Modifier = Modifier,
130+
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(8.dp),
131+
style: TextStyle = MaterialTheme.typography.headlineSmall,
132+
content: @Composable () -> Unit = {}
133+
) {
134+
Section(
135+
title = title,
136+
modifier = modifier,
137+
verticalArrangement = verticalArrangement,
138+
style = style,
139+
content = content,
140+
)
141+
}
142+
77143
@Preview
78144
@Composable
79145
private fun BasicSample() {
@@ -276,7 +342,7 @@ private fun RequestFocus() {
276342
}
277343

278344
@Composable
279-
private fun RequestFocus2() {
345+
internal fun RequestFocus2() {
280346
// [START android_compose_touchinput_focus_request2]
281347
val focusRequester = remember { FocusRequester() }
282348
var text by remember { mutableStateOf("") }
@@ -382,7 +448,7 @@ private fun ModifierOrder2() {
382448

383449
@OptIn(ExperimentalComposeUiApi::class)
384450
@Composable
385-
private fun RedirectFocus() {
451+
private fun FocusRedirection() {
386452
// [START android_compose_touchinput_focus_redirect]
387453
val otherComposable = remember { FocusRequester() }
388454

@@ -399,7 +465,7 @@ private fun RedirectFocus() {
399465
}
400466

401467
@Composable
402-
private fun FocusAdvancing() {
468+
internal fun FocusAdvancing() {
403469
// [START android_compose_touchinput_focus_advancing]
404470
val focusManager = LocalFocusManager.current
405471
var text by remember { mutableStateOf("") }
@@ -424,7 +490,7 @@ private fun FocusAdvancing() {
424490
@Composable
425491
private fun ReactToFocus() {
426492
// [START android_compose_touchinput_focus_react]
427-
var color by remember { mutableStateOf(Color.White) }
493+
var color by remember { mutableStateOf(White) }
428494
Card(
429495
modifier = Modifier
430496
.onFocusChanged {
@@ -460,7 +526,7 @@ private class MyHighlightIndicationNode(private val interactionSource: Interacti
460526
override fun ContentDrawScope.draw() {
461527
drawContent()
462528
if (isFocused) {
463-
drawRect(size = size, color = Color.White, alpha = 0.2f)
529+
drawRect(size = size, color = White, alpha = 0.2f)
464530
}
465531
}
466532
}

0 commit comments

Comments
 (0)