Close keypad when touch or click outside of EditText in Android - android

For closing the keypad when touches or clicks the outside of the Edittext, i followed the below code. if there is one edittext its working correctly but more than one its not working.
Example Scenario:
Step 1 : Clicking the first edittext it opens the keypad.
Step 2 : Clicking outside of the first edittext it closes the keypad.
Step 3 : Clicking the second edittext it doesn't open the keypad.
i think it consider second edittext as a outside of first edittext. i dont know really... can someone please help me...
and this code is for all edittext field in a activity we can use, we can do by using specific edittext also, but i don't want to find a outside clicks or touches of a specific edittext.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View view = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (view instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < w.getLeft() || x >= w.getRight()
|| y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
}
}
return ret;
}

Please try with following code:
public static void hideSoftKeyboard(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity
.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (inputMethodManager.isAcceptingText()) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED,
0);//You need to change the focus here to another view for hide the keyboard.
}
}
You need to call the above method as follows :
Here we are checking that if edittext object has focus then call below method.
if (etSeach.hasFocus()) {
hideSoftKeyboard(ViewTaskActivity.this);
}

Related

android - show keyboard fix bottom framelayout

I am developing an android application in which my mainActivity has 5 tabs at the bottom represented in FrameLayout. Each tab points to a particular fragment which gets loaded based on the tab clicked.
In my 1st tab, I've an EditText. When the edit text is focused, I need to show the keyboard by just moving the layout of ('EditText and Send button) "up" without pushing up the other components including the bottom tab frame layout. Similarly when hiding the keyboard, the 'EditText and Send btn' layout should sit just above the tab frame layout. Can someone please suggest me the solution for this?
(Assume the situation same as in Facebook comment. When we go inside a post to comment something, the layout ('Write Comment' ET and POST btn) will be pushed up without moving any other components including the bottom tabs ('News Feed', 'Requests', 'Messenger', 'Notifications', 'More'))
I don't want to make the frame layout Visibility.Gone/Visible as it ends up in an ugly animation when the keyboard slides up/down.
Use a LinearLayout whit no weightSum, and give your main View in your layout 0dp height, and weight 1
I have the exact same issue. The best workaround I could find (as alluded to in the original question) is below. But this is a marginal solution, and not a great one.
The problem with setting the weightSum is that if the scrollview containing the edit boxes is longer, the button will disappear. The best solution would allow the buttons to stay at bottom of page on normal view, but to be hidden during keyboard onscreen.
public boolean dispatchTouchEvent(MotionEvent event) {
View v = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (v instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP && (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
layButton.setVisibility(View.VISIBLE);
}
else
{
layButton.setVisibility(View.GONE);
}
}
return ret;
}

Android : keyboard show/hide with multiple EditText

I have an activity with a listview and each row has several editText. It looks like a questionnaire.
I have for example the focus on one EditText, then I click on another EditText. At the moment, the keyboard disappear and reappear. It is very irritating.
I use that code to make the keyboard disppear when the user click anywhere else on the activity when he don't want to type. But if the user click on another editText, I would like the keyboard stay.
Someone has an idea if the keyboard is already shown, how to keep it open when I click on a EditText?
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View w = getCurrentFocus();
if (w instanceof EditText) {
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP && (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
}
}
return super.dispatchTouchEvent(event);
}
Make sure you're setting your android:inputType on your EditText declarations in XML. If this doesn't solve the issue by itself, you can specify next focuses using the View class's android:nextFocusUp, android:nextFocusDown, android:nextFocusRight, or android:nextFocusLeft to indicate the next EditText to move to.
Lastly, it could have something to do with how the ListView is handling focus. If that's the case, there's a related question here that would probably be worth looking at.
Would be helpful if you could share your code.
I think what is happening is that your listview is causing your edit text to lose focus when you tap another one on a different row.
In general, what you could do is to register on a focus change listener on your edit text and then every time your edit text loses focus you can run a function to display the keyboard again.
replyInputEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
showKeyboard(editText);
}
});
The showkeyboard function :
public void showKeyboard(EditText field){
field.setFocusable(true);
field.setFocusableInTouchMode(true);
field.requestFocus();
InputMethodManager imm = (InputMethodManager) activity.getSystemService(activity.getApplicationContext().INPUT_METHOD_SERVICE);
imm.showSoftInput(field, InputMethodManager.SHOW_IMPLICIT);
}
You could also register an ontouchup listener on the text fields and then calling the showKeyboard function.

