Android中的活动中的软键盘打开和关闭监听器

Soft keyboard open and close listener in an activity in Android

我有一个Activity,其中有5 EditText s。 当用户单击第一个EditText时,将打开软键盘以在其中输入一些值。 当软键盘打开时,以及当用户点击第一个EditText时以及当软键盘从后退按钮上的相同EditText关闭时,我想将其他View的可见性设置为Gone 按。 然后我想将其他View的可见性设置为可见。

是否有任何监听器或回调或任何黑客攻击时,从Android上的第一个EditText点击打开软键盘?


仅当您的活动的android:windowSoftInputMode在清单中设置为adjustResize时才有效。您可以使用布局侦听器来查看键盘是否调整了活动的根布局。

我为我的活动使用类似下面的基类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class BaseActivity extends Activity {
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();

            LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this);

            if(heightDiff <= contentViewTop){
                onHideKeyboard();

                Intent intent = new Intent("KeyboardWillHide");
                broadcastManager.sendBroadcast(intent);
            } else {
                int keyboardHeight = heightDiff - contentViewTop;
                onShowKeyboard(keyboardHeight);

                Intent intent = new Intent("KeyboardWillShow");
                intent.putExtra("KeyboardHeight", keyboardHeight);
                broadcastManager.sendBroadcast(intent);
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard(int keyboardHeight) {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.rootLayout);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }
}

以下示例活动使用此选项在键盘显示时隐藏视图,并在隐藏键盘时再次显示键盘。

xml布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/rootLayout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">              

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

        <!-- omitted for brevity -->

    </ScrollView>

    <LinearLayout android:id="@+id/bottomContainer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        >

        <!-- omitted for brevity -->

    </LinearLayout>

</LinearLayout>

活动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class TestActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);

        attachKeyboardListeners();
    }

    @Override
    protected void onShowKeyboard(int keyboardHeight) {
        // do things when keyboard is shown
        bottomContainer.setVisibility(View.GONE);
    }

    @Override
    protected void onHideKeyboard() {
        // do things when keyboard is hidden
        bottomContainer.setVisibility(View.VISIBLE);
    }        
}


一块蛋糕与令人敬畏
KeyboardVisibilityEvent库

1
2
3
4
5
6
7
8
KeyboardVisibilityEvent.setEventListener(
    getActivity(),
    new KeyboardVisibilityEventListener() {
        @Override
        public void onVisibilityChanged(boolean isOpen) {
            // write your code
        }
    });

Yasuhiro SHIMIZU的积分


正如维克拉姆在评论中指出的那样,检测软键盘是否显示或已经消失只能通过一些丑陋的黑客攻击。

也许在edittext上设置焦点监听器就足够了:

1
2
3
4
5
6
7
8
9
10
yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
        if (hasFocus) {
            //got focus
        } else {
            //lost focus
        }
   }
});


对于活动:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();

                activityRootView.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 100) {
                 //enter your code here
                }else{
                 //enter code for hid
                }
            }
        });

片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    view = inflater.inflate(R.layout.live_chat_fragment, null);
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                Rect r = new Rect();
                //r will be populated with the coordinates of your view that area still visible.
                view.getWindowVisibleDisplayFrame(r);

                int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top);
                if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...

                }
            }
        });


Jaap的答案不适用于AppCompatActivity。而是获取状态栏和导航栏等的高度,并与您的应用程序的窗口大小进行比较。

像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // navigation bar height
        int navigationBarHeight = 0;
        int resourceId = getResources().getIdentifier("navigation_bar_height","dimen","android");
        if (resourceId > 0) {
            navigationBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // status bar height
        int statusBarHeight = 0;
        resourceId = getResources().getIdentifier("status_bar_height","dimen","android");
        if (resourceId > 0) {
            statusBarHeight = getResources().getDimensionPixelSize(resourceId);
        }

        // display window size for the app layout
        Rect rect = new Rect();
        getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);

        // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard
        int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height());

        if (keyboardHeight <= 0) {
            onHideKeyboard();
        } else {
            onShowKeyboard(keyboardHeight);
        }
    }
};


你可以尝试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void initKeyBoardListener() {
    // Минимальное значение клавиатуры.
    // Threshold for minimal keyboard height.
    final int MIN_KEYBOARD_HEIGHT_PX = 150;
    // Окно верхнего уровня view.
    // Top-level window decor view.
    final View decorView = getWindow().getDecorView();
    // Регистрируем глобальный слушатель. Register global layout listener.
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        // Видимый прямоугольник внутри окна.
        // Retrieve visible rectangle inside window.
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
            decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
            final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

            if (lastVisibleDecorViewHeight != 0) {
                if (lastVisibleDecorViewHeight > visibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX) {
                    Log.d("Pasha","SHOW");
                } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) {
                    Log.d("Pasha","HIDE");
                }
            }
            // Сохраняем текущую высоту view до следующего вызова.
            // Save current decor view height for the next call.
            lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
    });
}

