21

I have two EditText (each only only accepts one character) and I want to handle both fields like I had only one.

I'm using a TextWatcher to set the focus in the second one when the user writes a character in the first one, but I don't know how to do the opposite.

If the user press the delete button in the second EditText (being this EditText empty) I want to move the focus to the first EditText and delete the character there.

The problem is that TextWatcher doesn't work when the user tries to delete an empty field (because in fact nothing is changing). And onKeyDown event only works with hard keyboards so I don't have any idea of how to deal with this problem...

Thanks!

michael_ferl
  • 275
  • 1
  • 3
  • 8
  • Have You tried onKeyDown on device with hardware keyboard? Do You need to catch back or delete pressing? For back here's similar question http://stackoverflow.com/questions/3940127/intercept-back-button-from-soft-keyboard – sandrstar Aug 23 '12 at 02:42
  • yes, I tried with onKeyDown... but I need to catch the delete button in the soft keyboard. If the user press the delete button with the focus in the second edittext (and being the edittext empty), the focus will go to the first edittext) – michael_ferl Aug 23 '12 at 03:37

5 Answers5

15

Possible duplicate of Android EditText delete(backspace) key event

just checked the code from that question (which actually come from the provided question and answered by Labeeb P) with the test project with just two edits on layout and it seems to work just fine - I'm able to receive delete even if edit is empty.

    final EditText edit1 = (EditText) findViewById(R.id.editText1);

    edit1.setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            // You can identify which key pressed buy checking keyCode value
            // with KeyEvent.KEYCODE_
            if (keyCode == KeyEvent.KEYCODE_DEL) {
                // this is for backspace
                Log.e("IME_TEST", "DEL KEY");
            }
            return false;
        }
    });

Seems android documentation of EditText should be made more clear or at least any guide for EditText - Soft Keyboard interaction provided, because there many typical ones whose should be worked out by nearly every developer.

UPDATE: Seems this way doesn't work on latest (at least after 4.1) Android versions. This answer seems to work on versions after 4.1.

Community
  • 1
  • 1
sandrstar
  • 12,503
  • 8
  • 58
  • 65
  • strange, because then I've checked it (more than 6 months ago) it was working fine on nexus one device. – sandrstar Mar 28 '13 at 05:42
  • Neither on 4.2.2 But it doesn't work with Android stock virtual keyboard. I also have Swiftkey keyboard and I'm able to receive KEYCODE_DEL on OnKeyListener. – fr4gus Jun 21 '13 at 20:44
  • Seems, this http://stackoverflow.com/a/11377462/657487 answer could resolve the issue. – sandrstar Jun 22 '13 at 09:21
  • @DJphy 1. I've checked that answer myself, not sure if have you checked anything or not. 2. Provide better answer if you can and it can be accepted. – sandrstar Jun 10 '15 at 06:11
  • 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. – Saurabh Sep 18 '17 at 18:42
10

A simpler solution to this that I stumbled upon is using an InputFilter. InputFilter's filter() method appears to report all soft keyboard events - even those where the EditText's value isn't changing.

So to address your specific situation, construct an input filter and set accordingly:

private InputFilter filter = (charSequence, start, end, dest, dStart, dEnd) -> {

    if (end == 0 || dStart < dEnd) {
        // backspace was pressed! handle accordingly
    }

    return charSequence;
};

...

myEditText.setFilters(new InputFilter[] { filter });

Backspace events can be evaluated using end, dStart, and dEnd. dStart will always be less than dEnd if a character was deleted. If the EditText is empty, you can still evaluate backspace presses by checking if end == 0.

Note that bulk deletes will also be caught in this if statement, so you may want to do some extra checking withing filter(). Also note that if you're using your computer keyboard to type into EditTexts in emulators, you can get unexpected results. Best to click software buttons for testing.

AlexOlsen
  • 187
  • 2
  • 10
  • Doesn't work. Tried this only gets called when u delete the last cha from edit text. But don't get it when user press on delete on empty edit text. – Saurabh Sep 18 '17 at 18:41
  • @John Thanks for mentioning this. I updated the answer to correctly report. R.e. pressing delete on an empty edit text, is this happening on a real phone or emulator? If you're using your computer's 'backspace' to trigger deletes, you'll probably get unexpected results. Best to manually click the emulator's software buttons. – AlexOlsen Sep 19 '17 at 19:41
  • @AlexOlsen Tried on nexus 5x device – Saurabh Sep 21 '17 at 14:32
  • Confirmed this works!! This should be the best answer. – passerbywhu Oct 30 '17 at 06:00
  • can you please show how it is implemented with short example, confused with dStart – mohd khalid Siddiqui Sep 24 '19 at 10:11
