安卓:检测软键盘是否打开

35

当软键盘打开时,我希望滚动视图自动向下滚动到底部。

为了实现这个目的,我们可以使用以下代码:fullScroll(View.FOCUS_DOWN);

但是,如何在软键盘打开事件触发后执行该命令呢?


我检测键盘的方法非常简单,只需要使用一个简单的if条件语句即可。 - Rohith S
10个回答

47

这是我的解决方案:

1/ 一个简单的界面

public interface KeyboardVisibilityListener {
    void onKeyboardVisibilityChanged(boolean keyboardVisible);
}

2/ 一个实用方法(将其放在您想要的位置,例如在名为KeyboardUtil的类中)

public static void setKeyboardVisibilityListener(Activity activity, KeyboardVisibilityListener keyboardVisibilityListener) {
    View contentView = activity.findViewById(android.R.id.content);
    contentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        private int mPreviousHeight;

        @Override
        public void onGlobalLayout() {
            int newHeight = contentView.getHeight();
            if (mPreviousHeight != 0) {
                if (mPreviousHeight > newHeight) {
                    // Height decreased: keyboard was shown
                    keyboardVisibilityListener.onKeyboardVisibilityChanged(true);
                } else if (mPreviousHeight < newHeight) {
                    // Height increased: keyboard was hidden
                    keyboardVisibilityListener.onKeyboardVisibilityChanged(false);
                } else {
                    // No change
                }
            }
            mPreviousHeight = newHeight;
        }
    });
}

3/ 可以在 Activity 中这样使用(onCreate 是一个很好的位置):

KeyboardUtil.setKeyboardVisibilityListener(this, mKeyboardVisibilityListener);

2
我喜欢你的建议。只是需要指出这只能与adjustResize一起使用,对吧?你还能在这个提案中看到其他的要求吗? - Guilherme Oliveira
这是一种相当不错的方法,但我建议您引入一种方法来从ViewTreeObserver中删除先前添加的“Listener”(例如,在Fragment中使用此方法并且正在从Activity中分离)。 - Bartek Lipinski
2
虽然android:inputType="textPassword"失败了,但还是有一个不错的方法! - User3
@User3,我真的不明白为什么这会因为输入类型而失败。 - BoD
这应该被标记为正确答案。适用于所有Android版本,包括6.0.1。 - Rabi
显示剩余3条评论

29
根据这篇帖子这篇帖子上在android-developers的讨论,似乎无法实现你想要的功能。你可能需要重新审视你所做的用例。也许softInputMode标志之一适合你。

57
2012年12月。是的,没错,Android仍然没有很好的方法来通知软键盘是否可见。太惊人了! - sebrock
47
@sebrock 我也是在2014年。 - Rod_Algonquin
28
好的,我会尽力进行翻译:是的,2016年已经到了 :( - Arlind Hajredinaj
25
快到2017年了,我们有了新的模拟器、新的IDE,但仍然没有解决检查键盘状态的问题。 - Val
24
2019已经到来。不久的将来,第一个人将在火星上迈出第一步。几周前,世界看到了带有可折叠触摸屏的设备。机器人无处不在。但是我们仍然不能轻松地检测Android键盘的可见性。赞! - bukka.wh
显示剩余12条评论

7

观察日期,可能你已经有了解决方案,否则:

这里是我对另一个相关问题的回答:有办法判断软键盘是否显示?

但为了避免死链接,我在此处复制完整回答:

请检查您的Activity的配置更改

这是您的AndroidManifest.xml

以及您的Activityhttp://developer.android.com/reference/android/app/Activity.html#onConfigurationChanged(android.content.res.Configuration)

您需要@覆盖您的Activity的公共方法onConfigurationChanged(android.content.res.Configuration)来处理例如以下值:


hardKeyboardHidden,
keyboard,
keyboardHidden

查看所有可能的值,请访问http://developer.android.com/reference/android/content/res/Configuration.html

你将会看到类似于这样的内容:

HARDKEYBOARDHIDDEN_NO   
HARDKEYBOARDHIDDEN_UNDEFINED    
HARDKEYBOARDHIDDEN_YES  
KEYBOARDHIDDEN_NO   
KEYBOARDHIDDEN_UNDEFINED    
KEYBOARDHIDDEN_YES  
KEYBOARD_12KEY  
KEYBOARD_NOKEYS 
KEYBOARD_QWERTY 
KEYBOARD_UNDEFINED

同时,您还可以在那里阅读类似于以下内容的信息:

public int  hardKeyboardHidden  A flag indicating whether the hard keyboard has been      hidden.
public int  keyboard    The kind of keyboard attached to the device.
public int  keyboardHidden  A flag indicating whether any keyboard is available.

更新:

这是一个具体的示例,以说明我的意思:

http://developer.android.com/guide/topics/resources/runtime-changes.html

    
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
    // Checks whether a hardware keyboard is available
    if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
    } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
    }
}

我希望这能帮到你


10
这是硬键盘的示例,这对软键盘不起作用。 - longhairedsi
1
对,没有办法知道。这真的让我感到惊讶。 - sebrock
@longhairedsi 什么是 Android 中的软键盘和硬键盘? - SaravanaRaja

