35

I have been designing an application which holds an expandable list. At the end of every list, an empty EditText is ready to receive comments. I have the following problem; when I touch the EditText, the screen resizes slightly (not a problem as the resizing does not always happen, depending on my layout and the position of the EditText int he list) and the soft-keyboard appears. However, during these events, the EditText loses focus and does not regain it. This is quite inconvenient as without focus, despite the keyboard being available, the input from the keyboard does not reach the EditText. Once I touch it again, the EditText behaves as expected.

It gets even stranger. With the keyboard still displayed, I can touch a different EditText and the same happens; it loses focus. However, once I passed the initial loss of focus, I can move freely between previously touched EditTexts without any focus related issues. Only when I hide the soft-keyboard will the "state" disappear and I would need to tap EditTexts twice before being able to edit them.

Here are excerpts from my code. First, the empty_comment.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

<EditText
    android:id="@+id/blank_box"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:inputType="textCapSentences"
    android:imeOptions="actionDone"
    android:hint="@string/hint"
    android:layout_marginLeft="30dip" >
</EditText>
</LinearLayout>

Here an excerpt from the fragment where I use the layout in question:

convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.comment_empty, null);
final EditText editText = (EditText) convertView.findViewById(R.id.blank_box);
editText.setOnEditorActionListener(new OnEditorActionListener()
{   
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
    {
        if(actionId == EditorInfo.IME_ACTION_DONE)
        {
            // Unrelated code
        }
        return true;
    }
});

I am not specifying anything input specific in my manifest file but I could provide it if deemed helpful.

Update:

Adding a line to the Activity to adjust the pan android:windowSoftInputMode="adjustPan" does partially solve the problem. It replaces the current unexpected behaviour with the problem that EditText views might get hidden by the soft-keyboard.

As of now, no ideal solution has been found but approached. Have a look at the comments in the accepted answer, they might prove useful to you!

Graham Borland
  • 60,055
  • 21
  • 138
  • 179
Eric Tobias
  • 3,225
  • 4
  • 32
  • 50
  • Possible duplicate of [Focusable EditText inside ListView](http://stackoverflow.com/questions/2679948/focusable-edittext-inside-listview) – blahdiblah Oct 31 '13 at 23:50
  • I would like to object to that assessment. Reading the other question reveals that the question is related to `ListView` and the ability to shift focus to a specific child of the `ListView`. Consider the simplified questions: _All I want to do is be able to use the jogball/arrows, to navigate the selector to individual items like normal, but when I get to a particular row -- even if I have to explicitly identify the row -- that has a focusable child, I want that child to take focus instead of indicating the position with the selector._ I conclude that there is a notable difference! – Eric Tobias Dec 03 '13 at 09:30
  • Apparently others felt similarly because the question wasn't closed. Community moderation at its finest! – blahdiblah Dec 03 '13 at 16:38

6 Answers6

15

A solution is to post a delayed runnable that checks to see if the EditText view is still focused, and if it is not, focus it.

editText.setOnFocusChangeListener(new OnFocusChangeListener() {
    @Override
    public void onFocusChange(final View v, boolean hasFocus) {
        if (hasFocus) {
            // Request focus in a short time because the
            // keyboard may steal it away.
            v.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (!v.hasFocus()) {
                        v.requestFocus();
                    }
                }
            }, 200);
        }
    }
});
Tonic Artos
  • 1,568
  • 1
  • 10
  • 7
  • 2
    While this could be a work-around, it is also very dangerous. Who is to say that the user did not purposely change focus? I know, 200ms is not a lot but still, it is a bit of a hack. Not to mention the extra system resource needed to deploy this solution. Furthermore, the listener will, if I read it correctly, focus the `EditText` every time focus is lost. This is definitely not wanted behaviour. At some point, notably then the user is done, the `EditText` needs to loose focus! – Eric Tobias Aug 07 '13 at 06:39
  • 1
    No, it only posts the runnable if the focus change was to the focused state. It isn't a perfect solution for all cases, but for my app it is more desirable than `android:windowSoftInputMode="adjustPan"` – Tonic Artos Aug 08 '13 at 04:35
  • It is possible the runnable may be posted multiple times where focus is stolen away multiple times, but you can easily set a flag to ensure it is posted once, if that is what you want. – Tonic Artos Aug 08 '13 at 04:43
  • I am not sure I understand. When you are done typing in the `EditText` and commit the content, you will loose focus. In that case, your `Listener` should trigger, should it not? – Eric Tobias Aug 08 '13 at 06:21
  • In your scenario the user deliberately moves focus away from the `EditText` widget. In this case the `OnFocusChangeListener` run method will be called with the parameter `hasFocus := false`. This means the condition `if (hasFocus)` will evaluate to false, leading to the `Runnable` to request focus not being posted. – Tonic Artos Aug 08 '13 at 23:04
  • You misread the scenario. I quote from the question: `"[...]However, during these events, the EditText looses focus and does not regain it. This is quite inconvenient as without focus, despite the keyboard being available, the input from the keyboard does not reach the EditText. Once I touch it again, the EditText behaves as expected.[...]"`. The reason I cannot use `hasFocus()` is because I loose focus. This is the very reason I was asking the question for as focus does not behave as expected. – Eric Tobias Aug 09 '13 at 06:48
  • In my previous comment I was referring to the scenario you stated in your comment and not the original question. I had the exact same problem in my app as you outlined in your question and I solved it using the method I described in my answer. As for how it works I'll explain again. This is what causes the problem: When the user touches an EditText widget in a list, the soft keyboard forces the screen to resize, this results in the EditText losing its focus. – Tonic Artos Aug 10 '13 at 07:23
  • My solution is to post a Runnable when the EditText is first focused. The Runnable is delayed long enough that it is run after the list has finished its layout after the resize. At this time the EditText has lost its focus. The Runnable that was posted earlier is then executed, it checks to see if the EditText has indeed lost focus, if it has been lost, it requests focus for the EditText again. In this way it appears to the user that the EditText never lost focus. – Tonic Artos Aug 10 '13 at 07:23
  • I hadn't seen it that way. It is indeed a neat way to get around the problem. However, I see a few issues. The OS is not guaranteed to have finished its task by the 200ms mark. Hard-coding a time frame for guessing when an asynchronous task may complete is usually a bad practice. Also, this Runnable is run every time the `EditText` gains focus. While this is a minor concern, it may impact the performance a bit. However, your approach is interesting. Let me up-vote you for ingenuity and tenacity! ;) – Eric Tobias Aug 12 '13 at 07:02
  • 1
    This solution feels hacky, but after hours of trying myself, I still can't find a better way. I only wish there was something to hook into instead of using a fixed time delay. – M_M Sep 19 '18 at 11:58
  • This worked! Well done! – Edmund Rojas Sep 06 '22 at 03:50