How to avoid dispatchTouchEvent being called when click button

In my Activity, I have an EditText and a Button. I overrided the dispatchTouchEvent to hide the soft keyboard when click the other area of the screen than the EditText.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View view = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (view instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w
.getBottom())) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus()
.getWindowToken(), 0);
}
}
return ret;
}
The issue is, every time I click the Button, the dispatchTouchEvent will be called, so the soft keyboard will disappear, which is not what I want. I don't want to hide the keyboard when I click the Button. What I'm thinking is that is it possible to avoid calling the dispatchTouchEvent when I click the Button?
Any feedback will be highly appreciated.
You don't need to override the dispatchTouch event. Just set the onClickListener of you activity to the same with the Button and EditText, then call the methods to hide the softkeyboard on the "default" case of the switch case.
switch (v.getId())
{
case R.id.button:
whateverButtonDoes();
break;
case R.id.editText:
whateverEditTextDoes();
break;
default:
hideKeyboard();
break;
}
I strongly suggest you define a method to hide the keyboard for clear coding standards. Also you might use it elsewhere.

Frame layout getting stuck when I hide soft input (Android)

I have an Android app (fullscreen, landscape), and I have two Views, one is a FrameLayout that's always visible, and one's a LinearLayout. I set the visibility on the LinearLayout to View.GONE right off the bat. When the user taps a button on the FrameLayout, I set visibility on the LinearLayout to view.VISIBLE, and use the InputMethodManager to show the soft input on an EditText in the LinearLayout. This results in both views being shifted up such that the EditText is on screen just above the keyboard. This is all fine.
The problem arises when I close the soft input. In the same callback, I set the visibility of the LinearLayout (with the EditText) to View.GONE, and what I'm seeing is the FrameLayout getting stuck on top of the screen (it doesn't shift back down). If I don't GONE-ize the LinearLayout, both Views shift back down just fine.
I can work around the issue by starting a timer to wait before setting the visibility to GONE, but I shouldn't have to do that; there should be some notification that I can receive consistently that will indicate when I can hide the LinearLayout.
Things I've tried: hideSoftInputFromWindow with a ResultReceiver (result receiver callback gets invoked pretty well immediately (i.e. before the keyboard actually disappears), view is GONEd too quickly, same problem), overriding onLayout for the LinearLayout (doesn't get invoked when the soft input moves the View up and down), overriding other View "on"s.
The other really annoying thing is this problem seems to be exclusive to my phone - an AT&T Samsung Galaxy SII Skyrocket running Android 4.0.4 (possibly with some Samsung modifications). I can't repro on a 4.0.3 simulator, nor can anyone else repro on other devices with other Android versions.
Any ideas?
You're going to have to write your own touch event handler. Here is an example that will slide the whole frameLayout view down when the soft input is hidden. As you can see, it is important to call the super handler and wrap our code in a test for textEdit widgets. If you want to get a look at the calculations, uncomment the debugging logger code.
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View v = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (v instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
// Log.d("Activity",
// "Touch event "+event.getRawX()+","+event.getRawY()+" "+x+","+y+" rect "+w.getLeft()+","+w.getTop()+","+w.getRight()+","+w.getBottom()+" coords "+scrcoords[0]+","+scrcoords[1]);
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < w.getLeft() || x >= w.getRight() || y < w.getTop() || y > w
.getBottom())) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus()
.getWindowToken(), 0);
}
}
return ret;
}

How to hide soft keyboard on android after clicking outside EditText?

