Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is android KeyboardView.OnKeyboardActionListener.onRelease() called after each key repetition?

According to the KeyboardView.OnKeyboardActionListener.onRelease() SDK docs, "For keys that repeat, this is only called once". However, if I set isRepeatable to true for the 'a' key with the Android Softkeyboard example, and log onPress(), onKey() and onRelease() method calls, I get repetition as expected but I observe the following log for a single press/repeat/release sequence:

I/SoftKeyboard(31467): onPress: 97
I/SoftKeyboard(31467): onKey: 97
I/SoftKeyboard(31467): onRelease: 97
I/SoftKeyboard(31467): onKey: 97
I/SoftKeyboard(31467): onRelease: 97
I/SoftKeyboard(31467): onKey: 97
I/SoftKeyboard(31467): onRelease: 97
I/SoftKeyboard(31467): onKey: 97
I/SoftKeyboard(31467): onRelease: 97
I/SoftKeyboard(31467): onKey: 97
I/SoftKeyboard(31467): onRelease: 97

How can I determine exactly when the touch device has been released? Thanks, D.

EDIT (Edit by Paul Boddington 30/07/2015)

Although I am not the OP, I wanted to include a complete example showing the problem.

MyActivity:

public class MyActivity extends Activity {

    private static final String TAG = "MyActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        KeyboardView keyboardView = (KeyboardView) findViewById(R.id.keyboard_view);
        keyboardView.setKeyboard(new Keyboard(this, R.xml.keyboard));
        keyboardView.setOnKeyboardActionListener(new KeyboardView.OnKeyboardActionListener() {

            @Override
            public void onPress(int i) {
                Log.i(TAG, "onPress: " + i);
            }

            @Override
            public void onKey(int i, int[] ints) {
                Log.i(TAG, "onKey: " + i);
            }

            @Override
            public void onRelease(int i) {
                Log.i(TAG, "onRelease: " + i);
            }

            @Override public void onText(CharSequence charSequence) {}
            @Override public void swipeLeft() {}
            @Override public void swipeRight() {}
            @Override public void swipeDown() {}
            @Override public void swipeUp() {}
        });
    }
}

keyboard.xml:

<?xml version="1.0" encoding="utf-8"?>
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android">
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    >
    <Row
        android:keyWidth="25%p"
        android:keyHeight="60dp">
        <Key android:codes="0" android:keyLabel="0" android:isRepeatable="true"/>
        <Key android:codes="1" android:keyLabel="1" />
        <Key android:codes="2" android:keyLabel="2" />
        <Key android:codes="3" android:keyLabel="3" />
    </Row>
</Keyboard>

activity_my.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.inputmethodservice.KeyboardView
        android:id="@+id/keyboard_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:focusableInTouchMode="true"
        />
</LinearLayout>
like image 504
davhoo Avatar asked Mar 15 '15 10:03

davhoo


1 Answers

I couldn't figure out how to fix KeyboardView to properly emit the onRelease() for repeatable keys. So instead I came up with a workaround that recognizes the release by looking at the MotionEvent.ACTION_UP. You do this for repeatable keys only and ignore the onRelease() events for these keys as well.

private List<Integer> mRepeatableKeys = new ArrayList<Integer>();
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final KeyboardView keyboardView = (KeyboardView) findViewById(R.id.keyboard_view);
    keyboardView.post(new Runnable() {
        @Override
        public void run() {
            for( Keyboard.Key key : keyboardView.getKeyboard().getKeys() )
            {
                if(key.repeatable) mRepeatableKeys.add(key.codes[0]);
            }
        }
    });
    keyboardView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_UP) {
                if(mTrackingRepeatableKey != -1)
                {
                    Log.i(TAG, "Repeatable key released: " + mTrackingRepeatableKey);
                    mTrackingRepeatableKey = -1;
                }
            }
            return false;
        }
    });

    keyboardView.setKeyboard(new Keyboard(this, R.xml.keyboard));
    keyboardView.setOnKeyboardActionListener(mKeyboardActionListener);
}

private int mTrackingRepeatableKey = -1;
KeyboardView.OnKeyboardActionListener mKeyboardActionListener = new KeyboardView.OnKeyboardActionListener() {

    @Override
    public void onPress(int i) {
        Log.i(TAG, "onPress: " + i);
        if( mRepeatableKeys.contains(i))
        {
            mTrackingRepeatableKey = i;
        }
    }

    @Override
    public void onKey(int i, int[] ints) {
        if( mRepeatableKeys.contains(i))
        {
            Log.i(TAG, "onKey Repeat: " + i);
            return;
        }
        Log.i(TAG, "onKey: " + i);
    }

    @Override
    public void onRelease(int i) {
        // Ignore release for repeatable keys
        if( mRepeatableKeys.contains(i)) return;
        Log.i(TAG, "onRelease: " + i);
    }

    @Override
    public void onText(CharSequence text) {
    }

    @Override
    public void swipeLeft() {
    }

    @Override
    public void swipeRight() {
    }

    @Override
    public void swipeDown() {
    }

    @Override
    public void swipeUp() {
    }
};

Tested on Android 5.1. Logs now read:

I/MainActivity﹕ onPress: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ onKey Repeat: 1
I/MainActivity﹕ Repeatable key released: 1

If you want you could extend KeyboardView and contain all of this ugly logic inside of it.

like image 109
Trevor Carothers Avatar answered Oct 29 '22 16:10

Trevor Carothers