您可以使用我的Rx扩展功能(Kotlin)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * @return [Observable] to subscribe of keyboard visibility changes.
 */
fun AppCompatActivity.keyboardVisibilityChanges(): Observable<Boolean> {

    // flag indicates whether keyboard is open
    var isKeyboardOpen = false

    val notifier: BehaviorSubject<Boolean> = BehaviorSubject.create()

    // approximate keyboard height
    val approximateKeyboardHeight = dip(100)

    // device screen height
    val screenHeight: Int = getScreenHeight()

    val visibleDisplayFrame = Rect()

    val viewTreeObserver = window.decorView.viewTreeObserver

    val onDrawListener = ViewTreeObserver.OnDrawListener {

        window.decorView.getWindowVisibleDisplayFrame(visibleDisplayFrame)

        val keyboardHeight = screenHeight - (visibleDisplayFrame.bottom - visibleDisplayFrame.top)

        val keyboardOpen = keyboardHeight >= approximateKeyboardHeight

        val hasChanged = isKeyboardOpen xor keyboardOpen

        if (hasChanged) {
            isKeyboardOpen = keyboardOpen
            notifier.onNext(keyboardOpen)
        }
    }

    val lifeCycleObserver = object : GenericLifecycleObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event?) {
            if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                source.lifecycle.removeObserver(this)
                notifier.onComplete()
            }
        }
    }

    viewTreeObserver.addOnDrawListener(onDrawListener)
    lifecycle.addObserver(lifeCycleObserver)

    return notifier
            .doOnDispose {
                viewTreeObserver.removeOnDrawListener(onDrawListener)
                lifecycle.removeObserver(lifeCycleObserver)
            }
            .onTerminateDetach()
            .hide()
}

例:

1
2
3
4
5
(context as AppCompatActivity)
                    .keyboardVisibilityChanges()
                    .subscribeBy { isKeyboardOpen ->
                        // your logic
                    }


使用这个班,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
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 ([email protected])
 */
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 ([email protected])
 */
@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();
        }
    }

}
}

Android Manifest中,android:windowSoftInputMode="adjustResize"是必要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root
InputMethodManager im = (InputMethodManager)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()  {
    // Code here
}

@Override
public void onSoftKeyboardShow() {
    // Code here
}  
});

/*
Open or close the soft keyboard easily
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/* Prevent memory leaks:*/
@Override
public void onDestroy() {
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

P.S - 完全从这里开始。


软键盘监听器的不同实现,不依赖于窗口调整&amp;因此在多窗口世界中也能很好地工作。它当然有它自己的怪癖,但是它们与多窗口和窗口大小检测在一起的完全破碎相比毫无意义。

https://github.com/sqrt1764/AndroidSoftKeyboardListener

希望改进的想法!


您可以通过覆盖Activity中的两个方法来处理键盘可见性:onKeyUp()onKeyDown()此链接中的更多信息:https://developer.android.com/training/keyboard-input/commands


如果可以,尝试扩展EditText并覆盖'onKeyPreIme'方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void setOnEditorActionListener(final OnEditorActionListener listener) {
    mEditorListener = listener; //keep it for later usage
    super.setOnEditorActionListener(listener);
}

@Override
public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        if (mEditorListener != null) {
            //you can define and use custom listener,
            //OR define custom R.id.<imeId>
            //OR check event.keyCode in listener impl
            //* I used editor action because of ButterKnife @
            mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
        }
    }
    return super.onKeyPreIme(keyCode, event);
}

