20

I cannot figure how to implement onKeyPreIme(int keyCode, KeyEvent event) in a Fragment.

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && 
        event.getAction() == KeyEvent.ACTION_UP) {
            // do your stuff
            return false;
    }
    return super.dispatchKeyEvent(event);
}

I tried a lot but nothing works. Also, I could not find anything on Google or Stack Overflow. I would like to perform an action when the back key is pressed and the softkeyboard is up. Setting an onKeyListener on my EditTexts did not work, since KeyEvent.KEYCODE_BACK is not called when the soft keyboard is up. I appreciate any help and source code.

0xCursor
  • 2,242
  • 4
  • 15
  • 33
Max
  • 572
  • 2
  • 6
  • 23
  • Thanks for your code, it was helpful to understand that returning ``super.dispatchKeyEvent(event);`` the activity's back button will work! – Gibberish Oct 28 '14 at 14:01

4 Answers4

23

I was able to implement onKeyPreIme by sub-classing my EditText views that were related to the keyboard input. The goal is to make a custom lock screen that the user must enter a pass code or leave the application. When the user taps the "keyboard down" button the keyboard does not disappear.

Make sure to create a separate .java file for the subclassed EditText. Additionally, be sure to use the constructor in the code below (must pass AttrubuteSet).

I realize that my implementation of onKeyPreIme may not match yours, however it does demonstrate how to intercept the keyboard events before the InputMethodManager does it's thing.

I hope this helps.

Screenshot UserLockActivity enter image description here

EditText Subclass

public class LockEditText extends EditText {
    /* Must use this constructor in order for the layout files to instantiate the class properly */
    public LockEditText(Context context, AttributeSet attrs) 
    {
        super(context, attrs);
        // TODO Auto-generated constructor stub
    }

    @Override
    public boolean onKeyPreIme (int keyCode, KeyEvent event)
    {
        // Return true if I handle the event:
            // In my case i want the keyboard to not be dismissible so i simply return true
            // Other people might want to handle the event differently
        System.out.println("onKeyPreIme " +event);
        return true;
    }

}

UserLockActivity.java

public class UserLockActivity extends Activity 
{
    private LockEditText editText1;
    private LockEditText editText2;
    private LockEditText editText3;
    private LockEditText editText4;
    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_lock);
        editText1 = (LockEditText) findViewById(R.id.lock_text_1);
        editText2 = (LockEditText) findViewById(R.id.lock_text_2);
        editText3 = (LockEditText) findViewById(R.id.lock_text_3);
        editText4 = (LockEditText) findViewById(R.id.lock_text_4);
        setupTextChangedListener(editText1);
        setupTextChangedListener(editText2);
        setupTextChangedListener(editText3);
        setupTextChangedListener(editText4);
        // A method to bring out the keyboard when the view appears
        setFocusOnEditText(editText1);
    }

    public void setFocusOnEditText(LockEditText editText)
    {
        editText.clearFocus();
        editText.requestFocus();
        InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);

        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,0);
    }
    public void setupTextChangedListener(LockEditText editText)
    {
        editText.addTextChangedListener(new TextWatcher() 
        {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) 
            {

            }

            @Override
            public void afterTextChanged(Editable arg0) 
            {
                // TODO Auto-generated method stub
            }

            @Override
            public void beforeTextChanged(CharSequence arg0, int arg1,int arg2, int arg3) 
            {
                // TODO Auto-generated method stub
            }
        });
    }
}

