Santhosh Kumar
Santhosh Kumar

Reputation: 531

How to avoid Keyboard Options for Text Field in Compose

I have text field and it has its own custom input UI on the way. The Problem is i need to use my custom input, and do not want the keyboard to open when focus is gained on the text field. I don't want to set the text field as read only because it removes the ability to focus and edit, But i need the text field to be focusable and editable.

  TextField(
            value = text,
            onValueChange = { newText ->
                // Update the text with validation for digits
                if (newText.all { it.isDigit() } && newText.length <= 10) {
                    text = newText
                }
            },
            keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
            modifier = Modifier.fillMaxWidth(),
            label = { Text("Enter numbers") }
        )


Upvotes: 1

Views: 80

Answers (1)

Daksh Rawal
Daksh Rawal

Reputation: 530

okay so what i understand is you need a custom input ( probably something similar to custom on-screen keyboard ) instead of the traditional soft-keyboard

FOLLOW THE BELOW FOR JETPACK COMPOSE


Define an interactionSource

Read more

val interactionSource = remember { MutableInteractionSource() }

Add an interactionSource to your TextField

TextField(
    value = text,
    onValueChange = { newText ->
        // Update the text with validation for digits
        if (newText.all { it.isDigit() } && newText.length <= 10) {
            text = newText
        }
    },
    keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
    modifier = Modifier.fillMaxWidth(),
    label = { Text("Enter numbers") },

    interactionSource = interactionSource // add an interactionSource
)

Define a coroutineScope

Read more

val coroutineScope = rememberCoroutineScope()

Access the soft-keyboard using LocalSoftwareKeyboardController

Read more

val keyboardController = LocalSoftwareKeyboardController.current

Handle the release event

Read more

LaunchedEffect(interactionSource) {
    interactionSource.interactions.collect { interaction ->
        when (interaction) {
            is PressInteraction.Release -> {
                coroutineScope.launch {
                    keyboardController?.hide() // Hide the keyboard on finger release ( similar to the end event of tap, touch, click )
                    Log.d("MyTag","KeyBoard Hidden")
                }
            }
        }
    }
}

so basically everytime an Release event ( Read more ) is called, then hide the soft-keyboard using keyboardController?.hide(),why am i using release event is because it refers to the pointer/finger being lifted up after press,touch,click,tap etc.. , it may sometimes trigger Cancel event ( Read more ), but when cancel event is triggered, it means the previous event such as press is cancelled, if cancelled it wont trigger the soft-keyboard to popup, and release means the event has been occurred => triggering the popup of soft-keybaord, so when the popup of soft-keyboard is triggered on Release Event then => hide the soft-keyboard using keyboardController?.hide()


FOLLOW THE BELOW FOR TRADIONAL UI / XML LAYOUT / IMPERATIVE UI

so for your requirement that is hiding/closing the soft-keyboard for the edit-text ( textField ), you have make few changes to setOnTouchListener , setOnFocusChangeListener and addTextChangedListener

Follow the below steps and add the following inside onCreate method


Access the edit-text ( textField ) and the soft-keyboard object is saved to the imm variable

val editText: EditText = findViewById(R.id.editTextText)
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

setup an OnTouchListener, and return true, returning true means or tells other OnTouchListener to not perform actions on this field, ( meaning the event has been captured/handled by this listener, no need for further operations ), and the call .requestFocus(), to display the blinking cursor
editText.setOnTouchListener { _, _ ->
    editText.requestFocus()
    true
}

setup an OnFocusChangeListener, and return true, ( same as OnTouchListener ), and using hideSoftInputFromWindow to hide the keyboard

editText.setOnFocusChangeListener{ _, _ ->
    imm.hideSoftInputFromWindow(editText.windowToken, 0)
    true
}

setup an addTextChangedListener, to set the blinking cursor position at the end of text present in the text field, and added hideSoftInputFromWindow to hide/close keyboard if open, everytime the text is changed

editText.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(
        charSequence: CharSequence?,
        start: Int,
        count: Int,
        after: Int
    ) {
        // Code to handle before text change
    }

    override fun onTextChanged(
        charSequence: CharSequence?,
        start: Int,
        before: Int,
        count: Int
    ) {
        imm.hideSoftInputFromWindow(editText.windowToken, 0)
        editText.setSelection(editText.text.length)
    }

    override fun afterTextChanged(editable: Editable?) {
        // Code to handle after text change
    }
})

so we are overriding the setOnTouchListener and setOnFocusChangeListener methods to prevent the default behaviour of the text field, of

  • focusing ( showing cursor and visually appearing to be selected )
  • showing soft-keyboard
  • setting the cursor at the end of the text present in the field

and then making required changes to add the following feature to the text field

  • focusing ( showing cursor and visually appearing to be selected )
  • setting the cursor at the end of the text present in the field

above i used hideSoftInputFromWindow to hide the keyboard, which was not necessary, but considering the conditions of screen rotation ( landscape to potrait ) or if some way the soft-keyboard is popped up, it will automatically hide/close it upon interacting with any text field


you could achieve the same using a less simple and small code, BUT THAT IS MESSY AND GIVES A BUGGY FEELING OF UI

editText.setOnFocusChangeListener{ view, _ ->
    val handler = Handler(Looper.getMainLooper())
    handler.postDelayed({
        val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
        imm.hideSoftInputFromWindow(editText.windowToken, 0)
    }, 1)
}

the above code provides the required solution but randomly show the soft-keyboard for a split second and then hides it, ( it doesnt look visually appealing, you could still try it if needed )


I hope you found this useful, informative, solved your problem and met your specific requirements, Any doubts related to anything feel free to ask

Upvotes: 1

Related Questions