Hide keyboard when Edit Text in Recycler View is scrolled off screen - android

I have a RecyclerView that contains EditText child elements. I would like to hide the soft keyboard when the selected EditText is scrolled off screen. How can I tell when the EditText is no longer on screen? Is there some event listener that I can attach to the EditText element to tell?

Implement onTouchListener like this:
yourRecycleView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
return false;
}
});

This is what worked for me : I used RecyclerView.OnScrollListener with PublishRelay for debouncing events.
class RecyclerViewActivity : Activity(){
...
private val scrollableRelay = PublishRelay.create<Unit>()
private val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
scrollableRelay.accept(Unit)
}
})
scrollableRelay
.debounce(100, TimeUnit.MILLISECONDS)
.subscribe({
if (currentFocus == recyclerView) {
hideKeyboard()
}
})
.addTo(disposable)
}
override fun onDestroy() {
disposable.onDestroy()
}
}
Once the view is scrolled from the screen the focus goes up to recyclerView.
So, we can implement this functionality using RecyclerView.OnScrollListener.
onScrolled tracks even slightest scroll of the view. That's why we need to add debounce that we'll not receive a lot of events.
hideKeyboard and addTo are extension functions:
fun Disposable.addTo(compositeDisposable: CompositeDisposable) {
compositeDisposable.add(this)
}
fun Activity.hideKeyboard() =
currentFocus?.let {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(it.windowToken, 0)
}

yourRecycleView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
edittext.clearFocus(); //hidden keyboard
return false;
}
});

My solution:
editText.setOnFocusChangeListener((v, hasFocus) -> {
Handler handler = new Handler();
if (!hasFocus) {
handler.postDelayed(() -> {
if (!editText.hasFocus()) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, 0);
}
}, 200);
}
});

Kotlin solution that dismisses the keyboard when:
the focused View is scrolled off the RecyclerView
or if the user presses the Enter key, and the OS was unable to find something to focus on that is inside the RecyclerView...
// dismiss the keyboard when it is no longer focusing on anything inside the recyclerview
recyclerView.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus: View? ->
if (newFocus == recyclerView || recyclerView !in (newFocus?.ancestors ?: emptySequence())) {
recyclerView.context.inputService.hideSoftInputFromWindow(recyclerView.windowToken, 0)
}
}
val Context.inputService get() = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val View.ancestors: Sequence<View> get() = generateSequence(this) { it.parent as? View }

Been facing the same issue in BottomSheet . Took me a while to realize that to get currentFocus i have to use dialog.currentFocus instead of activity.currentFocus . Below is my Solution maybe it helps someone .
binding.recyclerView.addOnScrollListener(object:RecyclerView.OnScrollListener(){
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
dialog?.currentFocus?.let {
if(it is RecyclerView){
val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(it.windowToken, 0)
}
}
}
}
})

Related

Clear focus on tap outside of EditText in BottomSheetDialogFragment

I want to clear focus of TextInputEditTexts when the user taps outside of them. In order to do that I use the code from this answer. Because that does not work in dialogs I added the following code in my bottom sheet, as described here:
class MySheet(): BottomSheetDialogFragment() {
...
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireActivity(), theme) {
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val v = currentFocus
if (v is TextInputEditText) {
val outRect = Rect()
v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus()
val imm: InputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.getWindowToken(), 0)
}
}
}
return super.dispatchTouchEvent(event)
}
}
}
}
But now, the bottom sheet is displayed as a normal dialog with margins on all sides. How can I force the normal behaviour of a modal bottom sheet?

I want to hide the keyboard