activity_user_lock.xml Layout file

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".UserLockActivity" >

    <TextView
        android:id="@+id/main_lock_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentTop="true"
        android:paddingTop="60dp"
        android:paddingBottom="20dp"
        android:text="@string/enter_passcode"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <LinearLayout
        android:id="@+id/lock_input_layout"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_below="@+id/main_lock_text"
        android:orientation="horizontal" >

        <com.yourpackage.yourappname.LockEditText
            android:id="@+id/lock_text_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="numberPassword"
            android:textSize="30sp"
            android:gravity="center_horizontal"
            android:textStyle="bold" >
        </com.yourpackage.yourappname.LockEditText>
        <com.yourpackage.yourappname.LockEditText
            android:id="@+id/lock_text_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="numberPassword"
            android:textSize="30sp"
            android:gravity="center_horizontal"
            android:textStyle="bold" >
        </com.yourpackage.yourappname.LockEditText>
        <com.yourpackage.yourappname.LockEditText
            android:id="@+id/lock_text_3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="numberPassword"
            android:textSize="30sp" 
            android:gravity="center_horizontal"
            android:textStyle="bold">
        </com.yourpackage.yourappname.LockEditText>
        <com.yourpackage.yourappname.LockEditText
            android:id="@+id/lock_text_4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="numberPassword"
            android:textSize="30sp"
            android:gravity="center_horizontal"
            android:textStyle="bold" >
        </com.yourpackage.yourappname.LockEditText>
    </LinearLayout>

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/lock_input_layout"
        android:layout_centerHorizontal="true"
        android:text="text" />

</RelativeLayout>
i2097i
  • 764
  • 7
  • 15
  • It does not really match what I intended it for, but your idea is creative so I will accept your answer – Max May 06 '13 at 17:20
  • 1
    Superb @i2097i. Thanks buddy :) – Perry Jul 06 '15 at 13:30
  • @i2097i I have one question. Is there any method in android to **remove focus** from `EditText` programmatically? – Perry Jul 06 '15 at 13:54
  • @Perry http://stackoverflow.com/questions/5056734/android-force-edittext-to-remove-focus – i2097i Jul 06 '15 at 13:58
  • I want to do this in fragment using the inflater for this "LockEditText". but I am getting a exception in this case. Can you please explain how to use this LockEditText as a subclass of Edittext and add it with a inflater for Fragment – anand krish Jul 31 '15 at 12:03
  • @AnandKrish I would look into a method override for inflating the subclass of EditText. You may need to implement a specific method to initialize it with the desired behavior. – i2097i Aug 03 '15 at 20:53
  • Clean and nice solution. Exactly what I was looking for. – JusticeJuice Oct 28 '15 at 17:57
  • 1
    This solution worked well, but it is for an activity. To add to a Fragment, you have to get the activity when instantiating the InputMethodManager. So for this line: InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); Add a getActivity. before the getSystemService. So it will look like this: InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); – Bourne Aug 03 '16 at 13:15
23

This is my solution and it works really well for me, but everyones needs are different.

First i subclassed EditText and hooked up a listener (Google should make this the default)

public class ListenerEditText extends EditText {

    private KeyImeChange keyImeChangeListener;

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

    public void setKeyImeChangeListener(KeyImeChange listener){
        keyImeChangeListener = listener;
    }

    public interface KeyImeChange {
        public void onKeyIme(int keyCode, KeyEvent event);
    }

    @Override
    public boolean onKeyPreIme (int keyCode, KeyEvent event){
        if(keyImeChangeListener != null){
            keyImeChangeListener.onKeyIme(keyCode, event);
        }        
        return false;
    }
}

Then you can attach a listener from anywhere like so:

myListenerEditText.setKeyImeChangeListener(new KeyImeChange() {

    @Override
    public void onKeyIme(int keyCode, KeyEvent event) {
        // All keypresses with the keyboard open will come through here!
        // You could also bubble up the true/false if you wanted 
        // to disable propagation.
    }
});
Deminetix
  • 2,866
  • 26
  • 21
  • 1
    This looks like the best answer, since it actually enables overriding of onKeyPreIme. I hate to ask, but I'm having problems getting it to work. I extended AutoCompleteTextView and added a return statement in onKeyIme(...) within my activity (your second code block). Other than that, I've copied your code exactly, and set breakpoints on the "return" statement and each of the methods in the new class. setKeyImeChangeListener is called, and initializes keyImeChangeListener, however, the onKeyIme() never gets called when a key is pressed. Any ideas where to start troubleshooting? – John Ward Jan 04 '17 at 23:27
  • Ok, I think I get it. These keyboard events are for physical keyboards, not the soft keyboard. Can anyone confirm? Thanks. – John Ward Jan 05 '17 at 02:32
  • 1
    By the way, I'm still a student of all things Android, so thanks for this bit of code. I was able to use it to override the onFilterComplete method. Seems kinda silly that Android doesn't provide a listener for these events. – John Ward Jan 05 '17 at 15:15
  • 1
    I'm not seeing my onKeyIme method called either. Does this work for soft keyboards? – B T Nov 08 '18 at 00:19