你怎么能扩展它:

  • 实现onFocus监听并声明'onKeyboardShown'
  • 声明'onKeyboardHidden'
  • 我认为,如前所述,重新计算屏幕高度不是100%成功。
    要明确的是,"onKeyPreIme"的覆盖不会在"隐藏软键盘编程"方法中调用,但是如果你在任何地方都这样做,你应该在那里做'onKeyboardHidden'逻辑,而不是创建一个全面的解决方案。


    另一种方法是检查用户何时停止输入...

    当TextEdit处于焦点(用户正在/正在键入)时,您可以隐藏视图(焦点侦听器)

    并使用Handler + Runnable和文本更改侦听器来关闭键盘(无论其可见性如何)并在一段延迟后显示视图。

    需要注意的主要问题是您使用的延迟,这取决于这些TextEdits的内容。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    Handler timeoutHandler = new Handler();
    Runnable typingRunnable = new Runnable() {
        public void run() {
            // current TextEdit
            View view = getCurrentFocus();

            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            // reset focus
            view.clearFocus();
            // close keyboard (whether its open or not)
            imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN);

            // SET VIEWS VISIBLE
        }
    };

    editText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                // SET VIEWS GONE

                // reset handler
                timeoutHandler.removeCallbacks(typingRunnable);
                timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
            }
        }
    });

    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            // Reset Handler...
            timeoutHandler.removeCallbacks(typingRunnable);
        }

        @Override
        public void afterTextChanged(Editable s) {
            // Reset Handler Cont.
            if (editText.getText().toString().trim().length() > 0) {
                timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT);
            }
        }
    });

    以下代码对我有用,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    mainLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                if (mainLayout != null) {
                    int heightDiff = mainLayout.getRootView().getHeight() - mainLayout.getHeight();
                    if (heightDiff > dpToPx(getActivity(), 200)) {
                       //keyboard is open
                    } else {
                       //keyboard is hide
                    }
                }
            }
        });

    对于adjustResize和FragmentActivity的情况,从@Jaap接受的解决方案对我不起作用。

    这是我的解决方案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        private int contentDiff;
        private int rootHeight;
        @Override
        public void onGlobalLayout() {
            View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT);
            if (rootHeight != mDrawerLayout.getRootView().getHeight()) {
                rootHeight = mDrawerLayout.getRootView().getHeight();
                contentDiff = rootHeight - contentView.getHeight();
                return;
            }
            int newContentDiff = rootHeight - contentView.getHeight();
            if (contentDiff != newContentDiff) {
                if (contentDiff < newContentDiff) {
                    onShowKeyboard(newContentDiff - contentDiff);
                } else {
                    onHideKeyboard();
                }
                contentDiff = newContentDiff;
            }
        }
    };


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    private boolean isKeyboardShown = false;
    private int prevContentHeight = 0;
    private ViewGroup contentLayout;

    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener =
            new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {
            int contentHeight = contentLayout.getHeight();
            int rootViewHeight = contentLayout.getRootView().getHeight();

            if (contentHeight > 0) {

                if (!isKeyboardShown) {
                    if (contentHeight < prevContentHeight) {
                        isKeyboardShown = true;
                        onShowKeyboard(rootViewHeight - contentHeight);
                    }
                } else {
                    if (contentHeight > prevContentHeight) {
                        isKeyboardShown = false;
                        onHideKeyboard();
                    }
                }

                prevContentHeight = contentHeight;
            }
        }
    };

    我已经修改了Jaap接受的答案了。但在我的情况下,很少有假设,如android:windowSoftInputMode=adjustResize,键盘在应用程序启动时不会显示在开头。而且,我认为屏幕方面与父母的身高相匹配。

    contentHeight > 0此检查让我知道相关屏幕是隐藏还是显示为应用此特定屏幕的键盘事件监听。我还在主活动的onCreate()方法中传递attachKeyboardListeners()中关于屏幕的布局视图。每当相关屏幕的高度发生变化时,我将其保存到prevContentHeight变量,以便稍后检查键盘是显示还是隐藏。

    对我来说,到目前为止它已经很好地工作了。我希望它也适用于其他人。


    当键盘显示

    1
    rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight()

    是真的,否则隐藏


    "Jaap van Hengstum"的答案对我有用,但没有必要像他刚才说的那样设置"android:windowSoftInputMode"!

    我把它缩小了(它现在只是检测我想要的东西,实际上是显示和隐藏键盘的事件):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight();
            int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
            if(heightDiff <= contentViewTop){
                onHideKeyboard();
            } else {
                onShowKeyboard();
            }
        }
    };

    private boolean keyboardListenersAttached = false;
    private ViewGroup rootLayout;

    protected void onShowKeyboard() {}
    protected void onHideKeyboard() {}

    protected void attachKeyboardListeners() {
        if (keyboardListenersAttached) {
            return;
        }

        rootLayout = (ViewGroup) findViewById(R.id.CommentsActivity);
        rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener);

        keyboardListenersAttached = true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (keyboardListenersAttached) {
            rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener);
        }
    }

    并且不要忘记添加这个

    1
    2
    3
    4
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_comments);
        attachKeyboardListeners();}

    这段代码非常好用

    将此类用于根视图:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    public class KeyboardConstraintLayout extends ConstraintLayout {

    private KeyboardListener keyboardListener;
    private EditText targetEditText;
    private int minKeyboardHeight;
    private boolean isShow;

    public KeyboardConstraintLayout(Context context) {
        super(context);
        minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); //128dp
    }

    public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
    }

    public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height); // 128dp
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (!isInEditMode()) {
            Activity activity = (Activity) getContext();
            @SuppressLint("DrawAllocation")
            Rect rect = new Rect();
            getWindowVisibleDisplayFrame(rect);

            int statusBarHeight = rect.top;
            int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

            if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
                if (keyboardHeight > minKeyboardHeight) {
                    if (!isShow) {
                        isShow = true;
                        keyboardListener.onKeyboardVisibility(true);
                    }
                }else {
                    if (isShow) {
                        isShow = false;
                        keyboardListener.onKeyboardVisibility(false);
                    }
                }
            }
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    public boolean isShowKeyboard() {
        return isShow;
    }

    public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
        this.targetEditText = targetEditText;
        this.keyboardListener = keyboardListener;
    }

    public interface KeyboardListener {
        void onKeyboardVisibility (boolean isVisible);
    }

    }

    并在activity或fragment中设置键盘侦听器:

    1
    2
    3
    4
    5
    6
            rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
            @Override
            public void onKeyboardVisibility(boolean isVisible) {

            }
        });

    这将无需更改您的活动的android:windowSoftInputMode即可使用

    第1步:扩展EditText类并覆盖这两个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Override
    public void setOnEditorActionListener(final OnEditorActionListener listener) {
        mEditorListener = listener;
        super.setOnEditorActionListener(listener);
    }

    @Override
    public boolean onKeyPreIme(final int keyCode, final KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
            if (mEditorListener != null) {
                mEditorListener.onEditorAction(this, android.R.id.closeButton, event);
            }
        }
        return super.onKeyPreIme(keyCode, event);
    }

    第2步:在您的活动中创建这两个:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    private void initKeyboard() {
        final AppEditText editText = findViewById(R.id.some_id);
        editText.setOnFocusChangeListener(new OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
                setKeyboard(hasFocus);
            }
        });
        editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (event == null || event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                    editText.clearFocus();
                }
                return false;
            }
        });
    }

    public void setKeyboard(boolean isShowing) {
        // do something
    }

    ***请记住,为了使clearFocus工作,您必须使父层次结构中的父级或第一个子级成为可聚焦的。

    1
    2
        setFocusableInTouchMode(true);
        setFocusable(true);


    不幸的是,我没有足够的声誉来评论Jaap van Hengstum的回答。但是我读了一些人的评论,问题是contentViewTop总是0并且总是调用onShowKeyboard(...)

    我有同样的问题,并找出了我遇到的问题。我使用AppCompatActivity而不是'普通'Activity。在这种情况下,Window.ID_ANDROID_CONTENT指的是ContentFrameLayout,而不指向具有正确顶值的FrameLayout。在我的情况下,使用'普通'Activity是好的,如果你必须使用另一个活动类型(我刚刚测试了AppCompatActivity,也许这也是其他acitivy类型的问题,如FragmentActivity) ,你必须访问FrameLayout,这是ContentFrameLayout的祖先。


    这不符合要求......

    ...已经看到许多使用尺寸计算来检查......

    我想确定它是否打开,我找到isAcceptingText()

    所以这真的没有回答这个问题,因为它没有解决开放或关闭更像是打开或关闭所以它是相关的代码,可以帮助其他人在各种场景...

    在一项活动中

    1
    2
    3
    4
    5
        if (((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
            Log.d(TAG,"Software Keyboard was shown");
        } else {
            Log.d(TAG,"Software Keyboard was not shown");
        }

    在片段中

    1
    2
    3
    4
    5
    6
        if (((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).isAcceptingText()) {
            Log.d(TAG,"Software Keyboard was shown");
        } else {
            Log.d(TAG,"Software Keyboard was not shown");

        }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class MainActivity extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mainactivity);
        attachKeyboardListeners();
        ....
        yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
                if (hasFocus) {
                    yourEditText2.setVisibility(View.GONE);
                    yourEditText3.setVisibility(View.GONE);
                    yourEditText4.setVisibility(View.GONE);
                    yourEditText5.setVisibility(View.GONE);
                } else {
                    yourEditText2.setVisibility(View.VISIBLE);
                    yourEditText3.setVisibility(View.VISIBLE);
                    yourEditText4.setVisibility(View.VISIBLE);
                    yourEditText5.setVisibility(VISIBLE);
                }
           }
        });
        }
    }