1
\$\begingroup\$

I want to use the android component autocomplete:

https://developer.android.com/reference/android/widget/AutoCompleteTextView

There are some possible values the user can select, but I also want that, the user starts typing and, if there is no match with any of the "possible" optionsm, then, the text the user wrote is also valid. So, to put an example, imagine we have an autocomplete to select favourite fruit, and the options are Banana, Apple, but the user types Pear, then, it should be fine.

For that, I did this:

mAutocompleteTextView = findViewById(R.id.select_fruit_autoCompleteTextView)
mAutocompleteTextView.threshold = 1

val fruitListener = OnFruitSet()
// if the user clicks in one of the options
mAutocompleteTextView.onItemClickListener = fruitListener
// if the user writes a different option
mAutocompleteTextView.addTextChangedListener(fruitListener)

And then, my OnFruitSet private inner class:

private inner class OnFruitSet: AdapterView.OnItemClickListener, TextWatcher {

        private val textModified = AtomicBoolean(false)
        // this is used to change focus if the user finishes (by clicking on one of the options, or in the go button, next, etc. but it isn't working very good)
        private val goToNext = AtomicBoolean(false)

        override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
            // this means that the fruit is an option
            listeners.forEach{ it.onFruitSet(parent?.adapter?.getItem(position), true) }

        }

        override fun afterTextChanged(s: Editable?) {
            if (textModified.get()) {
                textModified.set(false)
                goToNext.set(true)
// right now the user can add new lines, and I want to avoid it.
                mAutocompleteTextView.setText(s?.replace("[\n]".toRegex(), ""))
            } else {
                goToNext.set(false)
            }

        }

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            val name = if (s?.contains('\n') == true) {
                textModified.set(true)
                s.replace("[\n]".toRegex(), "")
            } else {
                s
            }

            if (!textModified.get()) {
                val fruit = if (name.isNullOrEmpty() || name.isNullOrBlank()) {
                    null
                } else {
                    s.toString()
                }
                listeners.forEach{ it.onFruitSet(fruit, goToNext.get()) }
            }

        }

    }

There are some corner cases that don't work very good, specially with the \n, and when the user clicks on "Next".

Any hint on how to improve it? or is there any component already doing this?

Thanks

\$\endgroup\$
  • \$\begingroup\$ Question, because I don't know everything about Android... when s.replace("[\n]".toRegex(), "") is called, it is the last thing that onTextChanged does (as textModified is set). Does s.replace change the CharSequence itself? \$\endgroup\$ – tieskedh Jan 23 at 11:02
  • \$\begingroup\$ yes. It does. That's what I found the most problems \$\endgroup\$ – Manuelarte Jan 23 at 13:14
  • \$\begingroup\$ I searched further, and I believe it shouldn't: stackoverflow.com/a/6003815/3193776. In afterTextChanged, you are doing the same operation. Guess that's where the value is updated. Updating means the value is changed, so the loop starts over. \$\endgroup\$ – tieskedh Jan 23 at 14:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.