You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose开发Wordle游戏:OutlinedTextField字符不显示等问题求助

Wordle 游戏开发问题(Jetpack Compose)

我是Android新手,正在学习Jetpack Compose,首个任务是开发类似Wordle的游戏,用户需在N次尝试内猜出隐藏单词。

遇到以下问题:

  • 我使用OutlinedTextField构建猜词网格,计划用二维数组(String或Char类型)维护每个输入框的状态,更新状态以触发UI重组,但两种类型均未实现预期效果,请问此思路是否正确?
  • 无法定位代码Bug:通过println打印显示状态已更新为预期值,但OutlinedTextField多数时候不展示该数据,偶尔才显示。
  • 希望实现一个显示字母的自定义键盘,点击键盘组件时,对应字符填入当前聚焦的OutlinedTextField,请问应使用哪些UI组件,或是否有更合理的方案?

游戏状态数据类

// state of the Wordle Game
data class WordleState(
    val currentGuess: ArrayList<CharacterToGuess>, //holds the word being guessed, on value change, UI is supposed to get recompose
    var gameState: GameState = GameState.ONGOING,
    val wordToGuess: String = getWordFromWordBank(),
    var remainingAttempts: Int = MAX_GUESSES_ALLOWED,
    /*var currentGuess: MutableList<MutableList<String>> = MutableList(MAX_GUESSES_ALLOWED) {
        MutableList(MAX_LETTERS_LENGTH) {""}
    },*/
    var selectedRowIndex: Int = 0
) {
    companion object {
        fun getWordFromWordBank(): String { //TODO: move to the WordFactory
            val wordBank: List<String> = mutableListOf("AUDIO", "VIDEO", "KITES" )
            return wordBank[Random.nextInt(0, wordBank.size-1)]
        }

        var MAX_GUESSES_ALLOWED: Int = 1
        var MAX_LETTERS_LENGTH: Int = 5
    }

    override fun equals(other: Any?): Boolean { //Android studio suggested this override
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as WordleState

        if (gameState != other.gameState) return false
        if (wordToGuess != other.wordToGuess) return false
        if (remainingAttempts != other.remainingAttempts) return false
        if (!currentGuess.contentEquals(other.currentGuess)) return false
        if (selectedRowIndex != other.selectedRowIndex) return false

        return true
    }

    override fun hashCode(): Int { //Android studio suggested this override
        var result = gameState.hashCode()
        result = 31 * result + wordToGuess.hashCode()
        result = 31 * result + remainingAttempts
        result = 31 * result + currentGuess.contentHashCode()
        result = 31 * result + selectedRowIndex
        return result
    }

}

ViewModel中处理输入的代码

class WordleViewModel: ViewModel() {
    private val _wordleState = MutableLiveData(WordleState())
    var state: LiveData<WordleState> = _wordleState

...

    fun updateCurrentGuessLetter(it: String, col: Int) {
        val newGuessLetter = it.first()
        println("onValueChange before: " +
                "Letter:`$it`" +
                " currentGuess:${state.value!!.currentGuess.joinToString(separator = "")}," +
                " states:${state.value}")

        val updatedGuess = _wordleState.value!!.copy().currentGuess
        updatedGuess[col] = newGuessLetter
        _wordleState.value = _wordleState.value!!.copy(
            currentGuess = updatedGuess
        )

        println("onValueChange after: " +
                "Letter:`$it`" +
                " currentGuess:${state.value!!.currentGuess.joinToString(separator = "")}," +
                " states:${state.value}")
    }


...

}

MainActivity中的Composable代码

fun WordleGrid(viewModel: WordleViewModel, state: WordleState) {
    val context = LocalContext.current.applicationContext
    val focusManager = LocalFocusManager.current

    Column(modifier = Modifier.fillMaxWidth()) {
        for (row in 0 until WordleState.MAX_GUESSES_ALLOWED) {
            Row(modifier = Modifier
                .fillMaxWidth()
                .height(50.dp)) {
                for (col in 0 until WordleState.MAX_LETTERS_LENGTH) {
                    MyOutlinedTextField(
                        modifier = Modifier.weight(1f),
                        viewModel = viewModel,
                        state = state,
                        row = row, col = col
                    )
                }
            }
        }

        CheckButton(
            onClick = { viewModel.onSubmitWord() }
        )
    }
}

@Composable
fun MyOutlinedTextField(
    modifier: Modifier,
    viewModel: WordleViewModel,
    state: WordleState,
    row: Int,
    col: Int,
) {
    val isEnabled = (row == state.selectedRowIndex) //state.currentGuess[col].toString(),
    val focusManager = LocalFocusManager.current

    OutlinedTextField(
        //value = state.currentGuess[row][col], //for state as 2D string array
        value = state.currentGuess[col].toString(),
        enabled = isEnabled,
        onValueChange = {
            if (it.isNotBlank() && it.isNotEmpty()) {
                //viewModel.updateCurrentGuessLetter(newLetter.first().toString(), row, col)
                viewModel.updateCurrentGuessLetter(it, col)

                if(col == WordleState.MAX_LETTERS_LENGTH - 1) {
                    viewModel.onSubmitWord()
                } else {
                    focusManager.moveFocus(FocusDirection.Right)
                }
            } else {
                println("onValueChange: Ignoring blank/empty letter")
            }
        },
        singleLine = true,
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
            .then(modifier),
        colors = TextFieldDefaults.outlinedTextFieldColors(
            focusedBorderColor = Color.Blue,
            unfocusedBorderColor = Color.DarkGray
        ),
        textStyle = TextStyle(
            color = Color.Red,
            textAlign = TextAlign.Center,
            //fontSize = 28.sp,
        ),
        keyboardOptions = KeyboardOptions.Default.copy(
            capitalization = KeyboardCapitalization.Characters,
            autoCorrect = false,
            keyboardType = KeyboardType.Text,
            /*imeAction = (
                    if (col == WordleState.MAX_LETTERS_LENGTH - 1) ImeAction.Done
                    else ImeAction.Next
            )*/
        ),
        keyboardActions = KeyboardActions(
            onNext = {
                // Move focus to the next outlined text field
                focusManager.moveFocus(FocusDirection.Right)
            },
            onDone = {
                // Submit the word when the last outlined text field is completed
                viewModel.onSubmitWord()
            }
        ),
    )
}

若需更多信息,请告知,感谢您的帮助。

内容的提问来源于stack exchange,提问作者user3067435

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:24:56