본문 바로가기
안드로이드 코틀린

[Kotlin][Android] Jetpack Compose 텍스트 작성 중인 상태에 대해 사용자에게 알리기

by teamnova 2024. 11. 27.
728x90

안녕하세요 오늘은 사용자가 텍스트를 입력 중인지에 대한 여부를 추적하여서 사용자가 입력 중일 때에 

"입력 중.." 이라는 메시지를 나타내보고자 합니다 

 

본 게시글에서 나오는 방법은 키보드 입력을 감지하는지에 대한 여부가 아니라 사용자가 입력한 텍스트의 존재 여부를 텍스트를 하나 입력할때마다 확인하면서 키보드 입력 중인지 판단하는 방법입니다. 

키보드의 입력을 감지하려면 TextField의 onValueChange를 사용하여 isTyping 상태를 변경해야 합니다. 

 

아래는 해당 기능에 대한 코드 입니다
입력 후 1초마다 타이핑 여부를 감지합니다 

 // 새로 추가.
    var isTyping by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()


    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 8.dp)
    ) {
        TextField(
            value = editText,
            onValueChange = { newValue ->
                editText = newValue
                isTyping = true

                // 입력 후 1초동안 타이핑이 없으면 초기화
                scope.launch {
                    kotlinx.coroutines.delay(1000)
                    isTyping = false
                }
            },
            modifier = Modifier.fillMaxWidth(),
            singleLine = true,
            label = { Text("아이템 수정") }
        )

        // 새로 추가한 부분
        // 타이핑 중일때
        if (isTyping) {
            Text(
                text = "작성 중입니다...",
                color = Color.Gray
            )
        }
        Row {
            // 확인 버튼
            Button(onClick = {
                items[index] = editText // 편집 후 값을 저장
                isEditing[index] = false // 편집 모드 종료
            }) {
                Text("확인")
            }
            Spacer(modifier = Modifier.width(4.dp))
            // 취소 버튼
            Button(onClick = {
                isEditing[index] = false // 편집 모드 종료
            }) {
                Text("취소")
            }
        }
    }
} 

 

 

전체 코드입니다 


