当前位置 : 首页 » 互动问答 » 正文

How do I Detect if Software Keyboard is Visible on Android Device?

分类 : 互动问答 | 发布时间 : 2011-01-20 18:32:09 | 评论 : 22 | 浏览 : 195594 | 喜欢 : 170

Is there a way in Android to detect if the software (a.k.a. "soft") keyboard is visible on screen?

回答(22)

  • 1楼
  • This works for me. Maybe this is always the best way for all versions.

    contentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
    
        Rect r = new Rect();
        contentView.getWindowVisibleDisplayFrame(r);
        int screenHeight = contentView.getRootView().getHeight();
    
        // r.bottom is the position above soft keypad or device button.
        // if keypad is shown, the r.bottom is smaller than that before.
        int keypadHeight = screenHeight - r.bottom;
    
        Log.d(TAG, "keypadHeight = " + keypadHeight);
    
        if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
            // keyboard is opened
        }
        else {
            // keyboard is closed
        }
    }
    });
    
  • 2楼
  • try this:

    InputMethodManager imm = (InputMethodManager) getActivity()
                .getSystemService(Context.INPUT_METHOD_SERVICE);
    
        if (imm.isAcceptingText()) {
            writeToLog("Software Keyboard was shown");
        } else {
            writeToLog("Software Keyboard was not shown");
        }
    
  • 4楼
  • I created a simple class that can be used for this: https://github.com/ravindu1024/android-keyboardlistener. Just copy it in to your project and use as follows:

    KeyboardUtils.addKeyboardToggleListener(this, new KeyboardUtils.SoftKeyboardToggleListener()
    {
        @Override
        public void onToggleSoftKeyboard(boolean isVisible)
        {
            Log.d("keyboard", "keyboard visible: "+isVisible);
        }
    });
    
  • 5楼
  • Very Easy

    1. Put id on your root view

    rootView is just a view pointing to my root view in this case a relative layout:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:id="@+id/addresses_confirm_root_view"
                    android:background="@color/WHITE_CLR">
    

    2. Initialize your root view in your Activity:

    RelativeLayout rootView = (RelativeLayout) findViewById(R.id.addresses_confirm_root_view); 3. Detect if keyboard is opened or closed by using getViewTreeObserver()

        rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight();
    
                    if (heightDiff > 100) { 
                        Log.e("MyActivity", "keyboard opened");
                    } else { 
                        Log.e("MyActivity", "keyboard closed");
                    }
                }
            });
    
  • 7楼
  • You can refer to this answer - https://stackoverflow.com/a/24105062/3629912

    It worked for me everytime.

    adb shell dumpsys window InputMethod | grep "mHasSurface"
    

    It will return true, if software keyboard is visible.

  • 8楼
  • I used this as a basis: http://www.ninthavenue.com.au/how-to-check-if-the-software-keyboard-is-shown-in-android

    /**
    * To capture the result of IMM hide/show soft keyboard
    */
    public class IMMResult extends ResultReceiver {
         public int result = -1;
         public IMMResult() {
             super(null);
    }
    
    @Override 
    public void onReceiveResult(int r, Bundle data) {
        result = r;
    }
    
    // poll result value for up to 500 milliseconds
    public int getResult() {
        try {
            int sleep = 0;
            while (result == -1 && sleep < 500) {
                Thread.sleep(100);
                sleep += 100;
            }
        } catch (InterruptedException e) {
            Log.e("IMMResult", e.getMessage());
        }
        return result;
    }
    }
    

    Then wrote this method:

    public boolean isSoftKeyboardShown(InputMethodManager imm, View v) {
    
        IMMResult result = new IMMResult();
        int res;
    
        imm.showSoftInput(v, 0, result);
    
        // if keyboard doesn't change, handle the keypress
        res = result.getResult();
        if (res == InputMethodManager.RESULT_UNCHANGED_SHOWN ||
                res == InputMethodManager.RESULT_UNCHANGED_HIDDEN) {
    
            return true;
        }
        else
            return false;
    
    }
    

    You may then use this to test all fields (EditText, AutoCompleteTextView, etc) that may have opened a softkeyboard:

        InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
        if(isSoftKeyboardShown(imm, editText1) | isSoftKeyboardShown(imm, autocompletetextview1))
            //close the softkeyboard
            imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
    

    Addmittely not an ideal solution, but it gets the job done.

  • 9楼
  • I did this by setting a GlobalLayoutListener, as follows:

    final View activityRootView = findViewById(R.id.activityRoot);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
            new OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    int heightView = activityRootView.getHeight();
                    int widthView = activityRootView.getWidth();
                    if (1.0 * widthView / heightView > 3) {
                        //Make changes for Keyboard not visible
                    } else {
                        //Make changes for keyboard visible
                    }
                }
            });
    
  • 10楼
  • Try this code it's really working if KeyboardShown is Shown then this function return true value....

    private final String TAG = "TextEditor";
    private TextView mTextEditor;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_editor);
        mTextEditor = (TextView) findViewById(R.id.text_editor);
        mTextEditor.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                isKeyboardShown(mTextEditor.getRootView());
            }
        });
    }
    
    private boolean isKeyboardShown(View rootView) {
        /* 128dp = 32dp * 4, minimum button height 32dp and generic 4 rows soft keyboard */
        final int SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD = 128;
    
        Rect r = new Rect();
        rootView.getWindowVisibleDisplayFrame(r);
        DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
        /* heightDiff = rootView height - status bar height (r.top) - visible frame height (r.bottom - r.top) */
        int heightDiff = rootView.getBottom() - r.bottom;
        /* Threshold size: dp to pixels, multiply with display density */
        boolean isKeyboardShown = heightDiff > SOFT_KEYBOARD_HEIGHT_DP_THRESHOLD * dm.density;
    
        Log.d(TAG, "isKeyboardShown ? " + isKeyboardShown + ", heightDiff:" + heightDiff + ", density:" + dm.density
                + "root view height:" + rootView.getHeight() + ", rect:" + r);
    
        return isKeyboardShown;
    }
    
  • 11楼
  • In my case i had only one EditText to manage in my layout so i came up whit this solution. It works well, basically it is a custom EditText which listens for focus and sends a local broadcast if the focus changes or if the back/done button is pressed. To work you need to place a dummy View in your layout with android:focusable="true" and android:focusableInTouchMode="true" because when you call clearFocus() the focus will be reassigned to the first focusable view. Example of dummy view:

    <View
    android:layout_width="1dp"
    android:layout_height="1dp"
    android:focusable="true"
    android:focusableInTouchMode="true"/>
    

    Additional infos

    The solution which detects the difference in layout changes doesn't work very well because it strongly depends on screen density, since 100px can be a lot in a certain device and nothing in some others you could get false positives. Also different vendors have different keyboards.

  • 12楼
  • This was much less complicated for the requirements I needed. Hope this might help:

    On the MainActivity:

    public void dismissKeyboard(){
        InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(mSearchBox.getWindowToken(), 0);
        mKeyboardStatus = false;
    }
    
    public void showKeyboard(){
        InputMethodManager imm =(InputMethodManager)this.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
        mKeyboardStatus = true;
    }
    
    private boolean isKeyboardActive(){
        return mKeyboardStatus;
    }
    

    The default primative boolean value for mKeyboardStatus will be initialized to false.

    Then check the value as follows, and perform an action if necessary:

     mSearchBox.requestFocus();
        if(!isKeyboardActive()){
            showKeyboard();
        }else{
            dismissKeyboard();
        }
    
  • 13楼
  • Answer of @iWantScala is great but not working for me
    rootView.getRootView().getHeight() always has the same value

    one way is to define two vars

    private int maxRootViewHeight = 0;
    private int currentRootViewHeight = 0;
    

    add global listener

    rootView.getViewTreeObserver()
        .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                currentRootViewHeight = rootView.getHeight();
                if (currentRootViewHeight > maxRootViewHeight) {
                    maxRootViewHeight = currentRootViewHeight;
                }
            }
        });
    

    then check

    if (currentRootViewHeight >= maxRootViewHeight) {
        // Keyboard is hidden
    } else {
        // Keyboard is shown
    }
    

    works fine

  • 14楼
  • I think this is old question but today I come across good library to identify keyboard is open OR not. So I am sharing here.

    I had a similar problem and I found one library for this, and I played with it. It's working like aspected. Here it is Keyboard Library.

  • 15楼
  • So after a long time of playing around with AccessibilityServices, window insets, screen height detection, etc, I think I found a way to do this.

    Disclaimer: it uses a hidden method in Android, meaning it might not be consistent. However, in my testing, it seems to work.

    The method is InputMethodManager#getInputMethodWindowVisibleHeight(), and it's existed since Lollipop (5.0).

    Calling that returns the height, in pixels, of the current keyboard. In theory, a keyboard shouldn't be 0 pixels tall, so I did a simple height check (in Kotlin):

    val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
    if (imm.inputMethodWindowVisibleHeight > 0) {
        //keyboard is shown
    else {
        //keyboard is hidden
    }
    

    I use Android Hidden API to avoid reflection when I call hidden methods (I do that a lot for the apps I develop, which are mostly hacky/tuner apps), but this should be possible with reflection as well:

    val imm by lazy { context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager }
    val windowHeightMethod = InputMethodManager::class.java.getMethod("getInputMethodWindowVisibleHeight")
    val height = windowHeightMethod.invoke(imm) as Int
    //use the height val in your logic
    
  • 16楼
  • I had a similar problem. I needed to react to the Enter button on screen (which hid the keyboard). In this case you can subscribe to the OnEditorAction of the text view the keyboard was opend with - if you have multiple editable boxes, then subscribe to all of them.

    In your Activity you have full control of the keyboard, so at no point will you face the problem whether the keyboard is opened or not, if you listen to all opening and closing events.

  • 17楼
  • There is a direct method to find this out. And, it does not require the layout changes.
    So it works in immersive fullscreen mode, too.
    But, unfortunately, it does not work on all devices. So you have to test it with your device(s).

    The trick is that you try to hide or show the soft keyboard and capture the result of that try.
    If it works correct then the keyboard is not really shown or hidden. We just ask for the state.

    To stay up-to-date, you simply repeat this operation, e.g. every 200 milliseconds, using a Handler.

    The implementation below does just a single check.
    If you do multiple checks, then you should enable all the (_keyboardVisible) tests.

    public interface OnKeyboardShowHide
    {
        void    onShowKeyboard( Object param );
        void    onHideKeyboard( Object param );
    }
    
    private static Handler      _keyboardHandler    = new Handler();
    private boolean             _keyboardVisible    = false;
    private OnKeyboardShowHide  _keyboardCallback;
    private Object              _keyboardCallbackParam;
    
    public void start( OnKeyboardShowHide callback, Object callbackParam )
    {
        _keyboardCallback      = callback;
        _keyboardCallbackParam = callbackParam;
        //
        View view = getCurrentFocus();
        if (view != null)
        {
            InputMethodManager imm = (InputMethodManager) getSystemService( Activity.INPUT_METHOD_SERVICE );
            imm.hideSoftInputFromWindow( view.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY, _keyboardResultReceiver );
            imm.showSoftInput( view, InputMethodManager.SHOW_IMPLICIT, _keyboardResultReceiver );
        }
        else // if (_keyboardVisible)
        {
            _keyboardVisible = false;
            _keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
        }
    }
    
    private ResultReceiver      _keyboardResultReceiver = new ResultReceiver( _keyboardHandler )
    {
        @Override
        protected void onReceiveResult( int resultCode, Bundle resultData )
        {
            switch (resultCode)
            {
                case InputMethodManager.RESULT_SHOWN :
                case InputMethodManager.RESULT_UNCHANGED_SHOWN :
                    // if (!_keyboardVisible)
                    {
                        _keyboardVisible = true;
                        _keyboardCallback.onShowKeyboard( _keyboardCallbackParam );
                    }
                    break;
                case InputMethodManager.RESULT_HIDDEN :
                case InputMethodManager.RESULT_UNCHANGED_HIDDEN :
                    // if (_keyboardVisible)
                    {
                        _keyboardVisible = false;
                        _keyboardCallback.onHideKeyboard( _keyboardCallbackParam );
                    }
                    break;
            }
        }
    };
    
  • 18楼
  • In Android you can detect through ADB shell. I wrote and use this method:

    {
            JSch jsch = new JSch();
            try {
                Session session = jsch.getSession("<userName>", "<IP>", 22);
                session.setPassword("<Password>");
                Properties config = new Properties();
                config.put("StrictHostKeyChecking", "no");
                session.setConfig(config);
                session.connect();
    
                ChannelExec channel = (ChannelExec)session.openChannel("exec");
                BufferedReader in = new BufferedReader(new    
                InputStreamReader(channel.getInputStream()));
                channel.setCommand("C:/Android/android-sdk/platform-tools/adb shell dumpsys window 
                InputMethod | findstr \"mHasSurface\"");
                channel.connect();
    
                String msg = null;
                String msg2 = " mHasSurface=true";
    
                while ((msg = in.readLine()) != null) {
                    Boolean isContain = msg.contains(msg2);
                    log.info(isContain);
                    if (isContain){
                        log.info("Hiding keyboard...");
                        driver.hideKeyboard();
                    }
                    else {
                        log.info("No need to hide keyboard.");
                    }
                }
    
                channel.disconnect();
                session.disconnect();
    
            } catch (JSchException | IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
  • 19楼
  • Here is a workaround to know if softkeyboard is visible.

    1. Check for running services on the system using ActivityManager.getRunningServices(max_count_of_services);
    2. From the returned ActivityManager.RunningServiceInfo instances, check clientCount value for soft keyboard service.
    3. The aforementioned clientCount will be incremented every time, the soft keyboard is shown. For example, if clientCount was initially 1, it would be 2 when the keyboard is shown.
    4. On keyboard dismissal, clientCount is decremented. In this case, it resets to 1.

    Some of the popular keyboards have certain keywords in their classNames:

    1. Google AOSP = IME
    2. Swype = IME
    3. Swiftkey = KeyboardService
    4. Fleksy = keyboard
    5. Adaptxt = IME (KPTAdaptxtIME)
    6. Smart = Keyboard (SmartKeyboard)

    From ActivityManager.RunningServiceInfo, check for the above patterns in ClassNames. Also, ActivityManager.RunningServiceInfo's clientPackage=android, indicating that the keyboard is bound to system.

    The above mentioned information could be combined for a strict way to find out if soft keyboard is visible.

  • 20楼
  • I did this as follows, but its relevet only if your goal is to close / open the keyboad.

    close example: (checking if keyboard already closed, if not - closing)

    imm.showSoftInput(etSearch, InputMethodManager.HIDE_IMPLICIT_ONLY, new ResultReceiver(null) {
                        @Override
                        protected void onReceiveResult(int resultCode, Bundle resultData) {
                            super.onReceiveResult(resultCode, resultData);
                            if (resultCode != InputMethodManager.RESULT_UNCHANGED_HIDDEN)
                                imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
                        }
                    });
    
  • 21楼
  • final View activityRootView = findViewById(R.id.rootlayout);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
    
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
    
                int screenHeight = activityRootView.getRootView().getHeight();
                Log.e("screenHeight", String.valueOf(screenHeight));
                int heightDiff = screenHeight - (r.bottom - r.top);
                Log.e("heightDiff", String.valueOf(heightDiff));
                boolean visible = heightDiff > screenHeight / 3;
                Log.e("visible", String.valueOf(visible));
                if (visible) {
                    Toast.makeText(LabRegister.this, "I am here 1", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(LabRegister.this, "I am here 2", Toast.LENGTH_SHORT).show();
                }
            }
    });
    
  • 22楼
  • a may be using :

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    
        Log.d(
        getClass().getSimpleName(), 
        String.format("conf: %s", newConfig));
    
        if (newConfig.hardKeyboardHidden != hardKeyboardHidden) {
            onHardwareKeyboardChange(newConfig.hardKeyboardHidden);
    
            hardKeyboardHidden = newConfig.hardKeyboardHidden;
        }
    
        if (newConfig.keyboardHidden != keyboardHidden) {
            onKeyboardChange(newConfig.keyboardHidden);
    
            keyboardHidden = newConfig.hardKeyboardHidden;
        }
    
    }
    
    public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
    public static final int KEYBOARDHIDDEN_NO = 1;
    public static final int KEYBOARDHIDDEN_YES = 2;
    public static final int KEYBOARDHIDDEN_SOFT = 3;
    
    //todo
    private void onKeyboardChange(int keyboardHidden) {
    
    }
    
    //todo
    private void onHardwareKeyboardChange(int hardKeyboardHidden) {
    
    }
    

相关阅读:

Disabling of EditText in android

How to print all key and values from HashMap in Android?

How To Set Text In An EditText

With android studio no jvm found, JAVA_HOME has been set

ADB Android Device Unauthorized

How to insert a SQLite record with a datetime set to 'now' in Android application?

XXHDPI and XXXHDPI dimensions in dp for images and icons in android

java.lang.OutOfMemoryError: bitmap size exceeds VM budget - Android

ADB not recognising Nexus 4 under Windows 7

InflateException: Binary XML file line #1: Error inflating class <unknown> caused by OutOfMemoryError