사용자 도구

사이트 도구


android:collection:search
search

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판이전 판
다음 판
이전 판
android:collection:search [2025/05/11 21:18] – 함수호출하기 이거니맨android:collection:search [2025/06/03 18:22] (현재) – SQL 검색 이거니맨
줄 82: 줄 82:
 </code> </code>
  
 +==== 5. 대소문자 구분 없이 비교하기 ==== 
 +
 +=== 가. 한쪽으로 통일해 주는 방법 === 
 +다음과 같이 소문자로 통일해주면 대소문자 구분 없이 비교가 가능하다.
 +
 +<code kotlin>
 +it.title.lowercase().contains(searchString.lowercase())
 +</code>
 +
 +=== 나. ignoreCase 옵션 ===
 +
 +과거에는 contains함수에 ignoreCase 옵션이 없었다. 그런데 최신 kotlin문법은 이게 가능하다. 
 +
 +따라서 다음과 같이 코드가 가능하다.
 +
 +<code kotlin>
 +it.title.contains(searchString, ignoreCase = true)
 +</code>
  
 ===== 컴포즈에서 불러오기 ===== ===== 컴포즈에서 불러오기 =====
 +
 +==== 1. 검색 창 만들기 ==== 
 +
  
 다음과 같이 검색창을 만들고 리스를 불러오면 된다.  다음과 같이 검색창을 만들고 리스를 불러오면 된다. 
  
 <code kotlin> <code kotlin>
-    var searchString by remember { mutableStateOf("") }+var searchString by remember { mutableStateOf("") }
          
-            Row(modifier = Modifier +Row(modifier = Modifier 
-            .fillMaxWidth() +.fillMaxWidth() 
-            .padding(6.dp), horizontalArrangement = Arrangement.SpaceEvenly) {+.padding(6.dp), horizontalArrangement = Arrangement.SpaceEvenly) {
  
-            OutlinedTextField( +OutlinedTextField( 
-                value =  searchString, +    value =  searchString, 
-                onValueChange = { searchString = it }, +    onValueChange = { searchString = it }, 
-                label = { Text(text = "검색", style = MaterialTheme.typography.titleMedium )  }, +    label = { Text(text = "검색", style = MaterialTheme.typography.titleMedium )  }, 
-                trailingIcon = { +    trailingIcon = { 
-                    IconButton(onClick = { /*  */ }) { +        IconButton(onClick = {searchString = ""  }) { 
-                        Icon( +            Icon( 
-                            imageVector = Icons.Default.Search, +                imageVector = Icons.Default.Search, 
-                            contentDescription = "Select Drop Down" +                contentDescription = "Select Drop Down"
-                        ) +
-                    } +
-                }, +
-                textStyle = TextStyle(fontFamily = fontkjcMyungjo, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, fontStyle = FontStyle.Normal), +
-                keyboardOptions = KeyboardOptions.Default,+
             )             )
         }         }
 +    },
 +    textStyle = TextStyle(fontFamily = fontkjcMyungjo, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, fontStyle = FontStyle.Normal),
 +    keyboardOptions = KeyboardOptions.Default,
 +)
 +}
                  
        
