@@ -11,12 +11,10 @@ import androidx.compose.foundation.layout.Arrangement
11
11
import androidx.compose.foundation.layout.Box
12
12
import androidx.compose.foundation.layout.Column
13
13
import androidx.compose.foundation.layout.Row
14
- import androidx.compose.foundation.layout.WindowInsets
15
- import androidx.compose.foundation.layout.asPaddingValues
16
14
import androidx.compose.foundation.layout.fillMaxSize
17
15
import androidx.compose.foundation.layout.fillMaxWidth
18
16
import androidx.compose.foundation.layout.padding
19
- import androidx.compose.foundation.layout.systemBars
17
+ import androidx.compose.foundation.layout.systemBarsPadding
20
18
import androidx.compose.foundation.lazy.LazyColumn
21
19
import androidx.compose.foundation.lazy.LazyListState
22
20
import androidx.compose.foundation.lazy.items
@@ -76,11 +74,11 @@ class MainActivity : ComponentActivity() {
76
74
LazyColumn (
77
75
Modifier
78
76
.fillMaxSize()
79
- .padding( WindowInsets .systemBars.asPaddingValues() ),
77
+ .systemBarsPadding( ),
80
78
listState
81
79
) {
82
80
item {
83
- SearchBar (searchModel)
81
+ SearchBar (searchModel, listState )
84
82
}
85
83
items(results) {
86
84
ItemColumn (it, searchModel, listState)
@@ -93,18 +91,16 @@ class MainActivity : ComponentActivity() {
93
91
94
92
@Composable
95
93
private fun SearchBar (
96
- searchModel : SearchModel
94
+ searchModel : SearchModel ,
95
+ listState : LazyListState
97
96
) {
98
- val search by searchModel.search.collectAsState(" " )
99
- val iPos by searchModel.indicatorPos.collectAsState(0 )
97
+ val searchState by searchModel.searchState.collectAsState(TextFieldValue ())
100
98
TextField (
101
- value = TextFieldValue (text = search, selection = TextRange (iPos)),
102
- onValueChange = {
103
- searchModel.search(it.text)
104
- },
99
+ value = searchState,
100
+ onValueChange = { searchModel.search(it.text, listState) },
105
101
modifier = Modifier
106
102
.fillMaxWidth()
107
- .border(3 .dp, Color (0xFF6E6E6E ), RoundedCornerShape ( 6 .dp) ),
103
+ .border(3 .dp, Color (0xFF6E6E6E )),
108
104
placeholder = { Text (" Search" ) },
109
105
leadingIcon = {
110
106
// @todo add keyword filter
@@ -114,9 +110,9 @@ class MainActivity : ComponentActivity() {
114
110
horizontalArrangement = Arrangement .spacedBy((- 6 ).dp),
115
111
verticalAlignment = Alignment .CenterVertically // @note adapt with ic_search padding
116
112
) {
117
- if (search .isNotEmpty()) {
113
+ if (searchState.text .isNotEmpty()) {
118
114
IconButton (
119
- onClick = { searchModel.search(" " ) }
115
+ onClick = { searchModel.search(" " , listState ) }
120
116
) {
121
117
Icon (
122
118
painter = painterResource(R .drawable.ic_clear),
@@ -178,7 +174,7 @@ class MainActivity : ComponentActivity() {
178
174
}
179
175
if (jishoData.tags.isNotEmpty()) {
180
176
wordTag(
181
- " Wanikani level ${jishoData.tags.firstOrNull()?.lastOrNull().toString()} " ,
177
+ " wanikani level ${jishoData.tags.firstOrNull()?.lastOrNull().toString()} " ,
182
178
Color (0xFF909dc0 )
183
179
)
184
180
}
@@ -245,15 +241,19 @@ class MainActivity : ComponentActivity() {
245
241
modifier = Modifier
246
242
.background(
247
243
color = backgroundColor,
248
- shape = RoundedCornerShape (4 .dp)
244
+ shape = RoundedCornerShape (2 .dp)
249
245
)
250
- .padding(start = 5 .dp, end = 3 .dp)
246
+ .padding(start = 6 .dp, end = 4 .dp)
251
247
) {
252
248
Text (
253
249
text = label,
254
- color = Color (0xFF222222 ),
255
- fontSize = 10 .sp,
256
- fontWeight = FontWeight .Bold
250
+ maxLines = 1 ,
251
+ style = MaterialTheme .typography.labelLarge.copy(
252
+ color = Color (0xFF222222 ),
253
+ fontWeight = FontWeight .Bold ,
254
+ letterSpacing = 0.6 .sp,
255
+ fontSize = 12 .sp
256
+ )
257
257
)
258
258
}
259
259
}
@@ -273,49 +273,39 @@ class MainActivity : ComponentActivity() {
273
273
offsetPosition
274
274
).firstOrNull()?.let {
275
275
searchModel.apply {
276
- search(it.item)
277
- updateIndicator(it.item.length)
278
- viewModelScope.launch {
279
- listState.scrollToItem(0 )
280
- }
276
+ search(it.item, listState)
281
277
}
282
278
}
283
279
}
284
280
}
285
281
286
282
class SearchModel : ViewModel () {
287
- private val _search = MutableStateFlow (" " )
288
- val search : StateFlow <String > = _search
283
+ private val _searchState = MutableStateFlow (TextFieldValue () )
284
+ val searchState : StateFlow <TextFieldValue > = _searchState
289
285
290
286
private val _results = MutableStateFlow <List <JishoData >>(emptyList())
291
287
val results: StateFlow <List <JishoData >> = _results
292
288
293
289
private var job: Job ? = null
294
- fun search (query : String , page : Int = 1) {
295
- _search .value = query
296
- _indicatorPos .value = query.length
290
+ fun search (query : String , listState : LazyListState , page : Int = 1) {
291
+ _searchState .value = TextFieldValue (query, TextRange (query.length))
297
292
job?.takeIf { it.isActive }?.cancel()
298
293
if (query.isEmpty()) {
299
294
_results .value = emptyList()
300
295
return
301
296
}
302
297
job = viewModelScope.launch {
303
- try {
304
- val thisQuery = _search .value
298
+ runCatching {
299
+ val thisQuery = _searchState .value.text
305
300
search(thisQuery, page, { word ->
306
- if (thisQuery != _search .value) throw CancellationException ()
301
+ if (thisQuery != _searchState .value.text ) throw CancellationException ()
307
302
_results .value = word.data
308
303
})
309
- } catch (_: CancellationException ) {
310
- _results .value = emptyList()
304
+ listState.scrollToItem(0 )
305
+ }.onFailure {
306
+ if (it is CancellationException ) _results .value = emptyList()
311
307
}
312
308
}
313
309
}
314
-
315
- private val _indicatorPos = MutableStateFlow (0 )
316
- val indicatorPos: StateFlow <Int > = _indicatorPos
317
- fun updateIndicator (pos : Int ) {
318
- _indicatorPos .value = pos
319
- }
320
310
}
321
311
}
0 commit comments