i want to hide the keyboard but i want to write it in a class. To use it for all activities. ı need a edit text delete focus code
class Extensions(){
fun hideSoftKeyboard(view: View) {
val imm =getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
problem description
No value passed for parameter 'serviceClass'
Type mismatch: inferred type is String but Context was expected
new code: but I couldn't do the outer click event
fun Activity.hideKeyboard() {
val view = currentFocus
if (view != null) {
view.clearFocus()
val inputMethodManager =
getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(
view.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
You can make an Extenstion function on the edit text that would do that for you
fun EditText.hideKeyboard() {
clearFocus()
val imm = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(getWindowToken(), 0)
}
I haven't tested it out yet, please test that one out and tell me if it does work.
Extensions.kt
fun Activity.hideKeyboard(event: MotionEvent) {
if (event.action == MotionEvent.ACTION_DOWN){
val view = currentFocus
if (view != null) {
view.clearFocus()
val outRect = Rect()
view.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
val inputMethodManager =
getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(
view.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS
)
}
}
}
}
YouActivity
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
hideKeyboard(event)
return super.dispatchTouchEvent(event)
}
// on below line getting current view.
val view: View? = this.currentFocus
// on below line checking if view is not null.
if (view != null) {
// on below line we are creating a variable
// for input manager and initializing it.
val inputMethodManager =
getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
// on below line hiding our keyboard.
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0)
// displaying toast message on below line.
Toast.makeText(this, "Key board hidden", Toast.LENGTH_SHORT).show()
if you are using Jetpack Compose;
#Composable
fun MainScreenView(){
val keyboardController = LocalSoftwareKeyboardController.current
keyboardController.hide()

Android kotlin - EditText close activity onBackPressed

This is what I've tried:
override fun onBackPressed() {
nicknameInput.clearFocus()
finish()
}
It only closes soft keyboard but not activity and it also not clears the focus.
The solution here:
Android - onBackPressed close soft keyboard AND activity
only works for SearchView
EDIT:
This is almost the whole activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_set_nickname)
prefs = getSharedPreferences("userData", 0)
val preset = intent.getStringExtra("preset")
nicknameInput.setText(preset)
openSoftKeyboard(this, nicknameInput)
nicknameBG.setOnClickListener {
finish()
}
btnNotNowNickname.setOnClickListener {
finish()
}
btnSaveNickname.setOnClickListener {
checkNickname()
}
nicknameInput.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
// Log.d("pikabo", keyCode.toString())
if(keyCode == KeyEvent.KEYCODE_ENTER) {
checkNickname()
return#OnKeyListener true
}
false
})
} // ON CREATE
private fun openSoftKeyboard(context: Context, view: View) {
view.requestFocus()
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
}
Only the function checkNickname() is missing which isn't necessary here for sure
Using this you can listen for key pressed on edittext and pass your edittext view to hide function to close it.
replace view with your edittext reference.
view.setOnKeyListener(object : View.OnKeyListener {
override fun onKey(p0: View?, keyCode: Int, event: KeyEvent?): Boolean {
if (event!!.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK ) {
hide(view)
onBackPressed()
return true
}
return false
})
and using it you can close your keyboard
private fun hide(editText: EditText) {
try {
val inputMethodManager = this.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0)
} catch ( e:Exception) {
Log.d("TAG",e.message+"")
}
}

How to do generic onTouchEvent method for kotlin?

I have such a method. In loginactivity.kt. What I want to do is make this method generic and use it everywhere. I want to write a method in CommonExtensions.kt, but I can't write it right and I get an error. How can I make Generic become
LoginActivity.kt
override fun onTouchEvent(event: MotionEvent?): Boolean {
val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(this.currentFocus?.windowToken, 0)
return super.onTouchEvent(event)
}
CommonExtensions.kt
fun Context.onTouchEvent(event: MotionEvent?): Boolean {
val imm = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(this.onTouchEvent()?.windowToken, 0)
}
You have a couple of options:
Create a basic class for Activities, for example BaseActivity and override onTouchEvent method there. Inherit from that activity other activities.
Create some util file, for example UiUtils.kt and define method of hiding keyboard there, e.g.:
fun hideKeyboard(view: View) {
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
Call it from your activities' onTouchEvent() method:
override fun onTouchEvent(event: MotionEvent?): Boolean {
hideKeyboard(someView)
return super.onTouchEvent(event)
}
Create extension function on View in your CommonExtensions.kt file:
fun View.hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0)
}
And call it from onTouchEvent() method:
override fun onTouchEvent(event: MotionEvent?): Boolean {
someView.hideKeyboard()
return super.onTouchEvent(event)
}

