Android EditText listener for cursor position change
I have a dialog with EditText in it. The EditText is already populated when it is created. When the user places the cursor on or near certain parts of the text a Toast will pop up.
My problem is listening for changes in cursor position. Another post asks the same question and the accepted solution was
You can override onSelectionChanged (int selStart, int selEnd) to get notified about selection changes. If the cursor is moved, this is called as well (in this case 开发者_JS百科selStart == selEnd)
onSelectionChanged (int selStart, int selEnd) is a protected method of the TextView class. How do override it?
Just subclass or extend the class EditText and add the following code to the newly create class:
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
// Do ur task here.
}
Don't forget to add constructors to the subclass. :)
You can actually listen to selection changes without subclassing an EditText
. It's a little more complicated but still manageable. To do it you need to add a SpanWatcher
to a text and handle changes of selection spans.
final SpanWatcher watcher = new SpanWatcher() {
@Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
@Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
@Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
// Selection start changed from ostart to nstart.
} else if (what == Selection.SELECTION_END) {
// Selection end changed from ostart to nstart.
}
}
};
editText.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
if anyone is still looking for a solution that does not Subclass the EditText
:
(Code is in kotlin)
editText.setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
})
I debugged a related problem across different versions of Android (feel free to comment on your findings and I'll add them to the list).
Summary of findings:
4.1.6 (Samsung device)
onSelectionChanged()
gets called on TEXT EDITS only.
4.0.6 (HTC Device)
4.0.4 (reported by user Arch1tect on Samsung Note 1 device)
onSelectionChanged()
gets called on cursor changes (clicks, moves etc) but NOT on Text Edits.
In the cases above where you are not informed of the section changes (text edits in some versions of Android), you will have to do that using a TextWatcher
, for example in the afterTextChanged()
method.
Oh goodness, thanks so much for this idea. There's absolutely no reason why this feature shouldn't be in the SDK. I have an quick subclass that implements this idea but adding on the additional feature of listeners for when the selection changes. Hope it's useful.
public class EditTextSelectable extends EditText {
public interface OnSelectionChangedListener {
public void onSelectionChanged(int selStart, int selEnd);
}
private List<onSelectionChangedListener> listeners
= new ArrayList<onSelectionChangedListener>();
public void addOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.add(l);
}
public void removeOnSelectionChangedListener(OnSelectionChangedListener l) {
listeners.remove(l);
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
for (onSelectionChangedListener l : listeners)
l.onSelectionChanged(selStart, selEnd);
}
}
}
The java version of the above answer,
mEtEditor.setAccessibilityDelegate(new View.AccessibilityDelegate(){
/**
* Sends an accessibility event of the given type. If accessibility is not
* enabled this method has no effect.
* <p>
* The default implementation behaves as {@link View#sendAccessibilityEvent(int)
* View#sendAccessibilityEvent(int)} for the case of no accessibility delegate
* been set.
* </p>
*
* @param host The View hosting the delegate.
* @param eventType The type of the event to send.
* @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
*/
@Override
public void sendAccessibilityEvent(View host, int eventType) {
super.sendAccessibilityEvent(host, eventType);
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
// here you can access selection of the editText
// with `editText.selectionStart`
// and `editText.selectionEnd``
}
}
});
Here is an Extension
version of what @Saeed Entezari posted:
fun EditText.setSelectionChangedListener(onSelectionChangedListener: (editText: EditText, selectionStart: Int, selectionEnd: Int) -> Unit) {
setAccessibilityDelegate(object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View?, eventType: Int) {
super.sendAccessibilityEvent(host, eventType)
if (eventType == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED){
val editText = this@setSelectionChangedListener
onSelectionChangedListener.invoke(editText, editText.selectionStart, editText.selectionEnd)
}
}
})
}
The Question asker originally posted their answer into the question. I'm moving it into an answer to keep the question and answer separated.
Step One: Create the sub class
package com.example;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;
public class EditTextCursorWatcher extends EditText {
public EditTextCursorWatcher(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public EditTextCursorWatcher(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EditTextCursorWatcher(Context context) {
super(context);
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
Toast.makeText(getContext(), "selStart is " + selStart + "selEnd is " + selEnd, Toast.LENGTH_LONG).show();
}
}
Step Two: refer to the class in the layout file (eg main.xml (though mine was a custom dialog layout)). Don't forget to use full package name (in this case com.example.EditTextCursorWatcher, eg
<com.example.EditTextCursorWatcher
android:id="@+id/etEdit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:minLines="5"
android:inputType="textMultiLine"/>
editText.doAfterTextChanged {
doSomething(editText.selectionStart , editText.selectionEnd)
}
editText.setOnClickListener {
doSomething(editText.selectionStart , editText.selectionEnd)
}
精彩评论