2

I had the same problem...I solved it by extending EditText.

I created an onClickListener and in this method I've got the following code:

public OnClickListener clickListener = new OnClickListener() {
    @Override
    public void onClick(View v) {
        EditTextInput.this.setFocusable(true);
        EditTextInput.this.setFocusableInTouchMode(true);
        EditTextInput.this.requestFocus();
    }
};       

I then placed the following code in the onKeyPreIme callback. You'll have to extend EditText to get access to it.

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK & event.getAction() == KeyEvent.ACTION_UP) {
        EditTextInput.clearFocus();
        EditTextInput.this.setFocusable(false);
        EditTextInput.this.setFocusableInTouchMode(false);            
        return false;
    }
    return super.dispatchKeyEvent(event);
}

I find I have to toggle the focusable flag in order to bring the keyboard up on the very first click. Hope this helps...works for me.

Houston
  • 426
  • 4
  • 17
  • Interesting approach. I have since moved on in the development and don't have the resources to try it anymore. However, if I ever get back to it I will try it. Thank you! – Eric Tobias Mar 07 '13 at 09:35
  • @Houston This didn't work for me. I'm not sure if there are layout related parameters that I need to mess with. But, based on your final comment I'm not sure you are proposing to solve to posted issue. The issue is that touching the EditText causes the soft keyboard to appear, which in turn steals focus from the EditText. – stephen Sep 04 '13 at 17:57
1

You could try something like :

editText.requestFocus();

You could also set this :

android:windowSoftInputMode="stateHidden"

in your manifest file

lokoko
  • 5,785
  • 5
  • 35
  • 68
  • Unfortunately I cannot request focus. There is no place that would make sense in the code to request focus that would not hamper the expected behaviour in one way or another. As for your second suggestion, that is the default behaviour. The soft-keyboard does not show until I touch the `EditText`. – Eric Tobias Feb 06 '13 at 12:27
0

I had this problem with android.support.design.widget.TextInputEditText and it was solved from changing the flag order in manifest from:

android:windowSoftInputMode="stateHidden|adjustPan"

to

android:windowSoftInputMode="adjustPan|stateHidden"
Ultimo_m
  • 4,724
  • 4
  • 38
  • 60
0

In addition to android:windowSoftInputMode="adjustPan", try below:

  1. Set android:descendantFocusability="afterDescendants" to your ListView.
  2. Set android:focusable="true" and android:focusableInTouchMode="true" to your EditText.
Sam Chen
  • 7,597
  • 2
  • 40
  • 73
-1

you can try this below code to android manifest.xml

add the code to android manifest android:windowSoftInputMode="adjustPan"

And also add to xml layout

<EditText  
      android:background="#FFFFFF"
      android:capitalize="sentences"
      android:inputType="textVisiblePassword"
      android:layout_height="35px"
      >
      <requestFocus /> 

saravanan
  • 388
  • 2
  • 18
  • I am not sure what to make of your XML excerpt. It seems unrelated to my case. As stated above, requesting focus is not an option and the bits to put into the manifest solve the problem only partially. – Eric Tobias Feb 06 '13 at 12:33