Ok everyone knows that to hide a keyboard you need to implement:
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
But the big deal here is how to hide the keyboard when the user touches or selects any other place that is not an EditText or the softKeyboard?
I tried to use the onTouchEvent() on my parent Activity but that only works if user touches outside any other view and there is no scrollview.
I tried to implement a touch, click, focus listener without any success.
I even tried to implement my own scrollview to intercept touch events but I can only get the coordinates of the event and not the view clicked.
Is there a standard way to do this?? in iPhone it was really easy.
The following snippet simply hides the keyboard:
public static void hideSoftKeyboard(Activity activity) {
InputMethodManager inputMethodManager =
(InputMethodManager) activity.getSystemService(
Activity.INPUT_METHOD_SERVICE);
if(inputMethodManager.isAcceptingText()){
inputMethodManager.hideSoftInputFromWindow(
activity.getCurrentFocus().getWindowToken(),
0
);
}
}
You can put this up in a utility class, or if you are defining it within an activity, avoid the activity parameter, or call hideSoftKeyboard(this).
The trickiest part is when to call it. You can write a method that iterates through every View in your activity, and check if it is an instanceof EditText if it is not register a setOnTouchListener to that component and everything will fall in place. In case you are wondering how to do that, it is in fact quite simple. Here is what you do, you write a recursive method like the following, in fact you can use this to do anything, like setup custom typefaces etc... Here is the method
public void setupUI(View view) {
// Set up touch listener for non-text box views to hide keyboard.
if (!(view instanceof EditText)) {
view.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
hideSoftKeyboard(MyActivity.this);
return false;
}
});
}
//If a layout container, iterate over children and seed recursion.
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View innerView = ((ViewGroup) view).getChildAt(i);
setupUI(innerView);
}
}
}
That is all, just call this method after you setContentView in your activity. In case you are wondering what parameter you would pass, it is the id of the parent container. Assign an id to your parent container like
<RelativeLayoutPanel android:id="#+id/parent"> ... </RelativeLayout>
and call setupUI(findViewById(R.id.parent)), that is all.
If you want to use this effectively, you may create an extended Activity and put this method in, and make all other activities in your application extend this activity and call its setupUI() in the onCreate() method.
If you use more than 1 activity define common id to parent layout like
<RelativeLayout android:id="#+id/main_parent"> ... </RelativeLayout>
Then extend a class from Activity and define setupUI(findViewById(R.id.main_parent)) Within its OnResume() and extend this class instead of ``Activity in your program
Here is a Kotlin version of the above function:
#file:JvmName("KeyboardUtils")
fun Activity.hideSoftKeyboard() {
currentFocus?.let {
val inputMethodManager = ContextCompat.getSystemService(this, InputMethodManager::class.java)!!
inputMethodManager.hideSoftInputFromWindow(it.windowToken, 0)
}
}
You can achieve this by doing the following steps:
Make the parent view(content view of your activity) clickable and focusable by adding the following attributes
android:clickable="true"
android:focusableInTouchMode="true"
Implement a hideKeyboard() method
public void hideKeyboard(View view) {
InputMethodManager inputMethodManager =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Lastly, set the onFocusChangeListener of your edittext.
edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard(v);
}
}
});
As pointed out in one of the comments below, this might not work if the parent view is a ScrollView. For such case, the clickable and focusableInTouchMode may be added on the view directly under the ScrollView.
Just override below code in Activity
#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);
}
UPDATE:
In case someone needs the Kotlin version of this answer:
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
if (currentFocus != null) {
val imm = activity!!.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(activity!!.currentFocus!!.windowToken, 0)
}
return super.dispatchTouchEvent(ev)
}
I find the accepted answer a bit complicated.
Here's my solution. Add an OnTouchListener to your main layout, ie.:
findViewById(R.id.mainLayout).setOnTouchListener(this)
and put the following code in the onTouch method.
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
This way you don't have to iterate over all views.
I got one more solution to hide the keyboard by:
InputMethodManager imm = (InputMethodManager) getSystemService(
Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
Here pass HIDE_IMPLICIT_ONLY at the position of showFlag and 0 at the position of hiddenFlag.
It will forcefully close the soft keyboard.
A more Kotlin & Material Design way using TextInputEditText (this approach is also compatible with EditTextView)...
1.Make the parent view(content view of your activity/fragment) clickable and focusable by adding the following attributes
android:focusable="true"
android:focusableInTouchMode="true"
android:clickable="true"
2.Create an extension for all View (inside a ViewExtension.kt file for example) :
fun View.hideKeyboard(){
val inputMethodManager = context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}
3.Create a BaseTextInputEditText that inherit of TextInputEditText. Implement the method onFocusChanged to hide keyboard when the view is not focused :
class BaseTextInputEditText(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs){
override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
super.onFocusChanged(focused, direction, previouslyFocusedRect)
if (!focused) this.hideKeyboard()
}
}
4.Just call your brand new custom view in your XML :
<android.support.design.widget.TextInputLayout
android:id="#+id/textInputLayout"
...>
<com.your_package.BaseTextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
... />
</android.support.design.widget.TextInputLayout>
That's all. No need to modify your controllers (fragment or activity) to handle this repetitive case.
Well I manage to somewhat solve the problem, I overrode the dispatchTouchEvent on my activity, there I am using the following to hide the keyboard.
/**
* Called to process touch screen events.
*/
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
touchDownTime = SystemClock.elapsedRealtime();
break;
case MotionEvent.ACTION_UP:
//to avoid drag events
if (SystemClock.elapsedRealtime() - touchDownTime <= 150){
EditText[] textFields = this.getFields();
if(textFields != null && textFields.length > 0){
boolean clickIsOutsideEditTexts = true;
for(EditText field : textFields){
if(isPointInsideView(ev.getRawX(), ev.getRawY(), field)){
clickIsOutsideEditTexts = false;
break;
}
}
if(clickIsOutsideEditTexts){
this.hideSoftKeyboard();
}
} else {
this.hideSoftKeyboard();
}
}
break;
}
return super.dispatchTouchEvent(ev);
}
EDIT: The getFields() method is just a method that returns an array with the textfields in the view. To avoid creating this array on every touch, I created an static array called sFields, which is returned at the getFields() method. This array is initialized on the onStart() methods such as:
sFields = new EditText[] {mUserField, mPasswordField};
It is not perfect, The drag event time is only based on heuristics so sometimes it doesnt hide when performing long clics, and I also finished by creating a method to get all the editTexts per view; else the keyboard would hide and show when clicking other EditText.
Still, cleaner and shorter solutions are welcome
Use OnFocusChangeListener.
For example:
editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
hideKeyboard();
}
}
});
Update: you also may override onTouchEvent() in your activity and check coordinates of the touch. If coordinates are outside of EditText, then hide the keyboard.
Override public boolean dispatchTouchEvent(MotionEvent event) in any Activity (or extend class of Activity)
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
View view = getCurrentFocus();
boolean ret = super.dispatchTouchEvent(event);
if (view instanceof EditText) {
View w = getCurrentFocus();
int scrcoords[] = new int[2];
w.getLocationOnScreen(scrcoords);
float x = event.getRawX() + w.getLeft() - scrcoords[0];
float y = event.getRawY() + w.getTop() - scrcoords[1];
if (event.getAction() == MotionEvent.ACTION_UP
&& (x < w.getLeft() || x >= w.getRight()
|| y < w.getTop() || y > w.getBottom()) ) {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getWindow().getCurrentFocus().getWindowToken(), 0);
}
}
return ret;
}
Kotlin version:
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
val ret = super.dispatchTouchEvent(ev)
ev?.let { event ->
if (event.action == MotionEvent.ACTION_UP) {
currentFocus?.let { view ->
if (view is EditText) {
val touchCoordinates = IntArray(2)
view.getLocationOnScreen(touchCoordinates)
val x: Float = event.rawX + view.getLeft() - touchCoordinates[0]
val y: Float = event.rawY + view.getTop() - touchCoordinates[1]
//If the touch position is outside the EditText then we hide the keyboard
if (x < view.getLeft() || x >= view.getRight() || y < view.getTop() || y > view.getBottom()) {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(view.windowToken, 0)
view.clearFocus()
}
}
}
}
}
return ret
}
And that's all you need to do
I implemented dispatchTouchEvent in Activity to do this:
private EditText mEditText;
private Rect mRect = new Rect();
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
int[] location = new int[2];
mEditText.getLocationOnScreen(location);
mRect.left = location[0];
mRect.top = location[1];
mRect.right = location[0] + mEditText.getWidth();
mRect.bottom = location[1] + mEditText.getHeight();
int x = (int) ev.getX();
int y = (int) ev.getY();
if (action == MotionEvent.ACTION_DOWN && !mRect.contains(x, y)) {
InputMethodManager input = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
input.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
}
return super.dispatchTouchEvent(ev);
}
and I tested it, works perfect!
I modified the solution of Andre Luis IM I achieved this one:
I created a utility method to hide the soft keyboard the same way Andre Luiz IM did:
public static void hideSoftKeyboard(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
But instead of register an OnTouchListener for every view, that give a poor performance, I registered the OnTouchListener for just the root view. Since the event bubbles until it's consumed (EditText is one of the views that consumes it by default), if it arrives to the root view, it's because it wasn't consumed, so I close the soft keyboard.
findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Utils.hideSoftKeyboard(activity);
return false;
}
});
I'm aware that this thread is quite old, the correct answer seems valid and there are a lot of working solutions out there, but I think the approach stated bellow might have an additional benefit regarding efficiency and elegance.
I need this behavior for all of my activities, so I created a class CustomActivity inheriting from the class Activity and "hooked" the dispatchTouchEvent function. There are mainly two conditions to take care of:
If focus is unchanged and someone is tapping outside of the current input field, then dismiss the IME
If focus has changed and the next focused element isn't an instance of any kind of an input field, then dismiss the IME
This is my result:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_UP) {
final View view = getCurrentFocus();
if(view != null) {
final boolean consumed = super.dispatchTouchEvent(ev);
final View viewTmp = getCurrentFocus();
final View viewNew = viewTmp != null ? viewTmp : view;
if(viewNew.equals(view)) {
final Rect rect = new Rect();
final int[] coordinates = new int[2];
view.getLocationOnScreen(coordinates);
rect.set(coordinates[0], coordinates[1], coordinates[0] + view.getWidth(), coordinates[1] + view.getHeight());
final int x = (int) ev.getX();
final int y = (int) ev.getY();
if(rect.contains(x, y)) {
return consumed;
}
}
else if(viewNew instanceof EditText || viewNew instanceof CustomEditText) {
return consumed;
}
final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(viewNew.getWindowToken(), 0);
viewNew.clearFocus();
return consumed;
}
}
return super.dispatchTouchEvent(ev);
}
Side note: Additionally I assign these attributes to the root view making it possible to clear focus on every input field and preventing input fields gaining focus on activity startup (making the content view the "focus catcher"):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final View view = findViewById(R.id.content);
view.setFocusable(true);
view.setFocusableInTouchMode(true);
}
Instead of iterating through all the views or overriding dispatchTouchEvent.
Why Not just override the onUserInteraction() of the Activity this will make sure keyboard dismisses whenever the user taps outside of EditText.
Will work even when EditText is inside the scrollView.
#Override
public void onUserInteraction() {
if (getCurrentFocus() != null) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
}
In kotlin, we can do the following. No need to iterate all the views. It will work for fragments also.
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
currentFocus?.let {
val imm: InputMethodManager = getSystemService(
Context.INPUT_METHOD_SERVICE
) as (InputMethodManager)
imm.hideSoftInputFromWindow(it.windowToken, 0)
}
return super.dispatchTouchEvent(ev)
}
I liked the approach of calling dispatchTouchEvent made by htafoya, but:
I didn't understand the timer part (don't know why measuring the downtime should be necessary?)
I don't like to register/unregister all EditTexts with every view-change (could be quite a lot of viewchanges and edittexts in complex hierarchies)
So, I made this somewhat easier solution:
#Override
public boolean dispatchTouchEvent(final MotionEvent ev) {
// all touch events close the keyboard before they are processed except EditText instances.
// if focus is an EditText we need to check, if the touchevent was inside the focus editTexts
final View currentFocus = getCurrentFocus();
if (!(currentFocus instanceof EditText) || !isTouchInsideView(ev, currentFocus)) {
((InputMethodManager) getApplicationContext().getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
return super.dispatchTouchEvent(ev);
}
/**
* determine if the given motionevent is inside the given view.
*
* #param ev
* the given view
* #param currentFocus
* the motion event.
* #return if the given motionevent is inside the given view
*/
private boolean isTouchInsideView(final MotionEvent ev, final View currentFocus) {
final int[] loc = new int[2];
currentFocus.getLocationOnScreen(loc);
return ev.getRawX() > loc[0] && ev.getRawY() > loc[1] && ev.getRawX() < (loc[0] + currentFocus.getWidth())
&& ev.getRawY() < (loc[1] + currentFocus.getHeight());
}
There is one disadvantage:
Switching from one EditText to another EditText makes the keyboard hide and reshow - in my case it's desired that way, because it shows that you switched between two input components.
Plea: I recognize I have no clout, but please take my answer seriously.
Problem: Dismiss soft keyboard when clicking away from keyboard or edit text with minimal code.
Solution: External library known as Butterknife.
One Line Solution:
#OnClick(R.id.activity_signup_layout) public void closeKeyboard() { ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); }
More Readable Solution:
#OnClick(R.id.activity_signup_layout)
public void closeKeyboard() {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
Explanation: Bind OnClick Listener to the activity's XML Layout parent ID, so that any click on the layout (not on the edit text or keyboard) will run that snippet of code which will hide the keyboard.
Example: If your layout file is R.layout.my_layout and your layout id is R.id.my_layout_id, then your Butterknife bind call should look like:
(#OnClick(R.id.my_layout_id)
public void yourMethod {
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
Butterknife Documentation Link: http://jakewharton.github.io/butterknife/
Plug: Butterknife will revolutionize your android development. Consider it.
Note: The same result can be achieved without the use of external library Butterknife. Just set an OnClickListener to the parent layout as described above.
its too simple, just make your recent layout clickable an focusable by this code:
android:id="#+id/loginParentLayout"
android:clickable="true"
android:focusableInTouchMode="true"
and then write a method and an OnClickListner for that layout , so that when the uppermost layout is touched any where it will call a method in which you will write code to dismiss keyboard. following is the code for both;
// you have to write this in OnCreate()
yourLayout.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
hideKeyboard(view);
}
});
method called from listner:-
public void hideKeyboard(View view) {
InputMethodManager imm =(InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Here's another variation on fje's answer that addresses the issues raised by sosite.
The idea here is to handle both the down and the up actions in the Activity's dispatchTouchEvent method. On the down action, we make note of the currently focused view (if any) and whether the touch was inside it, saving both those bits of info for later.
On the up action, we first dispatch, to allow another view to potentially take focus. If after that, the currently focused view is the originally focused view, and the down touch was inside that view, then we leave the keyboard open.
If the currently focused view is different than the originally focused view and it's an EditText, then we also leave the keyboard open.
Otherwise we close it.
So, to sum up, this works as follows:
when touching inside a currently focused EditText, the keyboard stays open
when moving from a focused EditText to another EditText, the keyboard stays open (doesn't close/reopen)
when touching anywhere outside a currently focused EditText that is not another EditText, the keyboard closes
when long-pressing in an EditText to bring up the contextual action bar (with the cut/copy/paste buttons), the keyboard stays open, even though the UP action took place outside the focused EditText (which moved down to make room for the CAB). Note, though, that when you tap on a button in the CAB, it will close the keyboard. That may or may not be desirable; if you want to cut/copy from one field and paste to another, it would be. If you want to paste back into the same EditText, it would not be.
when the focused EditText is at the bottom of the screen and you long-click on some text to select it, the EditText keeps focus and therefore the keyboard opens like you want, because we do the "touch is within view bounds" check on the down action, not the up action.
private View focusedViewOnActionDown;
private boolean touchWasInsideFocusedView;
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
focusedViewOnActionDown = getCurrentFocus();
if (focusedViewOnActionDown != null) {
final Rect rect = new Rect();
final int[] coordinates = new int[2];
focusedViewOnActionDown.getLocationOnScreen(coordinates);
rect.set(coordinates[0], coordinates[1],
coordinates[0] + focusedViewOnActionDown.getWidth(),
coordinates[1] + focusedViewOnActionDown.getHeight());
final int x = (int) ev.getX();
final int y = (int) ev.getY();
touchWasInsideFocusedView = rect.contains(x, y);
}
break;
case MotionEvent.ACTION_UP:
if (focusedViewOnActionDown != null) {
// dispatch to allow new view to (potentially) take focus
final boolean consumed = super.dispatchTouchEvent(ev);
final View currentFocus = getCurrentFocus();
// if the focus is still on the original view and the touch was inside that view,
// leave the keyboard open. Otherwise, if the focus is now on another view and that view
// is an EditText, also leave the keyboard open.
if (currentFocus.equals(focusedViewOnActionDown)) {
if (touchWasInsideFocusedView) {
return consumed;
}
} else if (currentFocus instanceof EditText) {
return consumed;
}
// the touch was outside the originally focused view and not inside another EditText,
// so close the keyboard
InputMethodManager inputMethodManager =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(
focusedViewOnActionDown.getWindowToken(), 0);
focusedViewOnActionDown.clearFocus();
return consumed;
}
break;
}
return super.dispatchTouchEvent(ev);
}
I find the accepted answer bit complex for this simple requirement. Here's what worked for me without any glitch.
findViewById(R.id.mainLayout).setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
return false;
}
});
This one is the easiest solution for me (and worked out by me).
This is the method to hide the keyboard.
public void hideKeyboard(View view){
if(!(view instanceof EditText)){
InputMethodManager inputMethodManager=(InputMethodManager)getSystemService(INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),0);
}
}
now set onclick attribute of the parent layout of the activity to above method hideKeyboard either from the Design view of your XML file or writing below code in Text view of your XML file.
android:onClick="hideKeyboard"
There is a simpler approach, based on iPhone same issue. Simply override the background's layout on touch event, where the edit text is contained. Just use this code in the activity's OnCreate (login_fondo is the root layout):
final LinearLayout llLogin = (LinearLayout)findViewById(R.id.login_fondo);
llLogin.setOnTouchListener(
new OnTouchListener()
{
#Override
public boolean onTouch(View view, MotionEvent ev) {
InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
android.content.Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mActivity.getCurrentFocus().getWindowToken(), 0);
return false;
}
});
Method for show / hide soft keyboard
InputMethodManager inputMethodManager = (InputMethodManager) currentActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
if (isShow) {
if (currentActivity.getCurrentFocus() == null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
} else {
inputMethodManager.showSoftInput(currentActivity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);
}
} else {
if (currentActivity.getCurrentFocus() == null) {
inputMethodManager.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);
} else {
inputMethodManager.hideSoftInputFromInputMethod(currentActivity.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
}
}
I hope they have been useful
Activity
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
ScreenUtils.hideKeyboard(this, findViewById(android.R.id.content).getWindowToken());
return super.dispatchTouchEvent(ev);
}
ScreenUtils
public static void hideKeyboard(Context context, IBinder windowToken) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(windowToken, InputMethodManager.HIDE_NOT_ALWAYS);
}
I got this working with a slight variant on Fernando Camarago's solution. In my onCreate method I attach a single onTouchListener to the root view but send the view rather than activity as an argument.
findViewById(android.R.id.content).setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
Utils.hideSoftKeyboard(v);
return false;
}
});
In a separate Utils class is...
public static void hideSoftKeyboard(View v) {
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
This may be old but I got this working by implenting a custom class
public class DismissKeyboardListener implements OnClickListener {
Activity mAct;
public DismissKeyboardListener(Activity act) {
this.mAct = act;
}
#Override
public void onClick(View v) {
if ( v instanceof ViewGroup ) {
hideSoftKeyboard( this.mAct );
}
}
}
public void hideSoftKeyboard(Activity activity) {
InputMethodManager imm = (InputMethodManager)
getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
}
the best practice here is to create a Helper class and every container Relative / Linear Layouts should implement this.
**** Take note only the main Container should implement this class (For optimization) ****
and implement it like this :
Parent.setOnClickListener( new DismissKeyboardListener(this) );
the keyword this is for Activity. so if you are on fragment you use like getActivity();
---thumbs up if it help you...
--- cheers Ralph ---
I have done this way:
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
View view = getCurrentFocus();
if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
int scrcoords[] = new int[2];
view.getLocationOnScreen(scrcoords);
float x = ev.getRawX() + view.getLeft() - scrcoords[0];
float y = ev.getRawY() + view.getTop() - scrcoords[1];
if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
hideKeyboard(this);
}
return super.dispatchTouchEvent(ev);
}
Hide keyboard code:
public static void hideKeyboard(Activity act) {
if(act!=null)
((InputMethodManager)act.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((act.getWindow().getDecorView().getApplicationWindowToken()), 0);
}
Done
To solve this problem what you have to do is first use setOnFocusChangeListener of that Edittext
edittext.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
Log.d("focus", "focus loosed");
// Do whatever you want here
} else {
Log.d("focus", "focused");
}
}
});
and then what you need to do is override dispatchTouchEvent in the activity which contains that Edittext see below code
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if ( v instanceof EditText) {
Rect outRect = new Rect();
v.getGlobalVisibleRect(outRect);
if (!outRect.contains((int)event.getRawX(), (int)event.getRawY())) {
Log.d("focus", "touchevent");
v.clearFocus();
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
}
return super.dispatchTouchEvent(event);
}
Now what will happen is when a user click outside then firstly this dispatchTouchEvent will get called which then will clear focus from the editext now your OnFocusChangeListener will get called that focus has been changed now here you can do anything which you wanted to do hope it works
I have refined the method, put the following code in some UI utility class(preferably, not necessarily) so that it can be accessed from all your Activity or Fragment classes to serve its purpose.
public static void serachAndHideSoftKeybordFromView(View view, final Activity act) {
if(!(view instanceof EditText)) {
view.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
hideSoftKeyboard(act);
return false;
}
});
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
View nextViewInHierarchy = ((ViewGroup) view).getChildAt(i);
serachAndHideSoftKeybordFromView(nextViewInHierarchy, act);
}
}
}
public static void hideSoftKeyboard (Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
}
Then say for example you need to call it from activity, call it as follows;
UIutils.serachAndHideSoftKeybordFromView(findViewById(android.R.id.content), YourActivityName.this);
Notice
findViewById(android.R.id.content)
This gives us the root view of the current group(you mustn't have set the id on root view).
Cheers :)
Try to put stateHidden on as your activity windowSoftInputMode value
http://developer.android.com/reference/android/R.attr.html#windowSoftInputMode
For example for your Activity:
this.getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
Just Add this code in the class
#Overide
public boolean dispatchTouchEvent(MotionEvent ev) {
View view = getCurrentFocus();
if (view != null && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_MOVE) && view instanceof EditText && !view.getClass().getName().startsWith("android.webkit.")) {
int scrcoords[] = new int[2];
view.getLocationOnScreen(scrcoords);
float x = ev.getRawX() + view.getLeft() - scrcoords[0];
float y = ev.getRawY() + view.getTop() - scrcoords[1];
if (x < view.getLeft() || x > view.getRight() || y < view.getTop() || y > view.getBottom())
((InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow((this.getWindow().getDecorView().getApplicationWindowToken()), 0);
}
return super.dispatchTouchEvent(ev);
}

Categories

Resources