How can I detect delete (backspace) key event for a editText? I've tried using TextWatcher, but when the editText is empty, when I press delete key, nothing happens. I want to detect delete key press foe an editText even if it has no text.
NOTE: onKeyListener doesn't work for soft keyboards.
You can set OnKeyListener for you editText so you can detect any key press
EDIT: A common mistake we are checking KeyEvent.KEYCODE_BACK for backspace, but really it is KeyEvent.KEYCODE_DEL (Really that name is very confusing! )
editText.setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
//this is for backspace
}
return false;
}
});
It's been a while since you asked but I just had the same issue. As already mentioned by Estel the problem with key listeners is that they only work with hardware keyboards. To do this with an IME (soft keyboard), the solution is a bit more elaborate.
The single method we actually want to override is sendKeyEvent in the EditText's InputConnection class. This method is called when key events occur in an IME. But in order to override this, we need to implement a custom EditText which overrides the onCreateInputConnection method, wrapping the default InputConnection object in a proxy class! :|
Sounds complicated, but here's the simplest example I could contrive:
public class ZanyEditText extends EditText {
private Random r = new Random();
public ZanyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ZanyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZanyEditText(Context context) {
super(context);
}
public void setRandomBackgroundColor() {
setBackgroundColor(Color.rgb(r.nextInt(256), r.nextInt(256), r
.nextInt(256)));
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new ZanyInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class ZanyInputConnection extends InputConnectionWrapper {
public ZanyInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
#Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
ZanyEditText.this.setRandomBackgroundColor();
// Un-comment if you wish to cancel the backspace:
// return false;
}
return super.sendKeyEvent(event);
}
}
}
The line with the call to setRandomBackgroundColor is where my special backspace action occurs. In this case, changing the EditText's background colour.
If you're inflating this from XML remember to use the full package name as the tag:
<cc.buttfu.test.ZanyEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/somefield"
></cc.buttfu.test.ZanyEditText>
This is just an addition to Idris's answer, adding in the override to deleteSurroundingText as well. I found more info on that here: Android: Backspace in WebView/BaseInputConnection
package com.elavon.virtualmerchantmobile.utils;
import java.util.Random;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.widget.EditText;
public class ZanyEditText extends EditText {
private Random r = new Random();
public ZanyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ZanyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ZanyEditText(Context context) {
super(context);
}
public void setRandomBackgroundColor() {
setBackgroundColor(Color.rgb(r.nextInt(256), r.nextInt(256), r
.nextInt(256)));
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new ZanyInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class ZanyInputConnection extends InputConnectionWrapper {
public ZanyInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
#Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
ZanyEditText.this.setRandomBackgroundColor();
// Un-comment if you wish to cancel the backspace:
// return false;
}
return super.sendKeyEvent(event);
}
#Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// magic: in latest Android, deleteSurroundingText(1, 0) will be called for backspace
if (beforeLength == 1 && afterLength == 0) {
// backspace
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
Here is my easy solution, which works for all the API's:
private int previousLength;
private boolean backSpace;
// ...
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
previousLength = s.length();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
backSpace = previousLength > s.length();
if (backSpace) {
// do your stuff ...
}
}
UPDATE 17.04.18 .
As pointed out in comments, this solution doesn't track the backspace press if EditText is empty (the same as most of the other solutions).
However, it's enough for most of the use cases.
P.S. If I had to create something similar today, I would do:
public abstract class TextWatcherExtended implements TextWatcher {
private int lastLength;
public abstract void afterTextChanged(Editable s, boolean backSpace);
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastLength = s.length();
}
#Override
public void afterTextChanged(Editable s) {
afterTextChanged(s, lastLength > s.length());
}
}
Then just use it as a regular TextWatcher:
editText.addTextChangedListener(new TextWatcherExtended() {
#Override
public void afterTextChanged(Editable s, boolean backSpace) {
// Here you are! You got missing "backSpace" flag
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Do something useful if you wish.
// Or override it in TextWatcherExtended class if want to avoid it here
}
});
I sent 2 days to find a solution and I figured out a working one :) (on soft keys)
public TextWatcher textWatcher = 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) {
if (count == 0) {
//Put your code here.
//Runs when delete/backspace pressed on soft key (tested on htc m8)
//You can use EditText.getText().length() to make if statements here
}
}
#Override
public void afterTextChanged(Editable s) {
}
}
After add the textwatcher to your EditText:
yourEditText.addTextChangedListener(textWatcher);
I hope it works on another android devices too (samsung, LG, etc).
My simple solution which works perfectly. You should to add a flag. My code snippet:
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (after < count) {
isBackspaceClicked = true;
} else {
isBackspaceClicked = false;
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
#Override
public void afterTextChanged(Editable s) {
if (!isBackspaceClicked) {
// Your current code
} else {
// Your "backspace" handling
}
}
Example of creating EditText with TextWatcher
EditText someEdit=new EditText(this);
//create TextWatcher for our EditText
TextWatcher1 TW1 = new TextWatcher1(someEdit);
//apply our TextWatcher to EditText
someEdit.addTextChangedListener(TW1);
custom TextWatcher
public class TextWatcher1 implements TextWatcher {
public EditText editText;
//constructor
public TextWatcher1(EditText et){
super();
editText = et;
//Code for monitoring keystrokes
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_DEL){
editText.setText("");
}
return false;
}
});
}
//Some manipulation with text
public void afterTextChanged(Editable s) {
if(editText.getText().length() == 12){
editText.setText(editText.getText().delete(editText.getText().length() - 1, editText.getText().length()));
editText.setSelection(editText.getText().toString().length());
}
if (editText.getText().length()==2||editText.getText().length()==5||editText.getText().length()==8){
editText.setText(editText.getText()+"/");
editText.setSelection(editText.getText().toString().length());
}
}
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}
for some one who's using Kotlin
addOnTextChanged is not flexible enought to handle some cases (ex: detect if user press delete when edit text was empty)
setOnkeyListener worked even soft keyboard or hardkeyboard! but just on some devices. In my case, it work on Samsung s8 but not work on Xiaomi mi8 se.
if you using kotlin, you can use crossline function doOnTextChanged, it's the same as addOnTextChanged but callback is triggered even edit text was empty.
NOTE: doOnTextChanged is a part of Android KTX library
Based on #Jiff ZanyEditText here is WiseEditText with setSoftKeyListener(OnKeyListener)
package com.locopixel.seagame.ui.custom;
import java.util.Random;
import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
public class WiseEditText extends AppCompatEditText {
private Random r = new Random();
private OnKeyListener keyListener;
public WiseEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public WiseEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public WiseEditText(Context context) {
super(context);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
return new MyInputConnection(super.onCreateInputConnection(outAttrs),
true);
}
private class MyInputConnection extends InputConnectionWrapper {
public MyInputConnection(InputConnection target, boolean mutable) {
super(target, mutable);
}
#Override
public boolean sendKeyEvent(KeyEvent event) {
if (keyListener != null) {
keyListener.onKey(WiseEditText.this,event.getKeyCode(),event);
}
return super.sendKeyEvent(event);
}
#Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// magic: in latest Android, deleteSurroundingText(1, 0) will be called for backspace
if (beforeLength == 1 && afterLength == 0) {
// backspace
return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
public void setSoftKeyListener(OnKeyListener listener){
keyListener = listener;
}
}
This seems to be working for me :
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (before - count == 1) {
onBackSpace();
} else if (s.subSequence(start, start + count).toString().equals("\n")) {
onNewLine();
}
}
I am also faced same issue in Dialog.. because I am using setOnKeyListener.. But I set default return true. After change like below code it working fine for me..
mDialog.setOnKeyListener(new Dialog.OnKeyListener() {
#Override
public boolean onKey(DialogInterface arg0, int keyCode,
KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
mDialog.dismiss();
return true;
}
return false;//this line is important
}
});
My problem was, that I had custom Textwatcher, so I didn't want to add OnKeyListener to an EditText as well as I didn't want to create custom EditText. I wanted to detect if backspace was pressed in my afterTextChanged method, so I shouldn't trigger my event.
This is how I solved this. Hope it would be helpful for someone.
public class CustomTextWatcher extends AfterTextChangedTextWatcher {
private boolean backspacePressed;
#Override
public void afterTextChanged(Editable s) {
if (!backspacePressed) {
triggerYourEvent();
}
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
super.onTextChanged(s, start, before, count);
backspacePressed = count == 0; //if count == 0, backspace is pressed
}
}
I have tested #Jeff's solution on version 4.2, 4.4, 6.0. On 4.2 and 6.0, it works well. But on 4.4, it doesn't work.
I found an easy way to work around this problem. The key point is to insert an invisible character into the content of EditText at the begining, and don't let user move cursor before this character. My way is to insert a white-space character with an ImageSpan of Zero Width on it. Here is my code.
#Override
public void afterTextChanged(Editable s) {
String ss = s.toString();
if (!ss.startsWith(" ")) {
int selection = holder.editText.getSelectionEnd();
s.insert(0, " ");
ss = s.toString();
holder.editText.setSelection(selection + 1);
}
if (ss.startsWith(" ")) {
ImageSpan[] spans = s.getSpans(0, 1, ImageSpan.class);
if (spans == null || spans.length == 0) {
s.setSpan(new ImageSpan(getResources().getDrawable(R.drawable.zero_wdith_drawable)), 0 , 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}
And we need custom an EditText which has a SelectionChangeListener
public class EditTextSelectable extends android.support.v7.widget.AppCompatEditText {
public interface OnSelectChangeListener {
void onSelectChange(int start, int end);
}
private OnSelectChangeListener mListener;
public void setListener(OnSelectChangeListener listener) {
mListener = listener;
}
...constructors...
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (mListener != null) {
mListener.onSelectChange(selStart, selEnd);
}
super.onSelectionChanged(selStart, selEnd);
}
}
And the last step
holder.editText.setListener(new EditTextSelectable.OnSelectChangeListener() {
#Override
public void onSelectChange(int start, int end) {
if (start == 0 && holder.editText.getText().length() != 0) {
holder.editText.setSelection(1, Math.max(1, end));
}
}
});
And now, we are done~ We can detect backspace key event when EditText has no actual content, and user will know nothing about our trick.
This question may be old but the answer is really simple using a TextWatcher.
int lastSize=0;
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
//2. compare the old length of the text with the new one
//3. if the length is shorter, then backspace was clicked
if (lastSize > charSequence.length()) {
//4. Backspace was clicked
//5. perform action
}
//1. get the current length of of the text
lastSize = charSequence.length();
}
I have found a really simple solution which works with a soft keyboard.
override fun onTextChanged(text: CharSequence?, start: Int, before: Int, count: Int) {
text?.let {
if(count < before) {
Toast.makeText(context, "backspace pressed", Toast.LENGTH_SHORT).show()
// implement your own code
}
}
}
Belated but it may help new visitors, use TextWatcher() instead will help alot and also it will work for both soft and hard keyboard as well.
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.length() > 0) {
//Here it means back button is pressed and edit text is now empty
} else {
//Here edit text has some text
}
}
#Override
public void afterTextChanged(Editable editable) {
}
});
You could set a key listener on the activity, and in the callback method, you could detect
which key the user hit. The code below is for your reference. Hope it helps.
//after user hits keys, this method would be called.
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (editText.isFocused()) {
switch (keyCode) {
case KeyEvent.KEYCODE_DEL: //delete key
Log.i("INFO", "delete key hit"); //you should see this log in ddms after you hit delete key
break;
}
}
return super.onKeyUp(keyCode, event);
}
Related
I am implementing a custom view for OTP of varying length upto 6 digits. I have extended a LinearLayout and use multiple edit text as its child view. Each edit text holds one digit. I want to implement the delete action from the soft keyboard for the above custom view. The following is the code for the OTP custom view.
public class OTPEditText extends LinearLayout {
private int mDigitSpacing = 8; // Space between digits
private int mDigitNumber = 6; // Number of digits
private int mDigitSize = 28; // Font size of the digits
private ArrayList<EditText> mEditTexts; // List of edit text each holding one digit
private OnCompleteListener mCompleteListener; //when all the edit text gets one digit each
public OTPEditText(Context context) {
super(context);
}
public OTPEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public OTPEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/**
* Add the required number of Edit Texts
* #param number - number of digits
*/
public void setDigitNumber(int number){
this.mDigitNumber = number;
addViews();
}
public void setOnCompleteListener(OnCompleteListener listener) {
this.mCompleteListener = listener;
}
private void addViews() {
removeAllViews();
mEditTexts = new ArrayList<>();
for(int i = 0; i < mDigitNumber; i++){
EditText editText = new EditText(getContext());
//Set the necessary attributes
editText.addTextChangedListener(new GenericTextWatcher(i));
mEditTexts.add(editText);
addView(editText);
}
requestLayout();
if(mEditTexts.size() > 0) {
mEditTexts.get(0).requestFocus();
}
}
/**
* similar to setText of an edit text, but
* set one digit each to the edit text
* #param s - string for the edit text
*/
public void setText(String s){
if(s.length() > mDigitNumber){
s = s.substring(0, mDigitNumber);
}
int i;
for(i = 0; i < s.length(); i++){
mEditTexts.get(i).setText(s.charAt(i));
}
for(; i < mEditTexts.size(); i++){
mEditTexts.get(i).setText("");
}
}
/**
* Similar to the getText of an edit text,
* concatenates the text from each edit text
* #return - concatenated string from each edit text
*/
public String getText() {
String text = "";
if(!Utils.isEmptyList(mEditTexts)) {
for (EditText editText : mEditTexts){
text += editText.getText().toString();
}
}
return text;
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event){
return true;
}
/**
* Called whenever onClick of the View is called. Simulates the click event of
* the required edit text.
*/
public void doClick() {
if(!Utils.isEmptyList(mEditTexts)){
for(EditText editText : mEditTexts){
if(editText.getText().toString().equals("")){
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN , 0, 0, 0));
editText.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP , 0, 0, 0));
return;
}
}
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
mEditTexts.get(mEditTexts.size()-1).dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(),
SystemClock.uptimeMillis(), MotionEvent.ACTION_UP ,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, mDigitSize,
getResources().getDisplayMetrics()), 0, 0));
}
}
public interface OnCompleteListener {
void onComplete();
}
// Generic edit text watcher
public class GenericTextWatcher implements TextWatcher {
private int index;
public GenericTextWatcher(int index){
this.index = index;
}
#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.toString().length() >= 1){
if(index +1 < mEditTexts.size()){
mEditTexts.get(index + 1).requestFocus();
} else if(index == mEditTexts.size() - 1 && mCompleteListener != null){
mCompleteListener.onComplete();
}
}
}
}
}
edOtp1.addTextChangedListener(new OtpTextWatcher(edOtp1));
edOtp2.addTextChangedListener(new OtpTextWatcher(edOtp2));
edOtp3.addTextChangedListener(new OtpTextWatcher(edOtp3));
edOtp4.addTextChangedListener(new OtpTextWatcher(edOtp4));
create this class that handle text on addition or deletion.
private class OtpTextWatcher implements TextWatcher
{
private View view;
OtpTextWatcher(View view) {
this.view = view;
}
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
String text = charSequence.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
else{
edOtp1.requestFocus();
}
break;
case R.id.edOtp2:
if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
#Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void afterTextChanged(Editable editable) {
String text = editable.toString();
switch (view.getId()) {
case R.id.edOtp1:
if (text.length() == 1)
edOtp2.requestFocus();
else
edOtp1.setSelection(edOtp1.getText().length());
break;
case R.id.edOtp2:
if (text.length() == 1)
edOtp3.requestFocus();
else if (text.length() == 0) {
edOtp1.requestFocus();
edOtp1.setSelection(edOtp1.getText().length());
}
break;
case R.id.edOtp3:
if (text.length() == 1)
edOtp4.requestFocus();
else if (text.length() == 0) {
edOtp2.requestFocus();
edOtp2.setSelection(edOtp2.getText().length());
}
break;
case R.id.edOtp4:
if (text.length() == 0) {
edOtp3.requestFocus();
edOtp3.setSelection(edOtp3.getText().length());
}
break;
default:
break;
}
}
}
I created a gist here https://gist.github.com/ShivamPokhriyal/8d0cf4aef062e6c59d00c04c53e03158 which you can simply copy paste in your project.
It creates a custom OTPEditText class which handles shifting the focus to next or previous edittext when user types in or deletes and also handles the paste event when user long presses and pastes the otp in the editText. All this can be done in the xml only. No need to pollute your activity with these stuff.
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* This class handles otp input in multiple edittexts.
* It will move focus to next edittext, if available, when user enters otp.
* And it will move focus to the previous edittext, if available, when user deletes otp.
* It will also delegate the paste option, if user long presses and pastes a string into the otp input.
*
* <b>XML attributes</b>
*
* #attr ref your_package_name.R.styleable#OTPView_nextView
* #attr ref your_package_name.R.styleable#OTPView_prevView
*
* #author $|-|!˅#M
*/
public class OTPEditText extends androidx.appcompat.widget.AppCompatEditText {
#Nullable
private View nextView;
#Nullable
private View previousView;
// Unfortunately getParent returns null inside the constructor. So we need to store the IDs.
private int nextViewId;
private int previousViewId;
#Nullable
private Listener listener;
private static final int NO_ID = -1;
public interface Listener {
void onPaste(String s);
}
public OTPEditText(#NonNull Context context) {
super(context);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public OTPEditText(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
public void setListener(Listener listener) {
this.listener = listener;
}
/**
* Called when a context menu option for the text view is selected. Currently
* this will be one of {#link android.R.id#selectAll}, {#link android.R.id#cut},
* {#link android.R.id#copy}, {#link android.R.id#paste} or {#link android.R.id#shareText}.
*
* #return true if the context menu item action was performed.
*/
#Override
public boolean onTextContextMenuItem(int id) {
if (id == android.R.id.paste) {
ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// Examines the item on the clipboard. If getText() does not return null, the clip item contains the
// text. Assumes that this application can only handle one item at a time.
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
// Gets the clipboard as text.
CharSequence pasteData = item.getText();
if (listener != null && pasteData != null) {
listener.onPaste(pasteData.toString());
return true;
}
}
return super.onTextContextMenuItem(id);
}
#Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(focused, direction, previouslyFocusedRect);
// If we've gotten focus here
if (focused && this.getText() != null) {
this.setSelection(this.getText().length());
}
}
private void init(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.OTPView, 0, 0);
nextViewId = typedArray.getResourceId(R.styleable.OTPView_nextView, NO_ID);
previousViewId = typedArray.getResourceId(R.styleable.OTPView_prevView, NO_ID);
typedArray.recycle();
this.setOnKeyListener((v, keyCode, event) -> {
if (event.getAction()!= KeyEvent.ACTION_DOWN) {
return true;
}
//You can identify which key pressed by checking keyCode value with KeyEvent.KEYCODE_
if(keyCode == KeyEvent.KEYCODE_DEL) {
// Back pressed. If we have a previous view. Go to it.
if (getPreviousView() != null) {
getPreviousView().requestFocus();
return true;
}
}
return false;
});
this.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() == 1 && getNextView() != null) {
getNextView().requestFocus();
} else if (s.length() == 0 && getPreviousView() != null) {
getPreviousView().requestFocus();
}
}
});
// Android 3rd party keyboards show the copied text into the suggestion box for the user.
// Users can then simply tap on that suggestion to paste the text on the edittext.
// But I don't know of any API that allows handling of those paste actions.
// Below code will try to tell those keyboards to stop showing those suggestion.
this.setInputType(EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS | EditorInfo.TYPE_CLASS_NUMBER);
}
private View getNextView() {
if (nextView != null) {
return nextView;
}
if (nextViewId != NO_ID && getParent() instanceof View) {
nextView = ((View) getParent()).findViewById(nextViewId);
return nextView;
}
return null;
}
private View getPreviousView() {
if (previousView != null) {
return previousView;
}
if (previousViewId != NO_ID && getParent() instanceof View) {
previousView = ((View) getParent()).findViewById(previousViewId);
return previousView;
}
return null;
}
}
The gist also includes the xml and java code that you can directly add to your activity.
I want to intercept 0 to 9 button key events from soft keyboard in android. I have tried many ways but didn't succeed. any little help will help me a lot.
what I am doing is,
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode==KeyEvent.KEYCODE_12)
{
Toast.makeText(context, "Pressed", Toast.LENGTH_LONG).show();
return true;
}
return super.onKeyDown(keyCode, event);
}
in my custom EditText class but it is not working what am i missing? i have tried many key codes but no result in hand.
Use a text watcher, its much simpler:
At Class level:
EditText editText;
in onCreate:
editText = (EditText)findViewById(R.id.yourEdittext)
editText.addTextChangedListener(mTextEditorWatcher);
Outside onCreate(Class level) :
final TextWatcher mTextEditorWatcher = new TextWatcher(){
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
System.out.println("Entered text: "+editText.getText());
// USe edit_text.getText(); here
}
public void afterTextChanged(Editable s) {
}
};
If you want to restrict the entry on your Edit Text to only alphabets add this in the XML of your edit text control:
android:digits="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
If you dont like the above and want to achieve this through code, use the following:
editText.setFilters(new InputFilter[] {
new InputFilter() {
public CharSequence filter(CharSequence chr, int start,
int end, Spanned dst, int dstart, int dend) {
if(chr.equals("")){
return chr;
}
if(chr.toString().matches("[a-zA-Z ]+")){
return chr;
}
return "";
}
}
});
I am working with android 2.2.
How to know which character is get deleted on backspace when editing text in custom auto complete in android.
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// how to find here which character is get deleted
}
return false;
}
String prevText = "";
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
int pos = myEditText.getSelectionStart();
char c = prevText.charAt(pos);
// c is deleted
}
prevText = myEditText.getText.toString();
return false;
}
You can use add a TextWatcher to AutoCompleteTextView with addTextChangeListener(TextWatcher).
You don't need to listen to onKeyUp() the various TextWatcher methods inform you if the user is adding or removing text.
The easiest way is just keep last character that you type
int lastKeyCode;
public boolean onKeyUp(int keyCode,KeyEvent msg){
if(keyCode == KeyEvent.KEYCODE_DEL)
{
// print lastKeyCode here
// how to find here which character is get deleted
}
lastKeyCode = keyCode;
return false;
}
Try this, working for me
editText.addTextChangedListener(new TextWatcher() {
String initialChar = null;
int initCursorPosition = 0;
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int after) {
char addedChar = 0;
int finalCursorPosition = editText.getSelectionStart();
if (finalCursorPosition - initCursorPosition > 0) {
addedChar = charSequence.charAt(finalCursorPosition - 1);
Log.d(TAG, "onTextChanged added: " + addedChar);
//added char
} else {
char delChar = initialChar.charAt(initCursorPosition - 1);
Log.d(TAG, "onTextChanged deletedChar: " + delChar);
//deleted char
}
}
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int before, int after) {
Log.d(TAG, "textChange beforeTextChanged: " + charSequence);
Log.d(TAG, "textChange cursorPosition: " + editText.getSelectionStart());
initialChar = String.valueOf(charSequence);
initCursorPosition = editText.getSelectionStart();
}
#Override
public void afterTextChanged(Editable arg0) {
}
});
(Edittext).setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event)
{
if(event.getKeyCode()==67)
{
if((EditText).getText().toString().length()>0)
{
int pos = (Edittext).getSelectionStart();
Char c = (EditText).getText().toString.charAt(pos);
Toast.makeText(getApplicationontext(),String.valueOf(c),Toast.LENGTH_SHORT).show();
}
}
return false;
}
});
I think it helps you
I know this is a very late answer, but this would be effective for future users.
First the KeyEvent.KEYCODE_DEL doesn't work for soft keyboard in latest android versions, so you have to create a custom EditText to handle that.
So we'll create a class to handle all null type in LatinIME
import android.text.SpannableStringBuilder;
public class EditableAccomodatingLatinIMETypeNullIssues extends SpannableStringBuilder {
EditableAccomodatingLatinIMETypeNullIssues(CharSequence source) {
super(source);
}
public static CharSequence ONE_UNPROCESSED_CHARACTER = "/";
#Override
public SpannableStringBuilder replace(final int
spannableStringStart, final int spannableStringEnd, CharSequence replacementSequence,
int replacementStart, int replacementEnd) {
if (replacementEnd > replacementStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, replacementSequence, replacementStart, replacementEnd);
}
else if (spannableStringEnd > spannableStringStart) {
super.replace(0, length(), "", 0, 0);
return super.replace(0, 0, ONE_UNPROCESSED_CHARACTER, 0, 1);
}
return super.replace(spannableStringStart, spannableStringEnd,
replacementSequence, replacementStart, replacementEnd);
}
}
Then we'll go ahead to create another class to handle the InputConnection of the custom EditText to be created
import android.os.Build;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
public class InputConnectionAccomodatingLatinIMETypeNullIssues extends BaseInputConnection {
Editable myEditable = null;
public InputConnectionAccomodatingLatinIMETypeNullIssues(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
}
#Override
public Editable getEditable() {
if(Build.VERSION.SDK_INT >= 14) {
if(myEditable == null) {
myEditable = new EditableAccomodatingLatinIMETypeNullIssues(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
else {
int myEditableLength = myEditable.length();
if(myEditableLength == 0) {
myEditable.append(
EditableAccomodatingLatinIMETypeNullIssues.ONE_UNPROCESSED_CHARACTER);
Selection.setSelection(myEditable, 1);
}
}
return myEditable;
}
else {
return super.getEditable();
}
}
#Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if((Build.VERSION.SDK_INT >= 14) // && (Build.VERSION.SDK_INT < 19)
&& (beforeLength == 1 && afterLength == 0)) {
return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
&& super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
}
else {
return super.deleteSurroundingText(beforeLength, afterLength);
}
}
}
Then creating the custom EditText
import android.content.Context;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
public class CustomEditText extends androidx.appcompat.widget.AppCompatEditText {
public CustomEditText(#NonNull #NotNull Context context) {
super(context);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomEditText(#NonNull #NotNull Context context, #Nullable #org.jetbrains.annotations.Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
InputConnectionAccomodatingLatinIMETypeNullIssues baseInputConnection =
new InputConnectionAccomodatingLatinIMETypeNullIssues(this, false);
outAttrs.actionLabel = null;
outAttrs.inputType = InputType.TYPE_NULL;
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
return baseInputConnection;
}
}
So in your .xml file, you can say something like
<your.package.name.CustomEditText
<!set all required attributes-->
/>
Now in your java code, you have to listen for the Backspace click event and get the character to be deleted before it's deleted.
editText.setOnKeyListener(new View.OnKeyListener(){
public boolean onKey(View v, int keyCode, KeyEvent event){
if(event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_DEL) {
int selStart = editText.getSelectionStart();
int selEnd = editText.getSelectionEnd();
//incase if some text in the textbox are selected, selStart and selEnd will
//return the positions of both selected ends and you can get the text(s) deleted.
//check if the selEnd is at 0, and if true, then there's not point going further
if(selEnd == 0)
return false;
//if there is no texts selected, then reduce selStart by one to get the text to be deleted
if(selStart == selEnd)
selStart -= 1;
String text = editText.getText().toString();
//now this will get you char or chars to be deleted
String charDeleted = text.substring(selStart, selEnd);
//So before returning false to deleted the char,
//let me answer the question asked also on the
//comment section in Bobs' answer
//which is to find the color code of the deleted char
//Note, this is for only a single char, if you want to
//handle for multiple chars, then you'll have to split the
//deleted chars into an array and get the color codes for each
//This will get all HTML text within the deleted char including the color code
String htmlText = Html.toHtml((Spanned) editText.getText().subSequence(selStart, selEnd));
//Now you have to use a HTML parser, and I'm using JSoup
//Don't forget to implement the JSoup library into your project
Document doc = Jsoup.parse(htmlText, "UTF-8");
//Here I'm selecting a SPAN html tag, hoping that the char should be in a SPAN
//If it's in a FONT or any other tag, then you select the tag instead and also
//the attribute containing the color code
Elements element = doc.select("span");
//Attribute containing the color code for SPAN is the STYLE
String style = element.attr("style");
//So the style string would be giving you something like "color:#AABBCC;"
//Where #AABBCC is the color code of the deleted char
}
return false;
}
});
Try this
edittext.addTextChangeListener(new TextWatcher{
#override
public void afterTextChanged(Editable s){
String changedtext = s.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){}
});
I have an edittext, and a textwatcher that watches if SPACE arrived or not. If its a SPACE I would like to delete that instantly. Or if its a space I want to make sure it doesnt appear but indicate somehow (seterror, toast) for the user that space is not allowed.
edittext.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
//---//
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
I cannot define onkeydown in the afterTextChaned method, since it gives me an error.
public boolean onKeyDown(int keyCode, KeyEvent event) {
super.onKeyDown(keyCode, event);
if (keyCode == KeyEvent.KEYCODE_SPACE) {
}
}
So it is not working (syntax error, misplaced construct for the int keyCode.
Thanks you in advance!
The solution is as usually much simpler:
#Override
public void afterTextChanged(Editable s) {
String result = s.toString().replaceAll(" ", "");
if (!s.toString().equals(result)) {
ed.setText(result);
ed.setSelection(result.length());
// alert the user
}
}
This shouldn't have the problems of the previous attempts.
setSelection is there to set the cursor again at the end of your EditText:
editText.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence cs, int arg1, int arg2,
int arg3) {}
#Override
public void beforeTextChanged(CharSequence s, int arg1, int arg2,
int arg3) {}
#Override
public void afterTextChanged(Editable arg0) {
if(editText.getText().toString().contains(" ")){ editText.setText(editText.getText().toString().replaceAll(" " , ""));
editText.setSelection(editText.getText().length());
Toast.makeText(getApplicationContext(), "No Spaces Allowed", Toast.LENGTH_LONG).show();
}
}});
boolean editclicked =false ;
edittext.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
editclicked = false ;
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void onTextChanged(CharSequence s, int start, int before, int count) {
editclicked = true;
});
Put this as a separate function:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (editclicked) {
if (keyCode == KeyEvent.KEYCODE_SPACE) {
return false
}
} else {
super.onKeyDown(keyCode, event);
}
}
#Override
public void afterTextChanged(Editable s) {
String result = s.toString().replaceAll("\\s", "");
if (!s.toString().equals(result)) {
int pos = editText.getSelectionStart() - (s.length() - result.length());
editText.setText(result);
editText.setSelection(Math.max(0,Math.min(pos, result.length())));
editText.setError("No spaces allowed");
}
}
\s matches any whitespace character (equal to [\r\n\t\f\v ])
Setting selection like this, allow you to enter or paste text in middle of edittext without loosing cursor position
My relatively simple solution for instant whitespace deletion without removing spannables (styles) in EditText:
Remove at start:
#Override
public void afterTextChanged(Editable s) {
int i;
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
}
Basically that's it, but you can also do:
Remove at start (without interrupting first input):
#Override
public void afterTextChanged(Editable s) {
String text = s.toString();
if(!text.trim().isEmpty()){
int i;
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
}
}
Removing at start and end (allow 1 whitespace at end for convinient input):
#Override
public void afterTextChanged(Editable s) {
int i;
//remove at start
for (i = 0; i < s.length() && Character.isWhitespace(s.charAt(i)); i++) { ; }
s.replace(0, i, "");
//remove at end, but allow one whitespace character
for (i = s.length(); i > 1 && Character.isWhitespace(s.charAt(i-1)) && Character.isWhitespace(s.charAt(i-2)); i--) { ; }
s.replace(i, s.length(), "");
}
For removing the space instantly you can achieve it by two ways.
One simple solution you can set the digits to your edit text.
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
second way you can set a filter
EditText.setFilters(new InputFilter[] { filter });
InputFilter filter = new InputFilter() {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {
for (int i = start; i < end; i++) {
if (Character.isSpaceChar(source.charAt(i))) {
return "";
}
}
return null;
}
}
One more simple way to achieve this using the input Filter
editText.setFilters(new InputFilter[]{new InputFilter() {
#Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
if (source.toString().equalsIgnoreCase(" ")){
return "";
}
return source;
}
}});
This will remove the space entered by the user immediately and gives appearance like space is disabled.
<EditText
android:id="#+id/editText2"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:maxLines="5"
android:lines="5">
</EditText>
User can input more than 5 lines, by pressing enter/next row key. How can I limit user input to fixed amount of rows with EditText?
<EditText
android:id="#+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:maxLines="1"
/>
You just need to make sure you have the attribute "inputType" set. It doesn't work without this line.
android:inputType="text"
The attribute maxLines corresponds to the maximum height of the EditText, it controls the outer boundaries and not inner text lines.
This does not solve the general issue of limiting to n lines. If you want to limit your EditText to take just 1 line of text, this can be very easy.
You can set this in the xml file.
android:singleLine="true"
or programmatically
editText.setSingleLine(true);
#Cedekasem you are right, there isn't a built in "row limiter". But I did built one my self, so if anyone is interested the code is below. Cheers.
et.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// if enter is pressed start calculating
if (keyCode == KeyEvent.KEYCODE_ENTER
&& event.getAction() == KeyEvent.ACTION_UP) {
// get EditText text
String text = ((EditText) v).getText().toString();
// find how many rows it cointains
int editTextRowCount = text.split("\\n").length;
// user has input more than limited - lets do something
// about that
if (editTextRowCount >= 7) {
// find the last break
int lastBreakIndex = text.lastIndexOf("\n");
// compose new text
String newText = text.substring(0, lastBreakIndex);
// add new text - delete old one and append new one
// (append because I want the cursor to be at the end)
((EditText) v).setText("");
((EditText) v).append(newText);
}
}
return false;
}
});
I did something like you guys have been looking for. Here's my LimitedEditText class.
Features:
you can limit lines count in your LimitedEditText component
you can limit characters count in your LimitedEditText component
if you exceed the limit of characters or lines somewhere in the middle of text, cursor won't bring you to the end - it will stay where have you been.
Im turning off listener, because every call of setText() method would recursively call these 3 callback methods in case when user exceeded characters or lines limit.
Code:
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.EditText;
import android.widget.Toast;
/**
* EditText subclass created to enforce limit of the lines number in editable
* text field
*/
public class LimitedEditText extends EditText {
/**
* Max lines to be present in editable text field
*/
private int maxLines = 1;
/**
* Max characters to be present in editable text field
*/
private int maxCharacters = 50;
/**
* application context;
*/
private Context context;
public int getMaxCharacters() {
return maxCharacters;
}
public void setMaxCharacters(int maxCharacters) {
this.maxCharacters = maxCharacters;
}
#Override
public int getMaxLines() {
return maxLines;
}
#Override
public void setMaxLines(int maxLines) {
this.maxLines = maxLines;
}
public LimitedEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
public LimitedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public LimitedEditText(Context context) {
super(context);
this.context = context;
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
TextWatcher watcher = new TextWatcher() {
private String text;
private int beforeCursorPosition = 0;
#Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
//TODO sth
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
text = s.toString();
beforeCursorPosition = start;
}
#Override
public void afterTextChanged(Editable s) {
/* turning off listener */
removeTextChangedListener(this);
/* handling lines limit exceed */
if (LimitedEditText.this.getLineCount() > maxLines) {
LimitedEditText.this.setText(text);
LimitedEditText.this.setSelection(beforeCursorPosition);
}
/* handling character limit exceed */
if (s.toString().length() > maxCharacters) {
LimitedEditText.this.setText(text);
LimitedEditText.this.setSelection(beforeCursorPosition);
Toast.makeText(context, "text too long", Toast.LENGTH_SHORT)
.show();
}
/* turning on listener */
addTextChangedListener(this);
}
};
this.addTextChangedListener(watcher);
}
}
I've made simpler solution for this :D
// set listeners
txtSpecialRequests.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
lastSpecialRequestsCursorPosition = txtSpecialRequests.getSelectionStart();
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
txtSpecialRequests.removeTextChangedListener(this);
if (txtSpecialRequests.getLineCount() > 3) {
txtSpecialRequests.setText(specialRequests);
txtSpecialRequests.setSelection(lastSpecialRequestsCursorPosition);
}
else
specialRequests = txtSpecialRequests.getText().toString();
txtSpecialRequests.addTextChangedListener(this);
}
});
You can change the value of 3 in txtSpecialRequests.getLineCount() > 3 to your needs.
android:inputType="text" (or something different to "none")
android:maxLines="1" (and this line)
set editText android:inputType="text"
Here is a InputFilter that limits allowed lines in EditText:
/**
* Filter for controlling maximum new lines in EditText.
*/
public class MaxLinesInputFilter implements InputFilter {
private final int mMax;
public MaxLinesInputFilter(int max) {
mMax = max;
}
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
int newLinesToBeAdded = countOccurrences(source.toString(), '\n');
int newLinesBefore = countOccurrences(dest.toString(), '\n');
if (newLinesBefore >= mMax - 1 && newLinesToBeAdded > 0) {
// filter
return "";
}
// do nothing
return null;
}
/**
* #return the maximum lines enforced by this input filter
*/
public int getMax() {
return mMax;
}
/**
* Counts the number occurrences of the given char.
*
* #param string the string
* #param charAppearance the char
* #return number of occurrences of the char
*/
public static int countOccurrences(String string, char charAppearance) {
int count = 0;
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == charAppearance) {
count++;
}
}
return count;
}
}
To add it to EditText:
editText.setFilters(new InputFilter[]{new MaxLinesInputFilter(2)});
This is what i used in my project:
editText.addTextChangedListener(new TextWatcher() {
private String text;
public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
}
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {
text = arg0.toString();
}
public void afterTextChanged(Editable arg0) {
int lineCount = editText.getLineCount();
if(lineCount > numberOfLines){
editText.setText(text);
}
}
});
editText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// if enter is pressed start calculating
if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN){
int editTextLineCount = ((EditText)v).getLineCount();
if (editTextLineCount >= numberOfLines)
return true;
}
return false;
}
});
And it worked in all scenarios
Simplest solution:
android:maxLines="3"
...
#Override
public void afterTextChanged(Editable editable) {
// limit to 3 lines
if (editText.getLayout().getLineCount() > 3)
editText.getText().delete(editText.getText().length() - 1, editText.getText().length());
}
You can limit your text according to your no of lines i say around 37 alphabets in one line
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:lines="4"
android:maxLines="4"
android:minLines="4"
android:maxLength="150"
android:gravity="start"
android:background="#efeef5"
android:layout_marginTop="#dimen/pad_10dp"/>
this is one approach. Might help someone.
android:lines="1"
android:maxLines="1"
android:inputType="text
This is an extension of Indrek Kõue answer to Kotlin
input_name.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}
#SuppressLint("SetTextI18n")
override fun onTextChanged(
s: CharSequence?,
start: Int,
before: Int,
count: Int
) {
val text = (input_name as EditText).text.toString()
val editTextRowCount = input_name.lineCount
if (editTextRowCount > 15) {
val newText = text.substring(0, text.length - 1)
input_name.setText("")
input_name.append(newText)
}
}
})
<EditText
android:id="#+id/usrusr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:lines="1"
android:maxLines="1"
android:inputType="text"
android:hint="#string/inventory_no" />
Another idea: each time after typing, new text would be saved to a String lastText, only if the number of lines does not exeed the MAX_LINES. If it does, we would set the text of EditText to the last added text (so the changes would be deleted) and notify user to keep it short.
// Set listener to wishDescriptionEditText in order to limit line number
editText.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 line account is higher than MAX_LINES, set text to lastText
// and notify user (by Toast)
if (editText.getLineCount() > MAX_LINES) {
editText.setText(lastText);
Toast.makeText(getContext(), "Please keep it short", Toast.LENGTH_LONG).show();
} else {
lastText = editText.getText().toString();
}
}
});
getLineCount() is one option; if you want non-zero values there make sure your view is measured. For soft keyboard onKeyListener won't work so you have to add
addTextChangedListener() that will track text changes as you type. As soon as you get enough lines inside its call backs do whatever you want to limit it: delete characters with getText(), setText() or something more fancy. You can even limit the number of characters using a filter.
Another option is to monitor size of the text with getLineBounds(). This will interact with text gravity/paddign so be careful.
For limit number of characters we can simply use maxLength property of EditText as it will not allow user to enter more characters.
Another way to limit your EditText to one line is the following:
editText2.setTransformationMethod(new SingleLineTransformationMethod());
Note that after applying this transformation method, the enter key creates spaces when pressed. That still satisfies TS' question.