Close/hide the Android Soft Keyboard with Kotlin

I'm trying to write a simple Android app in Kotlin. I have an EditText and a Button in my layout. After writing in the edit field and clicking on the Button, I want to hide the virtual keyboard.
There is a popular question Close/hide the Android Soft Keyboard about doing it in Java, but as far as I understand, there should be an alternative version for Kotlin. How should I do it?
Use the following utility functions within your Activities, Fragments to hide the soft keyboard.
(*)Update for the latest Kotlin version
fun Fragment.hideKeyboard() {
view?.let { activity?.hideKeyboard(it) }
}
fun Activity.hideKeyboard() {
hideKeyboard(currentFocus ?: View(this))
}
fun Context.hideKeyboard(view: View) {
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
}
This will close the keyboard regardless of your code either in dialog fragment and/or activity etc.
Usage in Activity/Fragment:
hideKeyboard()
I think we can improve Viktor's answer a little. Based on it always being attached to a View, there will be context, and if there is context then there is InputMethodManager:
fun View.hideKeyboard() {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(windowToken, 0)
}
In this case the context automatically means the context of the view.
What do you think?
Simply override this method in your activity. It will automatically works in its child fragments as well.....
In JAVA
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (getCurrentFocus() != null) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
return super.dispatchTouchEvent(ev);
}
In Kotlin
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (currentFocus != null) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currentFocus!!.windowToken, 0)
}
return super.dispatchTouchEvent(ev)
}
In your Activity or Fragment create a function as:
fun View.hideKeyboard() {
val inputManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputManager.hideSoftInputFromWindow(windowToken, 0)
}
suppose you have a button with an id your_button_id in XML file related to this Activity or Fragment, so, on button click event:
your_button_id.setOnClickListener{
it.hideKeyboard()
}
Peter's solution solves neatly the problem by extending functionality of View class. Alternative approach could be to extend functionality of Activity class and thus bind operation of hiding keyboard with View's container rather than View itself.
fun Activity.hideKeyboard() {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(findViewById(android.R.id.content).getWindowToken(), 0);
}
I didn't see this variant of Kotlin extension function:
fun View.hideSoftInput() {
val inputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
}
Its benefit is that this extension function could be called from every CustomView and in every click or touch listener
Make an object class named Utils:
object Utils {
fun hideSoftKeyBoard(context: Context, view: View) {
try {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm?.hideSoftInputFromWindow(view.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
} catch (e: Exception) {
// TODO: handle exception
e.printStackTrace()
}
}
}
You can use this method in any class where you want to hide the soft input keyboard. I am using this in my BaseActivity.
Here the view is any view that you use in your layout:
Utils.hideSoftKeyBoard(this#BaseActivity, view )
You can use Anko to make life easier, so the line would be:
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
or maybe better to create extension function:
fun View.hideKeyboard(inputMethodManager: InputMethodManager) {
inputMethodManager.hideSoftInputFromWindow(windowToken, 0)
}
and call it like this:
view?.hideKeyboard(activity.inputMethodManager)
Although there are many answers but this answer is related to a best practice in KOTLIN by opening and closing the keyboard with life cycle and extension function.
1). Create Extension Functions create a file EditTextExtension.kt and paste the below code
fun EditText.showKeyboard(
) {
requestFocus()
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
}
fun EditText.hideKeyboard(
) {
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager
imm.hideSoftInputFromWindow(this.windowToken, 0)
}
2). Create LifeCycleObserver Class Create a class EditTextKeyboardLifecycleObserver.kt and paste the code below
class EditTextKeyboardLifecycleObserver(
private val editText: WeakReference<EditText>
) :
LifecycleObserver {
#OnLifecycleEvent(
Lifecycle.Event.ON_RESUME
)
fun openKeyboard() {
editText.get()?.postDelayed({ editText.get()?.showKeyboard() }, 50)
}
fun hideKeyboard() {
editText.get()?.postDelayed({ editText.get()?.hideKeyboard() }, 50)
}
}
3). Then use the below code in onViewCreated / onCreateView
lifecycle.addObserver(
EditTextKeyboardLifecycleObserver(
WeakReference(mEditText) //mEditText is the object(EditText)
)
)
The Keyboard will open when the user opens the fragment or activity.
if you occur any problems, following the solution feel free to ask in the comment.
Here is my solution in Kotlin for Fragment. Place it inside setOnClickListener of the button.
val imm = context?.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager?
imm?.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0)
I found the answer that worked for me here: http://programminget.blogspot.com/2017/08/how-to-close-android-soft-keyboard.html
val inputManager:InputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputManager.hideSoftInputFromWindow(currentFocus.windowToken, InputMethodManager.SHOW_FORCED)
This works well with API 26.
val view: View = if (currentFocus == null) View(this) else currentFocus
val inputMethodManager = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
Write a function to hide the keyboard:
private fun hideKeyboard(){
// since our app extends AppCompatActivity, it has access to context
val imm=getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
// we have to tell hide the keyboard from what. inorder to do is we have to pass window token
// all of our views,like message, name, button have access to same window token. since u have button
imm.hideSoftInputFromWindow(button.windowToken, 0)
// if you are using binding object
// imm.hideSoftInputFromWindow(binding.button.windowToken,0)
}
You have to call this function whereever u need
Thanks to #Zeeshan Ayaz
Here is a little improved version
Because 'currentFocus' is nullable we better check it using Kotlin's ?.let
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
currentFocus?.let { currFocus ->
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(currFocus.windowToken, 0)
}
return super.dispatchTouchEvent(ev)
}
You can use from bellow code, I write bellow code in my fragment:
private val myLayout = ViewTreeObserver.OnGlobalLayoutListener {
yourTextView.isCursorVisible = KeyboardTool.isSoftKeyboardShown(myRelativeLayout.rootView)
}
Then in onViewCreated of fragment:
......
super.onViewCreated(view, savedInstanceState)
myRelativeLayout.viewTreeObserver.addOnGlobalLayoutListener(myLayout)
......
And in onDestroyView use too:
override fun onDestroyView() {
super.onDestroyView()
myRelativeLayout.viewTreeObserver.removeOnGlobalLayoutListener(myLayout)
}
And:
object KeyboardTool {
fun isSoftKeyboardShown(rootView: View): Boolean {
val softKeyboardHeight = 100
val rect = Rect()
rootView.getWindowVisibleDisplayFrame(rect)
val dm = rootView.resources.displayMetrics
val heightDiff = rootView.bottom - rect.bottom
return heightDiff > softKeyboardHeight * dm.density
}
}
Kotlin
I use bellow code:
import splitties.systemservices.inputMethodManager
inputMethodManager.hideSoftInputFromWindow(view?.windowToken, 0)
You can use a Function Extension in Kotlin. Replace activity by fragment if you need make it in Fragment.
fun Activity.hideKeyboard() {
hideKeyboard(currentFocus ?: View(this))
}
Hello I frequently use these two extension functions for showing and hiding soft keyword.
Show Soft Keyboard
fun Any.showSoftKeyboard(view: View, context: Context) {
if (view.requestFocus()) {
val imm: InputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
}
}
Hide Soft Keyboard
fun Any.hideSoftKeyboard(view: View, context: Context) {
val imm =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as
InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
You can use these methods in any Object Class to access these globally or you can make separate Extensions/CommonUtils Files for these.

Categories

Resources