2
我能够解决这个问题的唯一方法是通过设置我的Activity的android:windowSoftInputMode="adjustResize",然后在布局中嵌入一个自定义的“检测器视图”来处理容器大小的更改,并将其作为自定义事件(通过Listener)传播给软键盘的开/关状态。
以下帖子描述了一种实现方法:EditText does not trigger changes when back is pressed

1
这对我有效。
parent.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {

            boolean someHasFocus = false;

            if(host.hasFocus())
                someHasFocus = true;
            if(folder.hasFocus())
                someHasFocus = true;
            if(user.hasFocus())
                someHasFocus = true;
            if(pass.hasFocus())
                someHasFocus = true;

            if(someHasFocus){
                if(bottom>oldBottom){
                    // Keyboard Close
                    viewToHide.setVisibility(View.VISIBLE);

                }else if(bottom<oldBottom){
                   // Keyboard Open
                    viewToHide.setVisibility(View.GONE);
                }

            }else{
                // show
                viewToHide.setVisibility(View.VISIBLE);
            }
        }
    });

其中parent是主要布局,viewToHide是在键盘显示或隐藏时显示或隐藏的视图,host、folder、user和pass是我的表单中的EditText。

而这是在清单文件中的内容。

android:windowSoftInputMode="stateHidden|adjustResize"

希望这有所帮助。

0

对于这个,我以前做的是一样的:

  import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;

public class SoftKeyboard implements View.OnFocusChangeListener
{
    private static final int CLEAR_FOCUS = 0;

    private ViewGroup layout;
    private int layoutBottom;
    private InputMethodManager im;
    private int[] coords;
    private boolean isKeyboardShow;
    private SoftKeyboardChangesThread softKeyboardThread;
    private List<EditText> editTextList;

    private View tempView; // reference to a focused EditText

    public SoftKeyboard(ViewGroup layout, InputMethodManager im)
    {
        this.layout = layout;
        keyboardHideByDefault();
        initEditTexts(layout);
        this.im = im;
        this.coords = new int[2];
        this.isKeyboardShow = false;
        this.softKeyboardThread = new SoftKeyboardChangesThread();
        this.softKeyboardThread.start();
    }


    public void openSoftKeyboard()
    {
        if(!isKeyboardShow)
        {
            layoutBottom = getLayoutCoordinates();
            im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT);
            softKeyboardThread.keyboardOpened();
            isKeyboardShow = true;
        }
    }

    public void closeSoftKeyboard()
    {
        if(isKeyboardShow)
        {
            im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
            isKeyboardShow = false;
        }
    }

    public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback)
    {
        softKeyboardThread.setCallback(mCallback);
    }

    public void unRegisterSoftKeyboardCallback()
    {
        softKeyboardThread.stopThread();
    }

    public interface SoftKeyboardChanged
    {
        public void onSoftKeyboardHide();
        public void onSoftKeyboardShow();
    }

    private int getLayoutCoordinates()
    {
        layout.getLocationOnScreen(coords);
        return coords[1] + layout.getHeight();
    }

    private void keyboardHideByDefault()
    {
        layout.setFocusable(true);
        layout.setFocusableInTouchMode(true);
    }

    /*
     * InitEditTexts now handles EditTexts in nested views
     * Thanks to Francesco Verheye (verheye.francesco@gmail.com)
     */
    private void initEditTexts(ViewGroup viewgroup)
    {
        if(editTextList == null)
            editTextList = new ArrayList<EditText>();

        int childCount = viewgroup.getChildCount();
        for(int i=0; i<= childCount-1;i++)
        {
            View v = viewgroup.getChildAt(i);

            if(v instanceof ViewGroup)
            {
                initEditTexts((ViewGroup) v);
            }

            if(v instanceof EditText)
            {
                EditText editText = (EditText) v;
                editText.setOnFocusChangeListener(this);
                editText.setCursorVisible(true);
                editTextList.add(editText);
            }
        }
    }

    /*
     * OnFocusChange does update tempView correctly now when keyboard is still shown
     * Thanks to Israel Dominguez (dominguez.israel@gmail.com)
     */
    @Override
    public void onFocusChange(View v, boolean hasFocus)
    {
        if(hasFocus)
        {
            tempView = v;
            if(!isKeyboardShow)
            {
                layoutBottom = getLayoutCoordinates();
                softKeyboardThread.keyboardOpened();
                isKeyboardShow = true;
            }
        }
    }

    // This handler will clear focus of selected EditText
    private final Handler mHandler = new Handler()
    {
        @Override
        public void handleMessage(Message m)
        {
            switch(m.what)
            {
                case CLEAR_FOCUS:
                    if(tempView != null)
                    {
                        tempView.clearFocus();
                        tempView = null;
                    }
                    break;
            }
        }
    };

    private class SoftKeyboardChangesThread extends Thread
    {
        private AtomicBoolean started;
        private SoftKeyboardChanged mCallback;

        public SoftKeyboardChangesThread()
        {
            started = new AtomicBoolean(true);
        }

        public void setCallback(SoftKeyboardChanged mCallback)
        {
            this.mCallback = mCallback;
        }

        @Override
        public void run()
        {
            while(started.get())
            {
                // Wait until keyboard is requested to open
                synchronized(this)
                {
                    try
                    {
                        wait();
                    } catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                }

                int currentBottomLocation = getLayoutCoordinates();

                // There is some lag between open soft-keyboard function and when it really appears.
                while(currentBottomLocation == layoutBottom && started.get())
                {
                    currentBottomLocation = getLayoutCoordinates();
                }

                if(started.get())
                    mCallback.onSoftKeyboardShow();

                // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
                // and at some moment equals layoutBottom.
                // That broke the previous logic, so I added this new loop to handle this.
                while(currentBottomLocation >= layoutBottom && started.get())
                {
                    currentBottomLocation = getLayoutCoordinates();
                }

                // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone
                while(currentBottomLocation != layoutBottom && started.get())
                {
                    synchronized(this)
                    {
                        try
                        {
                            wait(500);
                        } catch (InterruptedException e)
                        {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                    currentBottomLocation = getLayoutCoordinates();
                }

                if(started.get())
                    mCallback.onSoftKeyboardHide();

                // if keyboard has been opened clicking and EditText.
                if(isKeyboardShow && started.get())
                    isKeyboardShow = false;

                // if an EditText is focused, remove its focus (on UI thread)
                if(started.get())
                    mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget();
            }
        }

        public void keyboardOpened()
        {
            synchronized(this)
            {
                notify();
            }
        }

        public void stopThread()
        {
            synchronized(this)
            {
                started.set(false);
                notify();
            }
        }

    }
}

在你的Activityfragment中,在onCreate()方法中调用此方法。

  private void hideAndShowKeyBOrd() {
        InputMethodManager im = (InputMethodManager) getActivity().getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
        SoftKeyboard softKeyboard;
        softKeyboard = new SoftKeyboard(mainLayout, im);
        softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged() {

            @Override
            public void onSoftKeyboardHide() {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                    }
                });
            }

            @Override
            public void onSoftKeyboardShow() {
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        if (viewV.getVisibility() == View.VISIBLE) {
                            viewV.setVisibility(View.GONE);
                        }
                    }
                });

            }
        });
    }