1

I was not able to figure out how to implement the onKeyPreIME, but I was able to perform an action after the keyboard disappeared with the following code:

You man need to change comparison heightDiff > 200. This comparison worked for me because I had a scrollview.

fragmentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            if(getView() != null){
                int heightDiff = getView().getRootView().getHeight() - getView().getHeight();
                if (heightDiff < 200) { 
                    rlupdate.setVisibility(RelativeLayout.VISIBLE);
                }
                else {
                    rlupdate.setVisibility(RelativeLayout.GONE);
                }   
            }
        }
    });
Max
  • 572
  • 2
  • 6
  • 23
1

this is a more full code of answer by Deminetix.

i have used the answer by Deminetix to filter handheld barcode reader on android and have the result.

to make it usable on a screen only with buttons i have added a EditText with android:textColor="#FF000000" android:background="#00FFFFFF" android:enabled="false" disabled EditText still gets keyboard events.

optionally i could hide the software keyboard using the following but after disabling the EditText it was not required.

//InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
//imm.showSoftInput(textPatientId, InputMethodManager.HIDE_IMPLICIT_ONLY);

MainActivity.java:

package com.doodkin.keyboardtest;

import com.doodkin.keyboardtest.ListenerEditText.KeyImeChange;

import android.os.Bundle;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.KeyListener;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnKeyListener;
import android.widget.EditText;

public class MainActivity extends Activity {
    private static final String TAG = "keyboard test";
    //private EditText editText1;

    ListenerEditText editText1=null;
    public String barcodebuffer="";

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

        editText1 = (ListenerEditText) findViewById(R.id.editText1);

        editText1.setKeyImeChangeListener(new KeyImeChange() {

            @Override
            public boolean onKeyIme(int keyCode, KeyEvent event) {
                String deviceName=event.getDevice().getName();
                int keyboardType=event.getDevice().getKeyboardType();
                int indexof=deviceName.indexOf("USB");
                if(indexof!=-1 && keyboardType==InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC)
                {
                    if(event.getKeyCode()==KeyEvent.KEYCODE_ENTER)
                     {
                         if(barcodebuffer!="")
                         {
                             Log.d(TAG, "filterBarcodeKeys Chars Flush: " + barcodebuffer );
                             barcodebuffer="";
                         }
                     }
                     else
                     {

                         barcodebuffer+=Character.toString((char)event.getUnicodeChar());
                         //Log.d(TAG, "filterBarcodeKeys Char: " + Character.toString((char)event.getUnicodeChar()) );
                     }
                     return true;
                }
                return false;
            }
        });


    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }


}

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <com.doodkin.keyboardtest.ListenerEditText
        android:id="@+id/editText1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textView1"
        android:layout_below="@+id/textView1"
        android:ems="10" >

        <requestFocus />
    </com.doodkin.keyboardtest.ListenerEditText>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/editText1"
        android:layout_below="@+id/editText1"
        android:layout_marginTop="69dp"
        android:text="Button" />

</RelativeLayout>

ListenerEditText.java:

package com.doodkin.keyboardtest;

import android.content.Context;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.widget.EditText;

/*
 * example: 

 myListenerEditText.setKeyImeChangeListener(new KeyImeChange() {

    @Override
    public boolean onKeyIme(int keyCode, KeyEvent event) {
        // All keypresses with the keyboard open will come through here!
        // You could also bubble up the true/false if you wanted 
        // to disable propagation.
    }
});

 */

public class ListenerEditText extends EditText {

    private KeyImeChange keyImeChangeListener;

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

    public void setKeyImeChangeListener(KeyImeChange listener){
        keyImeChangeListener = listener;
    }

    public interface KeyImeChange {
        public boolean onKeyIme(int keyCode, KeyEvent event);
    }

    @Override
    public boolean onKeyPreIme (int keyCode, KeyEvent event){
        if(keyImeChangeListener != null){
            return keyImeChangeListener.onKeyIme(keyCode, event);
        }        
        return false;
    }
}
Shimon Doodkin
  • 4,310
  • 34
  • 37