1 /*
<lambda>null2 * Copyright 2024 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package androidx.tv.material3.samples
18
19 import androidx.annotation.Sampled
20 import androidx.compose.foundation.background
21 import androidx.compose.foundation.layout.Box
22 import androidx.compose.foundation.layout.fillMaxSize
23 import androidx.compose.foundation.layout.padding
24 import androidx.compose.runtime.Composable
25 import androidx.compose.runtime.LaunchedEffect
26 import androidx.compose.runtime.getValue
27 import androidx.compose.runtime.key
28 import androidx.compose.runtime.mutableStateOf
29 import androidx.compose.runtime.remember
30 import androidx.compose.runtime.setValue
31 import androidx.compose.ui.ExperimentalComposeUiApi
32 import androidx.compose.ui.Modifier
33 import androidx.compose.ui.focus.focusRestorer
34 import androidx.compose.ui.graphics.Color
35 import androidx.compose.ui.unit.dp
36 import androidx.compose.ui.unit.sp
37 import androidx.tv.material3.ExperimentalTvMaterial3Api
38 import androidx.tv.material3.Tab
39 import androidx.tv.material3.TabDefaults
40 import androidx.tv.material3.TabRow
41 import androidx.tv.material3.TabRowDefaults
42 import androidx.tv.material3.Text
43 import kotlin.time.Duration.Companion.microseconds
44 import kotlinx.coroutines.delay
45
46 /** Tab row with a Pill indicator */
47 @OptIn(ExperimentalComposeUiApi::class)
48 @Composable
49 @Sampled
50 fun PillIndicatorTabRow() {
51 val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
52 var selectedTabIndex by remember { mutableStateOf(0) }
53
54 TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
55 tabs.forEachIndexed { index, tab ->
56 key(index) {
57 Tab(
58 selected = index == selectedTabIndex,
59 onFocus = { selectedTabIndex = index },
60 ) {
61 Text(
62 text = tab,
63 fontSize = 12.sp,
64 modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
65 )
66 }
67 }
68 }
69 }
70 }
71
72 /** Tab row with an Underlined indicator */
73 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
74 @Composable
75 @Sampled
UnderlinedIndicatorTabRownull76 fun UnderlinedIndicatorTabRow() {
77 val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
78 var selectedTabIndex by remember { mutableStateOf(0) }
79
80 TabRow(
81 selectedTabIndex = selectedTabIndex,
82 indicator = { tabPositions, doesTabRowHaveFocus ->
83 TabRowDefaults.UnderlinedIndicator(
84 currentTabPosition = tabPositions[selectedTabIndex],
85 doesTabRowHaveFocus = doesTabRowHaveFocus,
86 )
87 },
88 modifier = Modifier.focusRestorer()
89 ) {
90 tabs.forEachIndexed { index, tab ->
91 key(index) {
92 Tab(
93 selected = index == selectedTabIndex,
94 onFocus = { selectedTabIndex = index },
95 colors = TabDefaults.underlinedIndicatorTabColors(),
96 ) {
97 Text(
98 text = tab,
99 fontSize = 12.sp,
100 modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
101 )
102 }
103 }
104 }
105 }
106 }
107
108 /** Tab row with delay between tab changes */
109 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
110 @Composable
111 @Sampled
TabRowWithDebouncenull112 fun TabRowWithDebounce() {
113 val tabs = listOf("Tab 1", "Tab 2", "Tab 3")
114 var selectedTabIndex by remember { mutableStateOf(0) }
115
116 // This index will be used to show a panel
117 var tabPanelIndex by remember { mutableStateOf(selectedTabIndex) }
118
119 // Change the tab-panel only after some delay
120 LaunchedEffect(selectedTabIndex) {
121 delay(250.microseconds)
122 tabPanelIndex = selectedTabIndex
123 }
124
125 TabRow(selectedTabIndex = selectedTabIndex, modifier = Modifier.focusRestorer()) {
126 tabs.forEachIndexed { index, tab ->
127 key(index) {
128 Tab(
129 selected = index == selectedTabIndex,
130 onFocus = { selectedTabIndex = index },
131 ) {
132 Text(
133 text = tab,
134 fontSize = 12.sp,
135 modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
136 )
137 }
138 }
139 }
140 }
141 }
142
143 /** Tab changes onClick instead of onFocus */
144 @OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
145 @Composable
146 @Sampled
OnClickNavigationnull147 fun OnClickNavigation() {
148 val bgColors =
149 listOf(
150 Color(0x6a, 0x16, 0x16),
151 Color(0x6a, 0x40, 0x16),
152 Color(0x6a, 0x6a, 0x16),
153 Color(0x40, 0x6a, 0x16),
154 )
155
156 var focusedTabIndex by remember { mutableStateOf(0) }
157 var activeTabIndex by remember { mutableStateOf(focusedTabIndex) }
158
159 Box(modifier = Modifier.fillMaxSize().background(bgColors[activeTabIndex])) {
160 TabRow(
161 selectedTabIndex = focusedTabIndex,
162 indicator = { tabPositions, doesTabRowHaveFocus ->
163 // FocusedTab's indicator
164 TabRowDefaults.PillIndicator(
165 currentTabPosition = tabPositions[focusedTabIndex],
166 activeColor = Color.Blue.copy(alpha = 0.4f),
167 inactiveColor = Color.Transparent,
168 doesTabRowHaveFocus = doesTabRowHaveFocus,
169 )
170
171 // SelectedTab's indicator
172 TabRowDefaults.PillIndicator(
173 currentTabPosition = tabPositions[activeTabIndex],
174 doesTabRowHaveFocus = doesTabRowHaveFocus,
175 )
176 },
177 modifier = Modifier.focusRestorer()
178 ) {
179 repeat(bgColors.size) {
180 key(it) {
181 Tab(
182 selected = activeTabIndex == it,
183 onFocus = { focusedTabIndex = it },
184 onClick = {
185 focusedTabIndex = it
186 activeTabIndex = it
187 }
188 ) {
189 Text(
190 text = "Tab ${it + 1}",
191 fontSize = 12.sp,
192 modifier = Modifier.padding(horizontal = 16.dp, vertical = 6.dp)
193 )
194 }
195 }
196 }
197 }
198 }
199 }
200