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 focus. I've tried using various InputMethodManager methods to make the keyboard start out in view (in order to get around the problem rather than truly solve it), but that didn't work - the keyboard was not in view when the Activity appeared.
The EditText's type is number, and when the keyboard is sliding in, it is a number keyboard, but when it finishes sliding and the EditText loses focus, it changes to the alphabetical keyboard (which reinforces the idea that the EditText no longer has focus).
My questions are these:
1) How can I make the selection of my EditText field and the subsequent sliding in of the soft keyboard not make my EditText lose focus?
... failing that...
2) How can I make the keyboard start out in view so it never has to slide in (thus avoiding the behavior I find so objectionable)?
My manifest does include android:windowSoftInputMode="stateAlwaysVisible", but the keyboard does not appear until I tap on an EditText. This ignoring of the 'stateAlwaysVisible' attribute seems to only occur in the emulator - on my provisioned device, it is honored so question number 2 above does work on the device... but not in the emulator.
Thanks for any help you can provide!
You need to change in your AndroidManifest.xml
Add android:windowSoftInputMode="adjustPan" in the activity holding the listview.
This will solve your problem.
<activity android:name=".MyEditTextInListView"
android:label="#string/app_name"
android:windowSoftInputMode="adjustPan">
Regards
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:
If focus was on a different view, then that view loses focus
The target gains focus
Soft keyboard pops up.
This causes the target to lose focus
The code detects this situation and calls target.requestFocus()
The leftmost, topmost view gains focus, due to Android nonsense
The leftmost view loses focus, due to requestFocus being called
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 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();
}
}
In AndroidManifest.xml use adjustNothing in the activity that contain the views
<activity
android:name=".ActivityName"
android:windowSoftInputMode="adjustNothing">
You should test this code on a device with hardware keyboard always visible. The behavior may also happen here.
To avoid this you can have the keyboard always visible.. but that is not very easy as you can see by this thread:
https://groups.google.com/forum/#!topic/android-developers/FyENeEdmYC0
Theoretically you may have to create your own Android keyboard (although using as base the stock Android keyboard) as described here: Android: How to make the keypad always visible?
If the editText inside the listView just make sure that you inflate the View in the getView method with this way.
if (convertView == null)
convertView = LayoutInflater.from(context).inflate(R.layout.yourItemListLayout,
parent, false);
Edit: this work for some mobiles not all I use the answer from Mr.Frank above.
This guy had the same problem and more besides. He solved it by using a ScrollView and a LinearLayout instead of a ListView.
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>
For those who come here with Xamarin or Xamarin.Forms:
I had the same issue as well but only with Android 5.x - all newer Versions including 8.1 worked well.
Obviously sheltond was right by saying:
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).
My listview was resizing as well and no, Franks solution to set windowSoftInputMode="adjustPan" was no option for me because that means that the keyboard moves the listview partly off the screen.
All I had to do after hours of focus-debugging was setting the cell caching strategy of the Xamarin Forms ListView:
From
CachingStrategy="RecycleElement"
To
CachingStrategy="RetainElement"
This will stop the cells from being recreated. However, this might result in bad performance and high memory consumption for huge lists. Be aware.
In my case, I had called
root_scrollview.fullScroll(View.FOCUS_DOWN)
on my root ScrollView when Keyboard appears.
I replaced it with
login_scrollview.post(new Runnable() {
#Override
public void run() {
root_scrollview.scrollTo(0,root_container.bottom)
}
});
where root_container is the immediate child of root_scrollview. This solved the problem for me.
Note: Directly calling
root_scrollview.scrollTo(0,root_container.bottom) was not working.
Convert to RecyclerView
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?
Related
There is the proper method which is to draw the line myself via canvas. I want to avoid the complexity and I am asking here to double check if there is a simpler method I can use.
I am currently using a temporary method, which is to use SpannableString to highlight the nearest text 'character' that the cursor is on. But as you know, this "selects" the current character. I prefer the cursor to be between two characters instead of on top of one character.
I also don't want keyboard focus because I already laid out some nice buttons for the user to interact with. I don't want the app to use the Android keyboard and input methods. Only my buttons should be enough.
I tried accessing TextView methods:
Remove keyboard focus and input via the manifest
setFocusable(false)
setFocusableInTouchMode(false)
setCursorVisible(true) or cursorVisible="true"
This didn't work for me. I know that if I make the TextView focusable, then the cursor will be visible. But if it's not focusable, then the cursor does not show, even if it it is: cursorVisible="true".
What should I do?
Solution:
Disable input method of EditText but keep cursor blinking
Maybe try leaving out the xml attribute android:editable entirely
and then try the following in combination to
keep the cursor blinking and prevent touch events from popping up
a native IME(keyboard)..
/*customized edittext class
* for being typed in by private-to-your-app custom keyboard.
* borrowed from poster at http://stackoverflow.com/questions/4131448/android-how-to-turn-off-ime-for-an-edittext
*/
public class EditTextEx extends EditText {
public EditTextEx(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onCheckIsTextEditor() {
return false; //for some reason False leads to cursor never blinking or being visible even if setCursorVisible(true) was
called in code.
}
}
Step 2 change the above method to say return true;
Step 3 Add another method to above class.
#Override public boolean isTextSelectable(){ return true; }
Step 4 In the other location where the instance of this class has been
instantiated and called viewB I added a new touch event handler
viewB.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
viewB.setCursorVisible(true);
return false;
} });
Step 5 Check to make sure XML and or EditText instantiation code
declares IME/keyboard type to be 'none'. I didnt confirm relevance,
but Im also using the focusable attributes below.
<questionably.maybe.too.longofa.packagename.EditTextEx
android:id="#+id/editText1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:focusable="true"
android:focusableInTouchMode="true"
android:inputType="none">
Sorry for so many xml attributes. My code uses them all, testing in
4.2.1, and has results. Hope this helps.
Bug summary
After EditText is being recycled in RecyclerView, its long press behavior which used to select all text, and show context menu "Cut/Copy/Paste", no longer work as expected.
This problem occurs from Android 15 till Android 28.
I tried both EditText and android.support.v7.widget.AppCompatEditText. Both yields same problem.
I can confirm this problem occurs after View is being recycled. If I apply setIsRecyclable(false); in ViewHolder, the problem will not occur.
Steps to reproduce
Long press on 1st EditText. We can confirm all text in EditText will be selected. Context menu will be shown.
Scroll RecyclerView till the end of list.
Scroll RecyclerView till the start of list.
Long press on 1st EditText. All text in EditText will NOT be selected. Context menu will NOT be shown.
Expected behavior
After the view has been recycled, we expect step 4 will still behave exactly same as step 1.
Source code
https://github.com/yccheok/edittext_bug_in_android9
Issue tracker
https://issuetracker.google.com/issues/125425940
I was wondering, has anyone encounter same problem as I do? Do you have any good workaround on this? Note, I need my RecyclerView item being recycle-able. Hence, using setIsRecyclable(false); is not an option for me.
In short, the problem happen because EditText doesn't perform Editor#prepareCursorControllers during attachToWindow.
Since Editor#prepareCursorControllers is not a public accessible function, we can invoke it indirectly using setCursorVisible.
edtImgDesc.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
#Override
public void onViewAttachedToWindow(View v) {
edtImgDesc.setCursorVisible(false);
edtImgDesc.setCursorVisible(true);
}
#Override
public void onViewDetachedFromWindow(View v) {
}
});
Reference source: https://www.jianshu.com/p/e334134a4ef7 (The blog is in Chinese)
I'm starting with one programatically created AutoCompleteTextView view on my layout. After user typed valid data into the field, I'm creating a new AutoCompleteTextView object right under the first one by the same way, with the same parameters. I'm storing all of the field references in an ArrayList to keep up with the last one, this way I'm making sure that there will be new fields only under the one at the bottom (basically the last element in the reference list) - so it goes on at every field.
I would like to add this feature: when a new field is created, I change the last field's IME options to EditorInfo.IME_ACTION_NEXT and nextFocusForward attribute to the freshly created field - also programmatically. What I want to achieve: right when user presses Enter on the keyboard, last field's focus jumps to the new field. I'm using this code to set new IME options:
ArrayList<AutoCompleteTextView> fields = new ArrayList<>();
//Creating freshlyCreatedField, IT'S STILL NOT IN THE fields LIST!
AutoCompleteTextView currentlySelectedField = fields.get(fields.size() - 1); // last element
currentlySelectedField.setImeOptions(EditorInfo.IME_ACTION_NEXT);
currentlySelectedField.setNextFocusForwardId(freshlyCreatedField.getId());
currentlySelectedField.setSingleLine(true); //ACTV needs it to get IME to work
//Adding freshlyCreatedField to the fields list,
//so next time it will be the currentlySelectedField
But after running this code nothing happens to the currently selected AutoCompleteTextView object. Clearing and requesting focus did not work, and making it work by the reversed way (creating +1 "invisible" (technically GONE) field everytime) would be much more painful.
One more thing: after selecting any other field and re-selecting the one with configuration changes the Enter button works as it should first time! If I could do the same programmatically, it would solve my problem... so, any ideas how to do it? (Of course, I welcome better solutions also... ;) )
After some days, I've started to play with this problem again. Yes, it is a solution, but no, it's not so pretty as I would it to be like...
Anyhow, I created this code:
ArrayList<AutoCompleteTextView> fields = new ArrayList<>();
//Creating freshlyCreatedField, IT'S STILL NOT IN THE fields LIST!
//Last element
AutoCompleteTextView currentlySelectedField = fields.get(fields.size() - 1);
currentlySelectedField.setOnEditorActionListener(
new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
boolean handled = false;
if (actionId == EditorInfo.IME_ACTION_NEXT
|| actionId == EditorInfo.IME_ACTION_DONE
|| event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
freshlyCreatedField.requestFocus();
handled = true;
}
return handled;
}
});
//Adding freshlyCreatedField to the fields list,
//so next time it will be the currentlySelectedField
I put an EditorActionListener to the field actually under edit where the keyboard listens for one of the IME_ACTION_NEXT (if it's already set) and IME_ACTION_DONE action IDs (it's the default), or for the Enter key (for the case if someone would like to use physical keyboard). Then it jumps to the field below.
It works great, there aren't terrible problems with this approach. However I'm a little bit sad, because all of these possibilities stay invisible. The Done action button switches to Next only by clicking to somewhere else as I wrote it down in my question. So I think my "solution" can work, but it's not the best answer, so feel free to write your thoughts down if you have another idea... :)
I am interested in how to make a custom keyboard on android and i look this page
http://www.fampennings.nl/maarten/android/09keyboard/index.htm
i do not understand one methods in this page.i try but every time i found different meanings.
if anybody know this page help me please what does focus_listener.writer wrote this coupled edittext. i know that we create new Edittext Onkey methods .and i understood that when we click edittext , it makes copy and when its make copy focus change ? after when we click edittext again while keyborad is visible , because of copy visible edittext there are no focus change and our keyboard is get unvisible. i understand that but completely i belive i make mistake.help me please
// Find the EditText
EditText edittext= (EditText)findViewById(...);
// Make the custom keyboard appear
edittext.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override public void onFocusChange(View v, boolean hasFocus) {
if( hasFocus ) showCustomKeyboard(v); else hideCustomKeyboard();
When OnFocusChangeListener is called?
Interface definition for a callback to be invoked when the focus state of a view changed.
What is onFocusChange?
abstract void onFocusChange(View v, boolean hasFocus)
Called when the focus state of a view has changed.
It's very simple, when you as user click on a EditText, it will get the "focus" and Android will show the system keyboard to let user write something inside it. Since you want to show your custom keyboard instead of the default everytime a EditText (or everything you want) gets the focus you call the method showCustomKeyboard which is:
public void showCustomKeyboard( View v ) {
mKeyboardView.setVisibility(View.VISIBLE);
mKeyboardView.setEnabled(true);
if( v!=null ) ((InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
}
mKeyboardView is our keyboard which should be showed instead of the original, while this line:
if( v!=null ) ((InputMethodManager)getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(v.getWindowToken(), 0);
is used to hide the default the keyboard (hideSoftInputFromWindow)
If you understand what i said above, you can understand what else hideCustomKeyboard(); means, it works like normal Android, when the user leave the EditText we don't need anymore to show the keyboard but since this time it's your keyboard you should care about show/hide it
"it makes copy and when its make copy focus change ?"
I don't understand what you mean, it don't copy anything.
"after when we click edittext again while keyborad is visible , because of copy visible edittext there are no focus change and our keyboard is get unvisible"
No, if you click again the same EditText it will still have the focus, the keyboard will be hidden when you change the focus
I'm trying to inspect a code for a very big Android (Amazon Fire TV) activity but i keep loosing the focus in the running app and i don't know what element is being focused.
I'm looking for a way (Wether it's an App, a developer setting - Show Layout Limits gets near - or something i can code inside the activity) to see what view is being focused, without having to change the layout (Selectors) of every single view.
What do you suggest?
Activity has a method called getCurrentFocus().
Maybe you could call hasFocus() on all the Views if the above doesn't work. I imagine the method would look something like this:
public View getFocusedView(View layout)
{
View focusedView = null;
// Note: I'm not sure if FOCUS_DOWN is the right one to use here
// so you may want to see the other constants offered
ArrayList<View> views = layout.getFocusables(View.FOCUS_DOWN)
for(View v: views)
{
if(v.hasFocus())
{
focusedView = v;
}
}
return focusedView;
}