享受你的代码:)


这会破坏支持设计库的 android.support.design.widget.TextInputLayout - User3

0

在运行组合测试时,这对我起作用了

 fun isKeyboardOpenedShellCheck(): Boolean {
        val checkKeyboardCmd = "dumpsys input_method | grep mInputShown"

        try {
            return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
                .executeShellCommand(checkKeyboardCmd).contains("mInputShown=true")
        } catch (e: IOException) {
            throw RuntimeException("Keyboard check failed", e)
        }
    }

0

这是可行的解决方案!

fun Activity.isKeyboardClosed(): Boolean {
    return !this.isKeyboardOpen()
}

private fun Activity.isKeyboardOpen(): Boolean {
    val visibleBounds = Rect()
    this.getRootView().getWindowVisibleDisplayFrame(visibleBounds)
    val heightDiff = getRootView().height - visibleBounds.height()
    val marginOfError = this.convertDpToPx(50F).roundToInt()
    return heightDiff > marginOfError
}


private fun Activity.getRootView(): View {
    return findViewById<View>(android.R.id.content)
}

private fun Context.convertDpToPx(dp: Float): Float {
    return TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP,
        dp,
        this.resources.displayMetrics
    )
}

0

这是我的解决方案。它不需要android:windowSoftInputMode="adjustResize"

public abstract class KeyboardActivity extends Activity {
    public static final int MIN_KEYBOARD_SIZE = 100;
    private Window mRootWindow;
    private View mRootView;
    private int mKeyboardHeight = -1;
    private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {

    public int height;
    public void onGlobalLayout() {
            Rect r = new Rect();
        View view = mRootWindow.getDecorView();
        view.getWindowVisibleDisplayFrame(r);
        if (height != r.height()) {
            int diff = height - r.height();
            height = r.height();
            if (Math.abs(diff) > MIN_KEYBOARD_SIZE) {
                int diff = height - r.height();
                if (height != 0 && Math.abs(diff) > MIN_KEYBOARD_SIZE) {
                    mKeyboardHeight = Math.abs(diff);
                    if (diff > 0) {
                        onKeyboardOpen();
                    } else {
                        onKeyboardClosed();
                    }
                }
                height = r.height();
            }
        }
    };

    protected abstract void onKeyboardClosed();

    protected abstract void onKeyboardOpen();

    /**
     * Should return keyboard height, if keyboard was shown at least once;
     * @return keyboard height or -1
     */
    protected int getKeyboardHeight() {
        return mKeyboardHeight;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mRootWindow = getWindow();
        mRootView = mRootWindow.getDecorView().findViewById(android.R.id.content);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mRootView.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
    }
}

接着我只需从这个活动中扩展我的活动并覆盖onKeyboardClosed/onKeyboardOpen方法。


0

如果我删除以下行,@BoD's answer 就可以正常工作。

if (mPreviousHeight != 0) {
     /* other code is same, because
        mPreviousHeight is 0 when it comes first */
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接