Как проверить видимость программной клавиатуры в Android?
Мне нужно сделать очень простую вещь - выяснить, отображается ли программная клавиатура. Возможно ли это в Android?
46 ответов
НОВЫЙ ОТВЕТ добавлен 25 января 2012
После написания приведенного ниже ответа кто-то рассказал мне о существовании ViewTreeObserver и его друзей, API, которые скрывались в SDK начиная с версии 1.
Вместо того, чтобы требовать пользовательский тип макета, гораздо более простое решение состоит в том, чтобы дать корневому представлению вашей деятельности известный идентификатор, скажем, @+id/activityRoot
, подключите GlobalLayoutListener к ViewTreeObserver и оттуда вычислите разницу в размерах между корнем представления вашей деятельности и размером окна:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
if (heightDiff > dpToPx(this, 200)) { // if more than 200 dp, it's probably a keyboard...
// ... do something here
}
}
});
Использование утилиты, такой как:
public static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
Легко!
Примечание. Ваше приложение должно установить этот флаг в манифесте Android. android:windowSoftInputMode="adjustResize"
В противном случае решение выше не будет работать.
ОРИГИНАЛЬНЫЙ ОТВЕТ
Да, это возможно, но это гораздо сложнее, чем должно быть.
Если мне нужно позаботиться о том, когда клавиатура появляется и исчезает (что происходит довольно часто), тогда я настраиваю класс макета верхнего уровня на тот, который переопределяет onMeasure()
, Основная логика заключается в том, что если раскладка оказывается заполненной значительно меньше, чем общая площадь окна, то, вероятно, отображается программная клавиатура.
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.LinearLayout;
/*
* LinearLayoutThatDetectsSoftKeyboard - a variant of LinearLayout that can detect when
* the soft keyboard is shown and hidden (something Android can't tell you, weirdly).
*/
public class LinearLayoutThatDetectsSoftKeyboard extends LinearLayout {
public LinearLayoutThatDetectsSoftKeyboard(Context context, AttributeSet attrs) {
super(context, attrs);
}
public interface Listener {
public void onSoftKeyboardShown(boolean isShowing);
}
private Listener listener;
public void setListener(Listener listener) {
this.listener = listener;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
Activity activity = (Activity)getContext();
Rect rect = new Rect();
activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rect.top;
int screenHeight = activity.getWindowManager().getDefaultDisplay().getHeight();
int diff = (screenHeight - statusBarHeight) - height;
if (listener != null) {
listener.onSoftKeyboardShown(diff>128); // assume all soft keyboards are at least 128 pixels high
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
Тогда в вашем классе деятельности...
public class MyActivity extends Activity implements LinearLayoutThatDetectsSoftKeyboard.Listener {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
LinearLayoutThatDetectsSoftKeyboard mainLayout = (LinearLayoutThatDetectsSoftKeyboard)findViewById(R.id.main);
mainLayout.setListener(this);
...
}
@Override
public void onSoftKeyboardShown(boolean isShowing) {
// do whatever you need to do here
}
...
}
Надеюсь, это поможет кому-то.
Новый ответ, который дал Рубен Скрэттон, великолепен и действительно эффективен, но он действительно работает, только если вы установили свой windowSoftInputMode в AdjustResize. Если вы установите его в AdjustPan, по-прежнему невозможно определить, видна ли клавиатура, используя его фрагмент кода. Чтобы обойти это, я сделал эту крошечную модификацию кода выше.
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
... do something here
}
}
});
Это было всегда с точки зрения компьютера, но этот вопрос все еще невероятно актуален!
Итак, я взял приведенные выше ответы и немного их объединил и уточнил...
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private boolean wasOpened;
private final int DefaultKeyboardDP = 100;
// From @nathanielwolf answer... Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
private final Rect r = new Rect();
@Override
public void onGlobalLayout() {
// Convert the dp to pixels.
int estimatedKeyboardHeight = (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, activityRootView.getResources().getDisplayMetrics());
// Conclude whether the keyboard is shown or not.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isShown = heightDiff >= estimatedKeyboardHeight;
if (isShown == wasOpened) {
Log.d("Keyboard state", "Ignoring global layout change...");
return;
}
wasOpened = isShown;
listener.onVisibilityChanged(isShown);
}
});
}
Работает для меня:)
ПРИМЕЧАНИЕ. Если вы заметили, что DefaultKeyboardDP не подходит для вашего устройства, играйте со значением и оставьте комментарий, чтобы все знали, каково должно быть значение... в конце концов мы получим правильное значение для всех устройств!
Извините за поздний ответ, но я создал небольшой вспомогательный класс для обработки событий открытия / закрытия с уведомлением слушателей и другими полезными вещами, возможно, кто-то посчитает это полезным:
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.LinkedList;
import java.util.List;
public class SoftKeyboardStateWatcher implements ViewTreeObserver.OnGlobalLayoutListener {
public interface SoftKeyboardStateListener {
void onSoftKeyboardOpened(int keyboardHeightInPx);
void onSoftKeyboardClosed();
}
private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
private final View activityRootView;
private int lastSoftKeyboardHeightInPx;
private boolean isSoftKeyboardOpened;
public SoftKeyboardStateWatcher(View activityRootView) {
this(activityRootView, false);
}
public SoftKeyboardStateWatcher(View activityRootView, boolean isSoftKeyboardOpened) {
this.activityRootView = activityRootView;
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
final Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (!isSoftKeyboardOpened && heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
isSoftKeyboardOpened = true;
notifyOnSoftKeyboardOpened(heightDiff);
} else if (isSoftKeyboardOpened && heightDiff < 100) {
isSoftKeyboardOpened = false;
notifyOnSoftKeyboardClosed();
}
}
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
}
public boolean isSoftKeyboardOpened() {
return isSoftKeyboardOpened;
}
/**
* Default value is zero {@code 0}.
*
* @return last saved keyboard height in px
*/
public int getLastSoftKeyboardHeightInPx() {
return lastSoftKeyboardHeightInPx;
}
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.add(listener);
}
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.remove(listener);
}
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardOpened(keyboardHeightInPx);
}
}
}
private void notifyOnSoftKeyboardClosed() {
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardClosed();
}
}
}
}
Пример использования:
final SoftKeyboardStateWatcher softKeyboardStateWatcher
= new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);
// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
Некоторые улучшения, позволяющие избежать неправильного определения видимости программной клавиатуры на устройствах высокой плотности:
Порог разности высот должен быть определен как 128 dp, а не 128 пикселей.
Обратитесь к Google Design Doc о метриках и сетке, 48 dp - удобный размер для сенсорного объекта и 32 dp - минимум для кнопок. Стандартная программная клавиатура должна включать 4 ряда клавиш, поэтому минимальная высота клавиатуры должна быть: 32 dp * 4 = 128 dp, что означает, что пороговый размер должен передаваться в пикселях путем умножения плотности устройства. Для устройств xxxhdpi (плотность 4) порог высоты программной клавиатуры должен составлять 128 * 4 = 512 пикселей.Разница высот между корневым видом и его видимой областью:
высота корневого представления - высота строки состояния - видимая высота рамки = нижняя часть корневого представления - нижняя часть видимой рамки, поскольку высота строки состояния равна верхней части видимой рамки корневого представления.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; }
Вау, у нас есть хорошие новости, Android Geeks. И пришло время прощаться со старым. Сначала я добавлю официальную заметку о выпуске, чтобы прочитать и узнать больше об этих методах / классах, а затем мы увидим эти удивительные методы / классы.
Важное примечание: не добавляйте их в свои релизные приложения, пока эти классы / методы не будут выпущены.
Как проверить видимость клавиатуры
val insets = ViewCompat.getRootWindowInsets(view)
val isKeyboardVisible = insets.isVisible(Type.ime())
Несколько других утилит
Как получить высоту клавиатуры
val insets = ViewCompat.getRootWindowInsets(view)
val keyboardHeight = insets.getInsets(Type.ime()).bottom
Как показать / скрыть клавиатуру
val controller = view.windowInsetsController
// Show the keyboard
controller.show(Type.ime())
// Hide the keyboard
controller.hide(Type.ime())
Примечание. WindowInsetsController добавлен в API-30, поэтому подождите, пока класс обратной совместимости не станет доступен.
Как прослушивать событие скрытия / показа клавиатуры
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val isKeyboardVisible = insets.isVisible(Type.ime())
if (isKeyboardVisible) {
// Do it when keyboard is being shown
} else {
// Do it when keyboard is hidden
}
// Return the insets to keep going down this event to the view hierarchy
insets
}
Я потратил немного времени, чтобы понять это... Я запустил некоторые CastExceptions, но понял, что вы можете заменить LinearLayout в layout.xml именем класса.
Как это:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/llMaster">
<com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard android:background="@drawable/metal_background"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:id="@+id/rlMaster" >
<LinearLayout android:layout_width="fill_parent"
android:layout_height="1dip" android:background="@drawable/line"></LinearLayout>
....
</com.ourshoppingnote.RelativeLayoutThatDetectsSoftKeyboard>
</LinearLayout>
Таким образом, вы не столкнетесь с какими-либо проблемами.
... и если вы не хотите делать это на каждой странице, я рекомендую вам использовать "MasterPage в Android". Смотрите ссылку здесь: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx
Идея заключается в том, что если вам необходимо одновременно скрыть клавиатуру и проверить состояние программного ввода, используйте следующее решение:
public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}
Этот метод возвращает true, если перед скрытием была показана клавиатура.
Проверка высоты элементов не надежна, потому что некоторые клавиатуры, такие как WifiKeyboard, имеют нулевую высоту.
Вместо этого вы можете использовать результат обратного вызова showSoftInput() и hideSoftInput() для проверки состояния клавиатуры. Полная информация и пример кода на
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
Вместо того, чтобы предполагать разностное кодирование, я сделал что-то вроде этого, так как у меня не было пунктов меню в моем приложении.
final View root= findViewById(R.id.myrootview);
root.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
int heightDiff = root.getRootView().getHeight() - root.getHeight();
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int contentViewTop=
window.findViewById(Window.ID_ANDROID_CONTENT).getTop();
if(heightDiff <= contentViewTop){
//Soft KeyBoard Hidden
}else{
//Soft KeyBoard Shown
}
}
});
Существует также решение с системными вставками, но оно работает только с API >= 21
(Android L
). Скажи у тебя BottomNavigationView
, который является ребенком LinearLayout
и вам нужно скрыть это, когда клавиатура отображается:
> LinearLayout
> ContentView
> BottomNavigationView
Все, что вам нужно сделать, это расширить LinearLayout
таким образом:
public class KeyboardAwareLinearLayout extends LinearLayout {
public KeyboardAwareLinearLayout(Context context) {
super(context);
}
public KeyboardAwareLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public KeyboardAwareLinearLayout(Context context,
@Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public KeyboardAwareLinearLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
int childCount = getChildCount();
for (int index = 0; index < childCount; index++) {
View view = getChildAt(index);
if (view instanceof BottomNavigationView) {
int bottom = insets.getSystemWindowInsetBottom();
if (bottom >= ViewUtils.dpToPx(200)) {
// keyboard is shown
view.setVisibility(GONE);
} else {
// keyboard is hidden
view.setVisibility(VISIBLE);
}
}
}
return insets;
}
}
Идея состоит в том, что когда клавиатура показана, системные вставки меняются с довольно большими .bottom
значение.
Я обнаружил, что комбинация метода @Reuben_Scratton и метода @ Йогеша, кажется, работает лучше всего. Объединение их методов приведет к чему-то вроде этого:
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
// ... do something here
}
}
});
Вы можете наблюдать скрытие программных клавиш, используя decorView действия.
public final class SoftKeyboardUtil {
public static final String TAG = "SoftKeyboardUtil";
public static void observeSoftKeyBoard(Activity activity , final OnSoftKeyBoardHideListener listener){
final View decorView = activity.getWindow().getDecorView();
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
int displayHight = rect.bottom - rect.top;
int hight = decorView.getHeight();
boolean hide = (double)displayHight / hight > 0.8 ;
if(Log.isLoggable(TAG, Log.DEBUG)){
Log.d(TAG ,"DecorView display hight = "+displayHight);
Log.d(TAG ,"DecorView hight = "+ hight);
Log.d(TAG, "softkeyboard visible = " + !hide);
}
listener.onSoftKeyBoardVisible(!hide);
}
});
}
public interface OnSoftKeyBoardHideListener{
void onSoftKeyBoardVisible(boolean visible);
}
}
Там скрытый метод может помочь в этом, InputMethodManager.getInputMethodWindowVisibleHeight
, Но я не знаю, почему это скрыто.
import android.content.Context
import android.os.Handler
import android.view.inputmethod.InputMethodManager
class SoftKeyboardStateWatcher(private val ctx: Context) {
companion object {
private const val DELAY = 10L
}
private val handler = Handler()
private var isSoftKeyboardOpened: Boolean = false
private val height: Int
get() {
val imm = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val method = imm.javaClass.getMethod("getInputMethodWindowVisibleHeight")
method.isAccessible = true
return method.invoke(imm) as Int
}
private val task: Runnable by lazy {
Runnable {
start()
if (!isSoftKeyboardOpened && height > 0) {
isSoftKeyboardOpened = true
notifyOnSoftKeyboardOpened(height)
} else if (isSoftKeyboardOpened && height == 0) {
isSoftKeyboardOpened = false
notifyOnSoftKeyboardClosed()
}
}
}
var listener: SoftKeyboardStateListener? = null
interface SoftKeyboardStateListener {
fun onSoftKeyboardOpened(keyboardHeightInPx: Int)
fun onSoftKeyboardClosed()
}
fun start() {
handler.postDelayed(task, DELAY)
}
fun stop() {
handler.postDelayed({
if (!isSoftKeyboardOpened) handler.removeCallbacks(task)
}, DELAY * 10)
}
private fun notifyOnSoftKeyboardOpened(keyboardHeightInPx: Int) {
listener?.onSoftKeyboardOpened(keyboardHeightInPx)
}
private fun notifyOnSoftKeyboardClosed() {
listener?.onSoftKeyboardClosed()
}
}
Я только что столкнулся с ошибкой при использовании большинства решений выше, которые предлагают добавить фиксированное число.
S4 имеет высокое значение dpi, в результате чего высота навигационной панели составляет 100 пикселей, поэтому мое приложение считает, что клавиатура постоянно открыта.
Поэтому, когда все новые телефоны с высоким разрешением выпускаются, я считаю, что использование жестко закодированных значений не является хорошей идеей в долгосрочной перспективе.
Лучший подход, который я нашел после некоторого тестирования на различных экранах и устройствах, заключался в использовании процента. Получите разницу между decorView и содержимым вашего приложения, а затем проверьте процент этой разницы. Из статистики, которую я получил, большинство навигационной панели (независимо от размера, разрешения и т. Д.) Будет занимать от 3% до 5% экрана. Где, как будто клавиатура открыта, она занимала от 47% до 55% экрана.
В качестве решения я решил проверить, что разница больше 10%, и я предполагаю, что клавиатура открыта.
Попробуй это:
final View activityRootView = getWindow().getDecorView().getRootView();
activityRootView.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.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff < activityRootView.getRootView().getHeight() / 4 ) { // if more than 100 pixels, its probably a keyboard...
// ... do something here ... \\
}
}
});
Согласно документу.. https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat
Чтобы получить текущую видимость клавиатуры, вы можете использовать getRootWindowInsets
, а затем вызовите isVisible()
функция, передавая тип IME.
val windowinsetscompat = ViewCompat.getRootWindowInsets(view)
val imeVisible = windowinsetscompat.isVisible(Type.ime())
есть также слушатель изменений OnApplyWindowInsetsListener
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val imeVisible = insets.isVisible(Type.ime())
}
Я использовал небольшой вариант ответа Реубана, который оказался более полезным в определенных обстоятельствах, особенно с устройствами с высоким разрешением.
final View activityRootView = findViewById(android.R.id.content);
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
}
}
});
Это было всегда с точки зрения компьютера, но этот вопрос все еще невероятно актуален! Итак, я взял приведенные выше ответы и немного их объединил и уточнил...
public interface OnKeyboardVisibilityListener {
void onVisibilityChanged(boolean visible);
}
public final void setKeyboardListener(final OnKeyboardVisibilityListener listener) {
final View activityRootView = ((ViewGroup) getActivity().findViewById(android.R.id.content)).getChildAt(0);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
private boolean wasOpened;
private final Rect r = new Rect();
@Override
public void onGlobalLayout() {
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
boolean isOpen = heightDiff > 100;
if (isOpen == wasOpened) {
logDebug("Ignoring global layout change...");
return;
}
wasOpened = isOpen;
listener.onVisibilityChanged(isOpen);
}
});
}
Меня устраивает.
Ни одно из этих решений не будет работать для Lollipop как есть. В леденце activityRootView.getRootView().getHeight()
включает в себя высоту панели кнопок, в то время как измерение вида не имеет. Я адаптировал лучшее / простое решение выше для работы с Lollipop.
final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
Resources res = getResources();
// The status bar is 25dp, use 50dp for assurance
float maxDiff =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, res.getDisplayMetrics());
//Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
float buttonBarHeight =
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48, res.getDisplayMetrics());
maxDiff += buttonBarHeight;
}
if (heightDiff > maxDiff) { // if more than 100 pixels, its probably a keyboard...
...do something here
}
}
});
У меня были трудности с поддержанием состояния клавиатуры при изменении ориентации фрагментов в окне просмотра. Я не уверен, почему, но это только кажется вялым и действует не так, как в стандартном действии.
Чтобы сохранить состояние клавиатуры в этом случае, сначала вы должны добавить android:windowSoftInputMode = "stateUnchanged"
на ваш AndroidManifest.xml
, Однако вы можете заметить, что это на самом деле не решает всей проблемы - клавиатура не открывалась для меня, если она была открыта до изменения ориентации. Во всех остальных случаях поведение казалось правильным.
Затем нам нужно реализовать одно из упомянутых здесь решений. Самым чистым из найденных мной был Джордж Майсурадзе - используйте логический обратный вызов из hideSoftInputFromWindow:
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
Я сохранил это значение в моем фрагменте onSaveInstanceState
метод и извлек его onCreate
, Затем я принудительно показал клавиатуру в onCreateView
если бы оно имело значение true
(он возвращает true, если клавиатура видна перед тем, как фактически скрыть ее до уничтожения фрагмента).
Я думаю, что этот метод поможет вам узнать, является ли клавиатура видимой или нет.
public Boolean isSoftKeyBoardVisible(){
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
Log.d(TAG,"Software Keyboard was shown");
return true;
} else {
Log.d(TAG,"Software Keyboard was not shown");
return false;
}
}
Не делайте никакого жесткого кода. Лучший способ - изменить размеры своих представлений во время работы над Get Focus на EditText с помощью KeyBord Show. Вы можете сделать это, добавив свойство resize для активности в файл Manifest, используя приведенный ниже код.
android:windowSoftInputMode="adjustResize"
Вы можете попробовать это, отлично работает для меня:
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
//Software Keyboard was shown..
} else {
//Software Keyboard was not shown..
}
Мой ответ в основном совпадает с ответом Качи, но я обернул его в хороший вспомогательный класс, чтобы очистить то, как он используется в моем приложении.
import android.app.Activity;
import android.app.Fragment;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
/**
* Detects Keyboard Status changes and fires events only once for each change
*/
public class KeyboardStatusDetector {
KeyboardVisibilityListener visibilityListener;
boolean keyboardVisible = false;
public void registerFragment(Fragment f) {
registerView(f.getView());
}
public void registerActivity(Activity a) {
registerView(a.getWindow().getDecorView().findViewById(android.R.id.content));
}
public KeyboardStatusDetector registerView(final View v) {
v.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
v.getWindowVisibleDisplayFrame(r);
int heightDiff = v.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
/** Check this variable to debounce layout events */
if(!keyboardVisible) {
keyboardVisible = true;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(true);
}
} else {
if(keyboardVisible) {
keyboardVisible = false;
if(visibilityListener != null) visibilityListener.onVisibilityChanged(false);
}
}
}
});
return this;
}
public KeyboardStatusDetector setVisibilityListener(KeyboardVisibilityListener listener) {
visibilityListener = listener;
return this;
}
public static interface KeyboardVisibilityListener {
public void onVisibilityChanged(boolean keyboardVisible);
}
}
Вы можете использовать это для обнаружения изменений клавиатуры в любом месте приложения:
new KeyboardStatusDetector()
.registerFragment(fragment) //register to a fragment
.registerActivity(activity) //or register to an activity
.registerView(view) //or register to a view
.setVisibilityListener(new KeyboardVisibilityListener() {
@Override
public void onVisibilityChanged(boolean keyboardVisible) {
if(keyboardVisible) {
//Do stuff for keyboard visible
}else {
//Do stuff for keyboard hidden
}
}
});
Примечание: используйте только один из "зарегистрированных" вызовов. Все они работают одинаково и только для удобства
После понимания некоторых проблем с различными разрешениями я решил использовать относительный размер. Как я заметил, разница между видимым и скрытым состояниями составляет около 30%. Поэтому я решил заменить 128 PX на 0.3.
И я добавил этот слушатель класса, чтобы уведомлять о любых изменениях.
Вот моя версия
import android.app.*;
import android.graphics.*;
import android.view.*;
public class SoftKeyboardState {
public static final int HIDDEN = 0, VISIBLE = 1;
private OnKeyboardStateChangedListener listener;
private View decorView;
public SoftKeyboardState(Activity activity) {
this.decorView = activity.findViewById(android.R.id.content);
initKeyboardListener();
}
private void initKeyboardListener() {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener(){
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) && (lastVisibleDecorViewHeight / visibleDecorViewHeight >= 0.3f)) {
// visible
if (listener != null)listener.onKeyboardStateChanged(VISIBLE);
} else if ((lastVisibleDecorViewHeight < visibleDecorViewHeight) && (visibleDecorViewHeight / lastVisibleDecorViewHeight >= 0.3f)) {
// hidden
if (listener != null)listener.onKeyboardStateChanged(HIDDEN);
}
}
lastVisibleDecorViewHeight = visibleDecorViewHeight;
}
});
}
public void setOnKeyboardStateChangedListener(OnKeyboardStateChangedListener listener) {
this.listener = listener;
}
public interface OnKeyboardStateChangedListener {
public void onKeyboardStateChanged(int state);
}
}
Вот мое решение, и оно работает. Вместо того, чтобы искать размер в пикселях, просто убедитесь, что высота представления контента изменилась или нет:
// Scroll to the latest comment whenever the keyboard is shown
commentsContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
private int oldHeight;
@Override
public void onGlobalLayout() {
int newHeight = commentsContent.getMeasuredHeight();
if (newHeight < oldHeight) {
// Check for the keyboard showing in case the height difference
// is a result of orientation change
if (isSoftKeyboardShowing(CommentsActivity.this)) {
// Keyboard is showing so scroll to the latest comment
scrollToLatestComment();
}
}
oldHeight = newHeight;
}
});
public static boolean isSoftKeyboardShowing(Activity activity) {
InputMethodManager inputMethodManager = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
return inputMethodManager.isActive();
}
Подумайте, есть простой способ, как это:
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.isActive();
Вы также можете увидеть, активен ли он в определенном представлении:
imm.isActive(View v);
99% решений здесь основаны на вероятности IME WINDOW SIZE, и каждое такое решение стоит гадости!
так как:
- НАДПИСИ - из пользовательских приложений или системных приложений
- IME не имеет МИНИМАЛЬНОГО РАЗМЕРА, он может занимать 100% размера окна и может быть настолько тонким, как воображение разработчика:)
- МОДАЛЬНЫЕ окна / МУЛЬТИ-окна
- и многое другое, например, незнание IPC (например: постороннее окно или обнаружение его содержимого)
поэтому угадывать, что это IME, всегда неверно - не угадайте, будьте уверены!!!
@kevin-du - лучшее решение, написанное сейчас, поскольку его запрос IMM для высоты IME - но, как он сказал, метод является скрытым API, поэтому его использование может быть опасным в плане получения неправильных "ложноотрицательных результатов" - из-за неправильного использования разработчика.
Это решение может снова открыть клавиатуру, но это работает.
InputMethodManager inputManager = ( (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE) );
private boolean isKeyboardShowing() {
boolean isKeyboardShowing = inputManager.hideSoftInputFromWindow(irrelevantView.getWindowToken(), 0);
if (isKeyboardShowing) {
inputManager.showSoftInput(this.getCurrentFocus(), 0);
}
return isKeyboardShowing;
}