Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OnEditorActionListener called twice with same eventTime on SenseUI keyboard

Tags:

android

On just one phone I am testing on (HTC Incredible, Android 2.2, Software 3.21.605.1), I am experiencing the following behavior.

The onEditorAction event handler is being called twice (immediately) when the Enter key on the Sense UI keyboard is pressed.

The KeyEvent.getEventTime() is the same for both times the event is called, leading me to this work-around:

protected void onCreate(Bundle savedInstanceState) {
    [...]

    EditText text = (EditText)findViewById(R.id.txtBox);
    text.setOnEditorActionListener(new OnEditorActionListener() {
        private long lastCalled = -1;

        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if ( event.getEventTime() == lastCalled ) {
                return false;
            } else {
                lastCalled = event.getEventTime();
                handleNextButton(v);
                return true;
            }
        }
    });

    [...]
}

The EditText is defined as:

<EditText 
    android:layout_width="150sp" 
    android:layout_height="wrap_content"
    android:id="@+id/txtBox" 
    android:imeOptions="actionNext"
    android:capitalize="characters" 
    android:singleLine="true"
    android:inputType="textVisiblePassword|textCapCharacters|textNoSuggestions"
    android:autoText="false" 
    android:editable="true" 
    android:maxLength="6"
/>

On all other devices I've tested on, the action button is properly labeled "Next" and the event is only called a single time when that button is pressed.

Is this a bug in Sense UI's keyboard, or am I doing something incorrectly?

Thank you for any assistance.


Updated - thanks to the answers given, I have settled on the following as my checks. This works fine on both of the phones I have available to test (Sense UI and Cyanogenmod CM7)

            if (event != null && event.getAction() != KeyEvent.ACTION_DOWN) {
                return false;
            }

            if ( actionId != EditorInfo.IME_ACTION_NEXT && actionId != EditorInfo.IME_NULL ) {
                return false;
            }
like image 315
mbafford Avatar asked Nov 03 '10 14:11

mbafford


4 Answers

As mitch said, you have to check the event action:

public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
    if (event == null || event.getAction() != KeyEvent.ACTION_DOWN)
        return false;

    // do your stuff

    return true;
}

This snippet works on both the Sense UI and the emulator.

like image 182
Gubbel Avatar answered Oct 04 '22 04:10

Gubbel


(EditText) passwordView = (EditText) findViewById(R.id.password);
passwordView.setImeOptions(EditorInfo.IME_ACTION_DONE);
    passwordView.setOnEditorActionListener(new OnEditorActionListener()
        {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event)
            {
                String input;
                if(actionId == EditorInfo.IME_ACTION_DONE)
                {
                   input= v.getText().toString();
                    Toast toast= Toast.makeText(LogIn.this,input,
                            Toast.LENGTH_LONG);
                    toast.setGravity(Gravity.CENTER, 0, 0);
                    toast.show();
                    return true;
                }
                return false;
            }
        });
like image 32
Dammak Mahdi Avatar answered Oct 04 '22 04:10

Dammak Mahdi


I think if you return true, it means that you are interested in the rest of the events until it reaches IME_ACTION_DONE. So if you return false, it means that you are not interested in the events and that other views will then have the opportunity to handle it. Since you only have 1 view, I suggest you just ignore the actionId check and just return false every time.

etMovieName = (EditText) view.findViewById(R.id.et_movie_name);
etMovieName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView tv, int actionId, KeyEvent event) {
        System.out.println("actionId= "+ actionId);
        performSearch();
        return false;
    }
});

Other situation is if you have overlapping views or a sibling view, then you can use actionId to pass information around. In this situation, returning true will allow you to pass info to the other view. If you really are interested in the event/actionId (for example if you have another sibling view), then you can do this:

etMovieName = (EditText) view.findViewById(R.id.et_movie_name);

etMovieName.setImeOptions(EditorInfo.IME_ACTION_DONE);
etMovieName.setSingleLine();

etMovieName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView tv, int actionId, KeyEvent event) {
        System.out.println("actionId= "+ actionId);
        return performSearch();      }
});

Notice the actionId value has changed to 6 once I added:

etMovieName.setImeOptions(EditorInfo.IME_ACTION_DONE);
etMovieName.setSingleLine();
like image 28
Gene Avatar answered Oct 04 '22 03:10

Gene


In addition to @Gubbel 's answer, I think listen to ACTION_UP instead of ACTION_DOWN is better, because user can actually tap his finger on the enter key (ACTION_DOWN) but then hesitate and hold it to other places.

Normally we want to fire the event only when user release his finger from the enter key.

So the code will be like this:

        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            boolean handled = false;
            if (event.getAction() == KeyEvent.ACTION_DOWN) return true;
            if ((actionId == EditorInfo.IME_ACTION_DONE || actionId == EditorInfo.IME_NULL)
                    && event.getAction() == KeyEvent.ACTION_UP) {
                //Do your stuff here
                handled = true;
            }
            return handled;
        }
like image 23
Sira Lam Avatar answered Oct 04 '22 04:10

Sira Lam