-           viewModel.quoteList(searchString, sortMethod).forEach() { item ->+viewModel.quoteList(searchString, sortMethod).forEach() { item ->
  
-           DocuItemCard(item = item)+DocuItemCard(item = item) 
 + 
 +
 + 
 + 
 +</code>  
 + 
 +==== 2. 검색결과를 하이라이트 하게 해주는 코드 만들기 ==== 
 + 
 +다음과 같이 특정 문구를 하이라이트 하게 해주는 함수를 만들었다.  
 + 
 +<code kotlin> 
 +// 검색 결과 하이라이트 
 +fun highlightContainedText(inputText: String, divider : String) : AnnotatedString 
 +
 + 
 +    val originalString : AnnotatedString = buildAnnotatedString { append(inputText) } 
 + 
 +    if (inputText.contains(divider, ignoreCase = true)) { 
 + 
 +        val listOfString = inputText.split(divider, ignoreCase = true) 
 + 
 +        val formattedString = buildAnnotatedString { 
 +            for (i : Int in 0 until (listOfString.size * 2) -1) 
 +            { 
 +                if (i % 2 == 0) { 
 +                    append(listOfString[i/2]) 
 +                }else 
 +                { 
 +                    withStyle(SpanStyle(color = Color.DarkGray, background = Color.Yellow)) { append(divider)} 
 +                } 
 + 
 +            }
  
         }         }
-        + 
 +        return formattedString 
 +    } 
 + 
 +    return originalString 
 +
 + 
 +</code>   
 + 
 +==== 3. 하이라이트를 포함한 리스트 아이템 소스 ==== 
 + 
 +<code kotlin> 
 +// 연락처 목록을 보여주는 컴퍼저블 
 +@Composable 
 +fun DocuItemCard(item : QuoteText, searchString : String) { 
 + 
 +    val scope = rememberCoroutineScope() 
 + 
 +    Row (modifier = Modifier 
 +        .fillMaxWidth() 
 +        .padding(8.dp) 
 +        .background(MaterialTheme.colorScheme.primary, RoundedCornerShape(16.dp)) 
 +        .padding(10.dp), 
 +        verticalAlignment = Alignment.CenterVertically, 
 + 
 +        ) { 
 + 
 +        Column (modifier = Modifier 
 +            .weight(0.8f) 
 +            .padding(horizontal = 16.dp), verticalArrangement = Arrangement.SpaceBetween){ 
 +            Text(text = highlightContainedText(item.title, searchString), color = MaterialTheme.colorScheme.primaryContainer, style = MaterialTheme.typography.displayMedium, modifier = Modifier.padding(vertical = 4.dp), overflow = TextOverflow.Ellipsis) 
 +//            Text(text = item.title, color = MaterialTheme.colorScheme.primaryContainer, style = MaterialTheme.typography.displayMedium, modifier = Modifier.padding(vertical = 4.dp), overflow = TextOverflow.Ellipsis) 
 +            Text(text = "by ${highlightContainedText(item.author, searchString)}"  + " ", color = MaterialTheme.colorScheme.primaryContainer,  textAlign = TextAlign.End, style = MaterialTheme.typography.bodyMedium, modifier = Modifier 
 +                .fillMaxWidth() 
 +                .padding(vertical = 4.dp), overflow = TextOverflow.Ellipsis) 
 +            Text(text = highlightContainedText(item.quote.first(), searchString), style = MaterialTheme.typography.bodySmall, overflow = TextOverflow.Ellipsis) 
 +        } 
 + 
 +        IconButton(onClick = { 
 + 
 +            // 스크린 라우팅하기 
 +            scope.launch { ScreenRouter.navigateTo(Screen.SpeechToText, item) } 
 +        }) { 
 +            Icon(Icons.Default.Edit, contentDescription = "${item.title} 연습하기", tint = Color.White) 
 + 
 +        } 
 +    } 
 + 
 +
 +</code>  
 + 
 + 
 +===== Roomdatabase에서 검색하기 =====  
 + 
 +==== 1. Dao 만들기 ====  
 + 
 +Dao에서 SQL문은 다음과 같이 작성하면 된다. 
 + 
 +<code kotlin> 
 +    @Query("SELECT * FROM SCORE " + 
 +            "WHERE (CASE WHEN :isFiltered = 1 THEN (quoteTitle LIKE :title AND speechResult LIKE '%' || :search || '%') ELSE (1=1 AND speechResult LIKE '%' || :search || '%') END)" + 
 +            "ORDER BY " + 
 +            "CASE WHEN :sortMethod='TitleASC'  THEN quoteTitle END ASC, " + 
 +            "CASE WHEN :sortMethod='TitleDESC' THEN quoteTitle END DESC," + 
 +            "CASE WHEN :sortMethod='DateASC' THEN createdAt END ASC," + 
 +            "CASE WHEN :sortMethod='DateDESC' THEN createdAt END DESC," + 
 +            "CASE WHEN :sortMethod='ScoreASC' THEN speechScore END ASC," + 
 +            "CASE WHEN :sortMethod='ScoreDESC' THEN speechScore END DESC" 
 + 
 +    ) 
 +    fun getScoreList(sortMethod : String, isFiltered : Boolean, title : String, search : String) : LiveData<List<Score>> 
 +</code>  
 + 
 +일부 단어 포함(위의 Collection 기능에서 contains 함수)을 하려면 SQL 문에서 '%'를 앞뒤로 붙이면 된다. 
 + 
 +그런데 Kotlin문에서 이를 표시하면 concatrate연산자 '||'를 사용한다. 따라서 다음과 같이 LIKE문을 만든다.  
 + 
 +  LIKE '%' || :search || '%' 
 +   
 + 
 +==== 2. ViewModel 만들기 ==== 
 + 
 +ViewModel은 다음과 같이 하면 된다. 
 + 
 +<code kotlin> 
 +    fun scoreList(sortMethod : Sort, isFiltered : Boolean, filter : String, search : String) : LiveData<List<Score>>
 +        return when (sortMethod) { 
 +            Sort.DateASC -> scoreDao.getScoreList("DateASC", isFiltered, filter, search) 
 +            Sort.DateDESC -> scoreDao.getScoreList("DateDESC", isFiltered, filter, search) 
 +            Sort.TitleASC -> scoreDao.getScoreList("TitleASC", isFiltered, filter, search) 
 +            Sort.TitleDESC -> scoreDao.getScoreList("TitleDESC", isFiltered, filter, search) 
 +            Sort.ScoreASC -> scoreDao.getScoreList("ScoreASC", isFiltered, filter, search) 
 +            Sort.ScoreDESC -> scoreDao.getScoreList("ScoreDESC", isFiltered, filter, search) 
 +        } 
 +    } 
 +</code> 
 + 
 + 
 +==== 3. Compose View화면 ====  
 + 
 +View  화면에서는 다음과 같이 뷰모델을 호출한다. 
 + 
 +<code kotlin> 
 +    //  검색어 
 +    var searchString by remember { mutableStateOf("") } 
 + 
 +    val scoreList by viewModel.scoreList(sortMethod.value, isListAll.value, curQuoteText?.title!!, searchString).observeAsState() // 점수 목록 
 +     
 +        // 검색 창 
 +    Row(modifier = Modifier 
 +        .fillMaxWidth() 
 +        .padding(6.dp), horizontalArrangement = Arrangement.SpaceEvenly) { 
 + 
 +        OutlinedTextField( 
 +            value =  searchString, 
 +            onValueChange = { searchString = it }, 
 +            label = { Text(text = "검색", style = MaterialTheme.typography.titleMedium )  }, 
 +            trailingIcon = { 
 +                IconButton(onClick = { searchString = "" }) { 
 +                    Icon( 
 +                        imageVector = Icons.Default.Close, 
 +                        contentDescription = "Select Drop Down" 
 +                    ) 
 +                } 
 +            }, 
 +            textStyle = TextStyle(fontFamily = fontkjcMyungjo, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, fontStyle = FontStyle.Normal), 
 +            keyboardOptions = KeyboardOptions.Default, 
 +        ) 
 +    } 
 +     
 + 
 +        // 연락처 리스트 
 +        scoreList?.forEach() { item -> 
 + 
 +            // 자세히 버튼 
 +//            val completeQuote by remember { mutableStateOf(doneList?.find { it.quoteID == item.quoteID })  } 
 +//            val done by remember { mutableStateOf(completeQuote?.isDone?:false) } 
 +            val completeQuote = doneList?.find { it.quoteID == item.quoteID } 
 +            val done = completeQuote?.isDone?:false
  
  
 +            if (!done || !isFiltered) {
 +                ScoreItemCard(item = item, onDelete = {viewModel.deleteScore(item)},
 +                    onComplete = {
 +                        if (completeQuote != null) {
 +                            completeQuote.isDone = false;
 +                            viewModel.upsertDone(completeQuote)
 +                        }
 +                    },
 +                    onUnComplete = {
 +                        if (completeQuote != null) {
 +                            completeQuote.isDone = true;
 +                            viewModel.upsertDone(completeQuote)
 +                        }else {
 +                            val tempComplete = DoneList(
 +                                quoteID = item.quoteID,
 +                                isDone = false,
 +                                completeDate = Date.from(Instant.now()),
 +                                ext1 = ""
 +                            )
 +                            viewModel.upsertDone(tempComplete)
 +                        }
 +                    }, isDone = done)
 +            }
 +        }?: Text(
 +            modifier = Modifier.fillMaxWidth(),
 +            textAlign = TextAlign.Center,
 +            text = "No items yet",
 +            fontSize = 16.sp
 +        )
 </code> </code>
android/collection/search.1746965892.txt.gz · 마지막으로 수정됨: 저자 이거니맨