4

Use the Extension provided at, https://github.com/ciasaboark/Android-Shell/blob/master/src/com/example/com/programmingthetux/tutorial/ZanyEditText.java

import java.util.Random;

import android.content.Context;
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;
/**
 * Created by mkallingal on 4/25/2016.
 */
public class CustomEditText extends EditText {

    private Random r = new Random();

    public CustomEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomEditText(Context context) {
        super(context);
    }

    public void setRandomBackgroundColor() {

    }

    @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) {
                CustomEditText.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);
        }

    }
}

Now you can use it in your Activity like so:

final CustomEditText editText = new CustomEditText(cxt);

editText.setOnKeyListener(new View.OnKeyListener() {
                    @Override
                    public boolean onKey(View v, int keyCode, KeyEvent event) {
                        if (keyCode == KeyEvent.KEYCODE_DEL) {
                            String _text= editText.getText().toString();
                            if(StringUtils.isBlank(_text))
                                 //editText is now empty
                            }
                        }
                        return false;
                    }
                });
Irshu
  • 8,248
  • 8
  • 53
  • 65
1

I achieved it by overriding EditText in order to get access to InputConnection object which contains deleteSurroundingText method. It helps to detect deletion (backspace) event. Please, take a look at a solution I provided there: Android - cannot capture backspace/delete press in soft. keyboard

This solution works properly for both hardKeyboard and softKeyboard.

Community
  • 1
  • 1
Ayaz Alifov
  • 8,334
  • 4
  • 61
  • 56
0
package com.bikash.layout;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity implements View.OnKeyListener, TextWatcher {
    EditText otp1, otp2, otp3, otp4, otp5, otp6;
    private boolean canDelete = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        otp1 = findViewById(R.id.otp1);
        otp2 = findViewById(R.id.otp2);
        otp3 = findViewById(R.id.otp3);
        otp4 = findViewById(R.id.otp4);
        otp5 = findViewById(R.id.otp5);
        otp6 = findViewById(R.id.otp6);

        otp1.addTextChangedListener(this);
        otp2.addTextChangedListener(this);
        otp3.addTextChangedListener(this);
        otp4.addTextChangedListener(this);
        otp5.addTextChangedListener(this);
        otp6.addTextChangedListener(this);

        otp1.setOnKeyListener(this);
        otp2.setOnKeyListener(this);
        otp3.setOnKeyListener(this);
        otp4.setOnKeyListener(this);
        otp5.setOnKeyListener(this);
        otp6.setOnKeyListener(this);

        otp1.requestFocus();
    }

    @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 (otp1.isFocused()) {
            if (otp1.getText().toString().length() == 1) {
                otp2.requestFocus();
            }
        }
        if (otp2.isFocused()) {
            if (otp2.getText().toString().length() == 1) {
                otp3.requestFocus();
            }
        }
        if (otp3.isFocused()) {
            if (otp3.getText().toString().length() == 1) {
                otp4.requestFocus();
            }
        }
        if (otp4.isFocused()) {
            if (otp4.getText().toString().length() == 1) {
                otp5.requestFocus();
            }
        }
        if (otp5.isFocused()) {
            if (otp5.getText().toString().length() == 1) {
                otp6.requestFocus();
            }
        }
        if (otp6.isFocused()) {
            if (otp6.getText().toString().length() == 1) {

            }
        }

    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {

        switch (v.getId()) {
            case R.id.otp2: {
                if (keyCode == KeyEvent.KEYCODE_DEL) {

                        otp1.setText("");
                        otp1.requestFocus();
                }
                break;
            }
            case R.id.otp3: {
                if (keyCode == KeyEvent.KEYCODE_DEL) {

                        otp2.setText("");
                        otp2.requestFocus();
                }
                break;
            }case R.id.otp4: {
                if (keyCode == KeyEvent.KEYCODE_DEL) {

                        otp3.setText("");
                        otp3.requestFocus();
                }
                break;
            }case R.id.otp5: {
                if (keyCode == KeyEvent.KEYCODE_DEL) {

                        otp4.setText("");
                        otp4.requestFocus();
                }
                break;
            }case R.id.otp6: {
                if (keyCode == KeyEvent.KEYCODE_DEL) {
                    
                        otp5.setText("");
                        otp5.requestFocus();
                }
                break;
            }
        }
        return false;
    }
}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 09 '21 at 10:16
  • Please add some explanation to your code to support, so that it can be understood by others in the future. – Abhishek Dutt Nov 10 '21 at 15:53