Assuming, that a user has written some text into an EditText and thereafter touched somewhere else on the screen, which caused the cursor position to change: How can one determine the new cursor position?
The simple version:
myEditText.getSelectionStart();
If you want to react on an event you may try
myEditText.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
// view is myEditText here
}
});
event allows to distinguish between presses and releases.
EditText also has a setOnClickListener() that might be worth to look at.
EDIT:
I forgot to mention onSelectionChanged(int selStart, int selEnd) where selEnd equals selStart if the position changed.
Best and safe way is using TextWatcher
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int cursorIndex = start + 1;
}
Related
I am building a PIN entry screen that has an invisible EditText to collect the PIN and four ImageViews that I populate as the user enters the PIN. It all works fine when the EditText is visible but when I make it invisible the addTextChangedListener does not fire?
XML
<EditText
android:id="#+id/pinEntryEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
android:visibility="invisible"
android:maxLength="4"
android:inputType="numberPassword"/>
Code
mPinEntryEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
switch (s.length()) {
case 1:
setPinFieldColor(mPinOneImageView, R.color.white);
break;
case 2:
setPinFieldColor(mPinTwoImageView, R.color.white);
break;
case 3:
setPinFieldColor(mPinThreeImageView, R.color.white);
break;
case 4:
setPinFieldColor(mPinFourImageView, R.color.white);
break;
}
}
});
I'm assuming you don't want to have the EditText be visible since you want the input to go straight to the ImageViews.
Can I maybe recommend an external library like https://github.com/alphamu/PinEntryEditText?
--
If you want to make a homegrown solution, maybe you can just show the keyboard and listen for keyboard input with onKeyDown?
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
return super.onKeyDown(keyCode, event);
//Logic...
}
This seems to be intentional behavior.
The TextView source (EditText extends TextView) overrides onVisibilityChanged():
#Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
if (mEditor != null && visibility != VISIBLE) {
mEditor.hideCursorAndSpanControllers();
stopTextActionMode();
}
}
As you can see, if the visibility of the View isn't VISIBLE, stopTextActionMode() will be called, which basically stops all updates to the View. This is probably to save on resources, since, usually, when a View is invisible, you don't need to be listening for changes in its state.
Unfortunately, it doesn't seem like you can override this behavior, although I don't really see why you need to receive updates from an invisible text input.
This is what happens:
Activity A and B have an EditText and they both have IME_SEARCH. The input is done only via soft keyboard on a SAMSUNG tablet.
On Activity A I can use the EditText without problems. The thing is that on Activity B I can't erase text with backspace after I hit 'space' or whenever I use a word from the suggestions. It behaves like there wasn't text there anymore. If I type new characters, I can erase them up to the space.
Important points:
The View hierarchy that contains the EditTexts are identical
The code that configures the IME_SEARCH processing (via setOnEditorActionListener) is identical
The TextWatcher of both are also identical
In the Manifest, both activities are configures with
android:configChanges="keyboardHidden|keyboard|orientation"
android:windowSoftInputMode="stateAlwaysHidden|adjustUnspecified"
I set a breakpoint on the method beforeTextChanged of both TextWatcher. I inserted a 'space' and hit 'backspace'. On the Edittext of activity A, the method is triggered but on activity B's it is not triggered. I can't see the reason for this to happen since the properties of both Edittext are configured identically.
I also tried removing the IME option but the behavior kept the same.
Does anyone know what could be happening?
EDIT 1:
searchTxt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (s.length() == 0) {
btnClear.setVisibility(View.GONE);
} else{
btnClear.setVisibility(View.VISIBLE);
}
}
});
searchTxt.setOnEditorActionListener(new TextView.OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
buildGrid();
return true;
}
return false;
}
});
EDIT 2:
The layout hierarchy is as following.
<LinearLayout
... >
<include layout="#layout/title_bar" />
<RelativeLayout
...>
<EditText
...>
The problem was that, for some reason, the Activity B was overriding dispatchKeyEvent() and always returning true. Removing it solved the problem.
I have similar problems that you are facing and I somehow managed to stumble on the solution. Apparently, I had setOnKeyListener to 'return true'. After I changed it to 'return false', the phone keyboard works perfect with backspace functioning properly once again on edittext. Hope this helps:
.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
...
return false;
}
});
I used EditText instead of SearchView. But here the problem is different I used to search in ListView and data in the ListView is coming when I hit the search api..let me explain..
Actually I have an api the when I put char in EditText , I get that char from EditText and hit the api in postman by passing the parameters in body, two params I have passed("key"=the char i get from EditText, user_id=null).
And I want to search the data like when I press "m" then "m" will post as value of "key" and shows the data , at the same time if i press "mo" then the api again hits at the same time and get me the results in ListView
Please help me...
mSearchFriends.setOnEditorActionListener(new TextView.OnEditorActionListener()
{
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
{
String input;
if(actionId == EditorInfo.IME_ACTION_DONE)
{
input = String.valueOf(mSearchFriends.getText());
new PostSearchApi(Add.this) {
#Override
public void fetchData(String output) {
Log.d("response>>>>>",output);
//input = String.valueOf(mSearchFriends.getText());
rowItemListForSearchList = new ArrayList<>();
mSearchListAdapter = new SearchListAdapter(getApplicationContext(),rowItemListForSearchList);
getjsonOfContact(output);
lvContacts.setAdapter(mSearchListAdapter);
mSearchListAdapter.notifyDataSetChanged();
mTextView_No_Result_Found.setVisibility(View.GONE);
}
}.execute(input,"");
It shows me result but after pressing the the arrow button on phone keyboard. What I want is I want to search random data when I type a character. I also tried the code in OnTextChanged method but when I type "a" then api hits and takes time when it completed then only i can type other character..
But the necessity is the functionality like finding the friends on facebook
Since no code provided by you, as far as I understand your ques, you can get realtime text changing events from EditText like this:
yourEditText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
//on Text change event. s is the text that is entered
// put your code here. Make a hit to your api and pass `s` as your parameter.
}
#Override
public void afterTextChanged(Editable s) {
}
});
I know this/similar question has been asked before but the solution given is not working for me so I'm asking again.
I tried the solution given in that answer but still my OnKeyListener is never being invoked on some devices, especially the ones with stock OS. I need to detect pressing of del key of soft keyboard when when there is no character in editText. Here is my code;
EditText et = (EditText) findViewById(R.id.et);
et.setOnKeyListener(new EditText.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.d("OnKeyListener", keyCode + " character(code) to send");
return false;
}
});
Finally solved myself by implementing this feature through TextWatcher. The major hurdle was that, I needed to detect backspace press even when there is no character in EditText or at least the end user perceives that there is no character there. The fist thing couldn't be achieved however I did the later one. Following is the detailed solution.
First of all, I always retained a space ' ' character in my editText.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) {
if(cs.toString().length() == 0)
editText.setText(" ");
}
#Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { }
#Override
public void afterTextChanged(Editable arg0) { }
});
Then I customized EditText to notify me for every cursor position change. This purpose is achieved by overriding onSelectionChanged method of EditText. My customized EditText looks like this.
public class SelectionEnabledEditText extends EditText {
public SelectionEnabledEditText(Context context) {
super(context);
}
public SelectionEnabledEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectionEnabledEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
super.onSelectionChanged(selStart, selEnd);
if(onSelectionChangeListener != null)
onSelectionChangeListener.onSelectionChanged(selStart, selEnd);
}
public static interface OnSelectionChangeListener{
public void onSelectionChanged(int selStart, int selEnd);
}
private OnSelectionChangeListener onSelectionChangeListener;
public void setOnSelectionChangeListener(OnSelectionChangeListener onSelectionChangeListener) {
this.onSelectionChangeListener = onSelectionChangeListener;
}
}
Finally, in my activity, I'm listening for cursor position changed event and resetting my cursor position in editText if it's there at the necessary space charatcer start i.e. at 0th index, like this;
editText.setOnSelectionChangeListener(new SelectionEnabledEditText.OnSelectionChangeListener() {
#Override
public void onSelectionChanged(int selStart, int selEnd) {
if (selEnd == 0) {
if (editText.getText().toString().length() == 0)
editText.setText(" ");
editText.setSelection(1);
}
}
});
Hope this would be helpful in similar situations. Suggestions are welcomed for improvements/optimizations.
The documentation states that the key events will only be propagated for the hardware key strokes, not software.
http://developer.android.com/reference/android/view/View.OnKeyListener.html
The device manufacturers are actually being discouraged to propagate soft keyboard events through key listeners, although it is completely up to the manufacturer to honour that or to actually treat the soft and hard keyboards with equal terms.
Starting from Android 4.2.2, Android system itself will not support key stoke events for the soft keyboards at all, so even the manufacturers will not be able to choose their way.
So the only foolproof option here is to implement your own IME (soft keyboard), and handle the keystrokes yourself.
TextWatcher can be used mostly to replace the key listeners, however editText.setText(...); will also trigger the TextWatcher events, so if one is interested in typed keys only then probably TextWatcher is not a solution either.
Please be cautious when using TextWatcher with AutocomleteTextView or EditText. Do not modify text in the AutocompleteTextView / EditText's content from within TextWatcher events, cause otherwise you'll most probably end up in an infinite event/listening loop.
Based on the documentation of the OnKeyListener it seems that the callback is invoked only for hardware keyboards.
Interface definition for a callback to be invoked when a hardware key event is dispatched to this view. The callback will be invoked before the key event is given to the view. This is only useful for hardware keyboards; a software input method has no obligation to trigger this listener.
There's an attribute for EditText: android:imeOptions
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/main_editor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionGo"
/>
I noticed that for values: actionGo, actionNone, normal, actionSearch onKeyListener got called.
P.S. It even worked without specifying that attribute.!
Try something like this :
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL) {
//Log key
Log.d("OnKeyListener", keyCode + " character(code) to send");
}
return false;
}
Thanks in advance for your answers. I am working on a basic tax calculator to help me better understand the fundamentals to the Android SDK. The user enters a subtotal and a tax rate (.08) and they press calculate to calculate the solution. I disable the calculate button until both fields have contents. My problem is that when the user enters numbers for both of the fields, the calculate button is still disabled. I posted the code I currently am running (the OnKeyListener). The edittext fields are registered to the listener.
private OnKeyListener mKeyListener = new OnKeyListener()
{
#Override
public boolean onKey(View v, int keyCode, KeyEvent event)
{
switch(v.getId())
{
case R.id.txtSub:
cmdSubmit.setEnabled(txtTax.getText().length()>0
&& txtSub.getText().length() > 0);
break;
case R.id.txtTax:
cmdSubmit.setEnabled(txtTax.getText().length()>0
&& txtSub.getText().length() > 0);
break;
}
return false;
}
};
If you look at the spec for OnKeyListener you'll notice that it gets invoked BEFORE the KeyEvent is given to the view. Other than that this looks right: I would suspect you aren't binding the listener to the right view. Put a break point in the method and make sure it is getting invoked correctly.
I've personally found the TextWatchers a much nicer way of keeping track of whether an EditText has data or not:
TextWatcher tw = new TextWatcher() {
#Override
public void afterTextChanged(Editable s) {
cmdSubmit.setEnabled(!Utils.isStringBlank(txtTax.getText().toString()) &&
!Utils.isStringBlank(txtSub.getText().toString()));
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
};
txtTax.addTextChangedListener(tw);
txtSub.addTextChangedListener(tw);