Android EditText listener for cursor position change

AndroidAndroid Edittext

Android Problem Overview


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 selStart == selEnd)

onSelectionChanged (int selStart, int selEnd) is a protected method of the TextView class. How do override it?

Android Solutions


Solution 1 - Android

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. :)

Solution 2 - Android

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);

Solution 3 - Android

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.

Solution 4 - Android

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``
            }
        }
    })

Solution 5 - Android

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);		
	    }
    }

}

Solution 6 - Android

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``                    
            }
        }
    });

Solution 7 - Android

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)
            }
        }
    })
}

Solution 8 - Android

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"/> 

Solution 9 - Android

editText.doAfterTextChanged {
    doSomething(editText.selectionStart , editText.selectionEnd)  
}

editText.setOnClickListener {
    doSomething(editText.selectionStart , editText.selectionEnd)  
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMelView Question on Stackoverflow
Solution 1 - AndroidnecixyView Answer on Stackoverflow
Solution 2 - AndroidMichaelView Answer on Stackoverflow
Solution 3 - AndroidAndrew MackenzieView Answer on Stackoverflow
Solution 4 - AndroidSaeed EntezariView Answer on Stackoverflow
Solution 5 - AndroidcurtleView Answer on Stackoverflow
Solution 6 - AndroidmifthiView Answer on Stackoverflow
Solution 7 - AndroidMario LenciView Answer on Stackoverflow
Solution 8 - AndroidGeorge StockerView Answer on Stackoverflow
Solution 9 - AndroidViacheslavInStackView Answer on Stackoverflow