class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Kotlin_exampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ItemList(cnt = 3)
                }
            }
        }
    }
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ItemList(cnt: Int) {
    // 아이템 리스트 초기화
    val items =
        remember { mutableStateListOf<String>().apply { repeat(cnt) { add("Item ${it + 1}") } } }

    // 체크 박스 상태 리스트
    val selectedItems =
        remember { mutableStateListOf<Boolean>().apply { repeat(cnt) { add(false) } } }

    // 편집 상태 리스트 (각 아이템이 수정 중인지 여부를 저장)
    val isEditing = remember { mutableStateListOf<Boolean>().apply { repeat(cnt) { add(false) } } }

    // 새로운 아이템 입력 상태
    var newItemText by remember { mutableStateOf("") }

    Column {
        // 새로운 아이템 추가 입력 필드
        Row(modifier = Modifier.padding(16.dp)) {
            TextField(
                value = newItemText,
                onValueChange = { newItemText = it },
                label = { Text("새 아이템 입력") },
                modifier = Modifier.weight(1f)
            )
            Spacer(modifier = Modifier.width(8.dp))
            Button(onClick = {
                if (newItemText.isNotBlank()) {
                    items.add(newItemText)
                    selectedItems.add(false) // 새로운 아이템에 대해 체크박스 상태 추가
                    isEditing.add(false) // 새로운 아이템의 편집 상태 추가
                    newItemText = "" // 입력 필드 초기화
                }
            }) {
                Text("추가")
            }
        }

        // 선택된 아이템 개수를 표시
        Text(
            text = "선택된 아이템: ${selectedItems.count { it }}",
            modifier = Modifier
                .fillMaxWidth()
                .padding(16.dp),
            color = Color.Blue
        )


        // 일괄 삭제 버튼
        Row(modifier = Modifier.padding(16.dp)) {
            Button(
                onClick = {
                    // 선택된 아이템들 삭제
                    val iterator = items.iterator()
                    var index = 0
                    while (iterator.hasNext()) {
                        iterator.next()
                        if (selectedItems[index]) {
                            iterator.remove()
                            selectedItems.removeAt(index)
                            isEditing.removeAt(index)
                            index--
                        }
                        index++
                    }
                },
                enabled = selectedItems.contains(true) // 선택된 아이템이 있을 때만 활성화
            ) {
                Text("선택된 아이템 전부 삭제")
            }
        }


        // 체크박스 리스트 표시
        LazyColumn {
            itemsIndexed(items, key = { _, item -> item }) { index, item ->
                val dismissState = rememberSwipeToDismissBoxState(
                    initialValue = SwipeToDismissBoxValue.Settled,
                    confirmValueChange = {
                        if (it != SwipeToDismissBoxValue.Settled) {
                            true
                        } else {
                            false
                        }
                    }
                )

                if (dismissState.currentValue != SwipeToDismissBoxValue.Settled) {
                    LaunchedEffect(dismissState.currentValue) {
                        if (dismissState.currentValue == SwipeToDismissBoxValue.EndToStart ||
                            dismissState.currentValue == SwipeToDismissBoxValue.StartToEnd
                        ) {
                            // 삭제할 때 리스트 인덱스가 변경되지 않도록 지연시키는 방식 사용
                            items.removeAt(index)
                            selectedItems.removeAt(index)
                            isEditing.removeAt(index)
                        }
                    }
                }



                SwipeToDismissBox(
                    state = dismissState,
                    backgroundContent = {
                        Box(
                            modifier = Modifier
                                .fillMaxSize()
                                .background(Color.LightGray)
                                .padding(6.dp)
                        )
                    },
                    content = {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .padding(vertical = 8.dp)
                                .clickable {
                                    selectedItems[index] = !selectedItems[index] // 체크박스 상태 토글
                                }
                                .padding(16.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Checkbox(
                                checked = selectedItems[index],
                                onCheckedChange = { checked -> selectedItems[index] = checked }
                            )
                            if (isEditing[index]) {
                                // 편집 모드일 때 TextField와 확인, 취소 버튼 표시
                                var editText by remember { mutableStateOf(item) }

                                // 새로 추가.
                                var isTyping by remember { mutableStateOf(false) }
                                val scope = rememberCoroutineScope()


                                Column(
                                    modifier = Modifier
                                        .fillMaxWidth()
                                        .padding(vertical = 8.dp)
                                ) {
                                    TextField(
                                        value = editText,
                                        onValueChange = { newValue ->
                                            editText = newValue
                                            isTyping = true

                                            // 입력 후 1초동안 타이핑이 없으면 초기화
                                            scope.launch {
                                                kotlinx.coroutines.delay(1000)
                                                isTyping = false
                                            }
                                        },
                                        modifier = Modifier.fillMaxWidth(),
                                        singleLine = true,
                                        label = { Text("아이템 수정") }
                                    )

                                    // 새로 추가한 부분
                                    // 타이핑 중일때
                                    if (isTyping) {
                                        Text(
                                            text = "작성 중입니다...",
                                            color = Color.Gray
                                        )
                                    }
                                    Row {
                                        // 확인 버튼
                                        Button(onClick = {
                                            items[index] = editText // 편집 후 값을 저장
                                            isEditing[index] = false // 편집 모드 종료
                                        }) {
                                            Text("확인")
                                        }
                                        Spacer(modifier = Modifier.width(4.dp))
                                        // 취소 버튼
                                        Button(onClick = {
                                            isEditing[index] = false // 편집 모드 종료
                                        }) {
                                            Text("취소")
                                        }
                                    }
                                }
                            } else {
                                // 기본 모드일 때 Text와 수정 버튼 표시
                                Text(
                                    text = item,
                                    color = Color.Black,
                                    modifier = Modifier
                                        .weight(1f)
                                )
                                Spacer(modifier = Modifier.width(8.dp))
                                Button(onClick = {
                                    isEditing[index] = true // 수정 버튼 클릭 시 편집 모드로 전환
                                }) {
                                    Text("수정")
                                }
                            }

                            Spacer(modifier = Modifier.weight(1f)) // X 표시를 오른쪽으로 정렬하기 위한 spacer

                            // 기존 삭제 버튼 유지
                            IconButton(onClick = {
                                items.removeAt(index)
                                selectedItems.removeAt(index)
                                isEditing.removeAt(index)
                            }) {
                                Icon(
                                    imageVector = Icons.Default.Close,
                                    contentDescription = "삭제",
                                    tint = Color.Red
                                )
                            }
                        }
                    }
                )
            }
        }
    }
}


@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    Kotlin_exampleTheme {
        ItemList(cnt = 100)
    }
}

 

 

시연 영상입니다 

 

 

감사합니다