I\'ve got a few EditText fields in a ListView. When I tap on one of the EditText fields, the keyboard slides into view (as it should), but the EditText field I tapped loses
Add android:windowSoftInputMode="adjustResize" in the activity holding the listview or EditText. This will solve your problem.
<activity android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
</activity>
Here is how I did it. The onFocusChangeListener()
is called several times when you touch a EditText
to type text into it. The sequence is:
Target finally gains focus
//////////////////////////////////////////////////////////////////
private final int minDelta = 300; // threshold in ms
private long focusTime = 0; // time of last touch
private View focusTarget = null;
View.OnFocusChangeListener onFocusChangeListener = new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View view, boolean hasFocus) {
long t = System.currentTimeMillis();
long delta = t - focusTime;
if (hasFocus) { // gained focus
if (delta > minDelta) {
focusTime = t;
focusTarget = view;
}
}
else { // lost focus
if (delta <= minDelta && view == focusTarget) {
focusTarget.post(new Runnable() { // reset focus to target
public void run() {
focusTarget.requestFocus();
}
});
}
}
}
};
The code above works well for the keyboard pop-ups. However, it does not detect the speech-to-text pop-up.
In AndroidManifest.xml use adjustNothing in the activity that contain the views
<activity
android:name=".ActivityName"
android:windowSoftInputMode="adjustNothing">
I believe that loss of focus on show or hide of the keyboard is not expected behavior and either should have been (or should be) a reported Android issue.
But too late now, 10 years after OP encountered it!
In my case, the disadvantage of switching from SOFT_INPUT_ADJUST_RESIZE
to SOFT_INPUT_ADJUST_PAN
outweighed the advantage of not losing focus.
Why? ADJUST_RESIZE
is the officially preferred approach because ADJUST_PAN
blocks part of your view and may prevent scrolling.
But thanks to an earlier answer to this question I became intensely suspicious of ListView
.
To prove my suspicions I spent a day converting a complex ListView
-based editor to RecyclerView
.
I can confirm that soft keyboard state changes no longer affect EditText
focus even though I am using ADJUST_RESIZE
.
Seemingly painful I know - but perhaps the final result is nicer than sub-classing or tricky workarounds?
In my case, this is happening because when the ListView resizes, it re-creates all of the list items (i.e. it calls getView() again for each visible list item).
Because the EditText is within the layout that I'm returning from getView(), this means that it's a different instance of EditText than the one which had the focus previously. A secondary consequence is that when the soft-keyboard appears or disappears I found that I was losing the contents of the EditText.
Because I wanted my view to remain fully accessible (i.e. I want it to be resized instead of hidden behind the keyboard window with some parts not accessible), I couldn't use Frank's answer, which otherwise seems like the best approach.
I solved this by using an OnFocusChangeListener on the EditText to record the timestamp when the focus was lost, and then in getView() when recreating the list item, if the current time is within some threshold from when the focus was lost, call requestFocus() to give it back to the EditText in question.
You can also grab the text from the previous instance of the EditText at that point and transfer it to the new instance.
private class MyAdapter<Type> extends ArrayAdapter<String>
implements OnFocusChangeListener
{
private EditText mText;
private long mTextLostFocusTimestamp;
private LayoutInflater mLayoutInflater;
public MyAdapter(Context context, int resource, int textResourceId, ArrayList<String> data, LayoutInflater li) {
super(context, resource, textResourceId, data);
mLayoutInflater = li;
mTextLostFocusTimestamp = -1;
}
private void reclaimFocus(View v, long timestamp) {
if (timestamp == -1)
return;
if ((System.currentTimeMillis() - timestamp) < 250)
v.requestFocus();
}
@Override public View getView (int position, View convertView, ViewGroup parent)
{
View v = mLayoutInflater.inflate(R.layout.mylayout, parent, false);
EditText newText = (EditText) v.findViewById(R.id.email);
if (mText != null)
newText.setText(mText.getText());
mText = newText;
mText.setOnFocusChangeListener(this);
reclaimFocus(mText, mTextLostFocusTimestamp);
return v;
}
@Override public void onFocusChange(View v, boolean hasFocus) {
if ((v == mText) && !hasFocus)
mTextLostFocusTimestamp = System.currentTimeMillis();
}
}
This guy had the same problem and more besides. He solved it by using a ScrollView and a LinearLayout instead of a ListView.