Мне нужно сделать очень простую вещь - выяснить, отображается ли программная клавиатура. Возможно ли это в Android?
Мне нужно сделать очень простую вещь - выяснить, отображается ли программная клавиатура. Возможно ли это в Android?
Ответы:
НОВЫЙ ОТВЕТ добавлен 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
}
...
}
((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
android.R.id.content
), вы сможете с большей уверенностью сказать, что System
объект, а не ваше приложение, изменяет свою высоту. Для команды Android было бы намного безопаснее дать нам передышку и сообщить нам хотя бы основные сведения о вводе SoftKeyboard.
heightDiff
всегда будет указана высота панели действий. В новом ответе, который был проигнорирован при тестировании, если эта высота больше некоторой константы, но 100 пикселей недостаточно для устройств xxhdpi, таких как Nexus 4. Рассмотрите возможность преобразования этого значения в DP, если вы действительно хотите использовать эту хакерскую работу. около.
Надеюсь, это поможет кому-то.
Новый ответ, который дал Рубен Скраттон, великолепен и действительно эффективен, но он действительно работает, только если вы установили свой 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 > 0.25*activityRootView.getRootView().getHeight()) { // if more than 25% of the screen, its probably a keyboard...
... do something here
}
}
});
TwoDScrollerView
похожего на stackoverflow.com/a/5224088/530513, хотя и с масштабированием. Ребенок был не простой, ImageView
а пользовательской раскладкой (расширяет RelativeLayout
), но не смог обнаружить клавиатуру, используя рекомендованное решение, несмотря на настройки android:windowSoftInputMode="adjustResize"
. Спасибо!
ActionBar
и ActionBarSherlock
. Большое спасибо! Кстати, есть способ r.height()
:)
heightDiff > root.getRootView().getHeight() / 4
это хорошая ценность для работы с устройством с высоким разрешением. 100px - короткая. в Nexus 5 с разрешением 1080x1920, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // Клавиатура работает в Galaxy S2 с разрешением 480x800, 800 - (800-38)>? 100 = 38 800 - (410-38)>? 100 = 428 // клавиатура включена, поэтому магическое число 100px недостаточно хорошо.
Это было всегда с точки зрения компьютера, но этот вопрос все еще невероятно актуален!
Итак, я взял приведенные выше ответы и немного их объединил и уточнил ...
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
getLastKeyboardHeightInPx()
строки не включает высоту этого ряда. Знаете ли вы, как принять это во внимание?
Некоторые улучшения, позволяющие избежать неправильного определения видимости программной клавиатуры на устройствах высокой плотности:
Порог разности высот должен быть определен как 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;
}
Я потратил немного времени, чтобы выяснить это ... Я запустил некоторые исключения CastException, но понял, что вы можете заменить 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
Проверка высоты элементов не надежна, потому что некоторые клавиатуры, такие как WifiKeyboard, имеют нулевую высоту.
Вместо этого вы можете использовать результат обратного вызова showSoftInput () и hideSoftInput (), чтобы проверить состояние клавиатуры. Полная информация и пример кода на
https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android
Идея заключается в том, что если вам нужно одновременно скрыть клавиатуру и проверить состояние программного ввода, используйте следующее решение:
public boolean hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}
Этот метод возвращает true, если перед скрытием была показана клавиатура.
Я обнаружил, что комбинация метода @ 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);
}
}
Вместо того, чтобы предполагать разностное кодирование, я сделал что-то вроде этого, так как у меня не было пунктов меню в моем приложении.
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
значением.
Для этого может помочь скрытый метод 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()
}
}
Ни одно из этих решений не будет работать для Lollipop как есть. В 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
}
}
});
Я только что столкнулся с ошибкой при использовании большинства решений выше, которые предлагают добавить фиксированное число.
S4 имеет высокое значение dpi, в результате чего высота навигационной панели составляет 100 пикселей, поэтому мое приложение считает, что клавиатура постоянно открыта.
Так что, учитывая выпуск всех новых телефонов с высоким разрешением, я считаю, что использование жестко закодированных значений не является хорошей идеей в долгосрочной перспективе.
Лучший подход, который я нашел после некоторого тестирования на различных экранах и устройствах, заключался в использовании процента. Получите разницу между decorView и содержимым вашего приложения, а затем проверьте процент этой разницы. Из статистики, которую я получил, большинство навигационной панели (независимо от размера, разрешения и т. Д.) Будет занимать от 3% до 5% экрана. Где, как будто клавиатура открыта, она занимала от 47% до 55% экрана.
В качестве решения я решил проверить, составляет ли разница больше 10%, тогда я предполагаю, что клавиатура открыта.
Я использовал небольшой вариант ответа Реубана, который оказался более полезным в определенных обстоятельствах, особенно с устройствами с высоким разрешением.
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
}
}
});
R.id.activityRoot
, вы можете просто использовать android.R.id.content
именно то, что вам нужно.
Это было всегда с точки зрения компьютера, но этот вопрос все еще невероятно актуален! Итак, я взял приведенные выше ответы и немного их объединил и уточнил ...
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);
}
});
}
Меня устраивает.
Попробуй это:
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 ... \\
}
}
});
Мой ответ в основном совпадает с ответом Качи, но я обернул его в хороший вспомогательный класс, чтобы очистить то, как он используется в моем приложении.
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
}
}
});
Примечание: используйте только один из «зарегистрированных» вызовов. Все они работают одинаково и только для удобства
Вы можете попробовать это, отлично работает для меня:
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm.isAcceptingText()) {
//Software Keyboard was shown..
} else {
//Software Keyboard was not shown..
}
У меня были трудности с поддержанием состояния клавиатуры при изменении ориентации фрагментов в окне просмотра. Я не уверен почему, но это только кажется вялым и действует не так, как в стандартном действии.
Чтобы сохранить состояние клавиатуры в этом случае, сначала вы должны добавить 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, если клавиатура видна до фактического ее скрытия до уничтожения фрагмента).
Вот мое решение, и оно работает. Вместо того, чтобы искать размер в пикселях, просто убедитесь, что высота представления содержимого изменилась или нет:
// 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();
}
Не делайте никакого жесткого кода. Лучший способ - изменить размеры своих представлений во время работы над Get Focus на EditText с помощью KeyBord Show. Вы можете сделать это, добавив свойство resize для активности в файл Manifest, используя приведенный ниже код.
android:windowSoftInputMode="adjustResize"
Существует прямой способ выяснить это. И это не требует каких-либо изменений макета.
Таким образом, он работает и в полноэкранном режиме с погружением.
Хитрость в том, что вы пытаетесь скрыть или показать программную клавиатуру и зафиксировать результат этой попытки.
Никакой паники, это не совсем показывает или скрывает клавиатуру. Мы просто просим о состоянии.
Чтобы оставаться в курсе, вы можете просто повторить операцию, например, каждые 200 миллисекунд, используя обработчик.
Вы найдете реализацию здесь: https://stackoverflow.com/a/27567074/2525452
Я думаю, что этот метод поможет вам узнать, является ли клавиатура видимой или нет.
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;
}
}
Новый ответ Рубена Скрэттона (рассчитать HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
) не будет работать в действии, если вы установите режим полупрозрачной строки состояния.
Если вы используете полупрозрачную строку состояния, activityRootView.getHeight()
никогда не изменится погода, если видна программная клавиатура. он всегда будет возвращать высоту активности и статусную строку.
Например, Nexus 4, Android 5.0.1, установленный android:windowTranslucentStatus
в true, вернет 1184 навсегда, даже если у меня есть opend. Если вы установитеandroid:windowTranslucentStatus
значение false, он вернёт высоту правильно, если ime невидим, он вернёт 1134 (не включая строку состояния). Закройте ime, возможно, вернётся 5xx (зависит от высоты ime)
Я не знаю, погода ли это, я пробовал на 4.4.4 и 5.0.1, результат тот же.
Итак, до сих пор, второй наиболее согласованный ответ, решение Качи будет самым безопасным способом для расчета высоты им. Вот копия:
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
}
}
});
Метод, который который не нуждается в LayoutListener
В моем случае я хотел бы сохранить состояние клавиатуры перед заменой моего фрагмента. Я вызываю метод hideSoftInputFromWindow изonSaveInstanceState
, который закрывает клавиатуру и возвращает меня была ли клавиатура видна или нет.
Этот метод прост, но может изменить состояние вашей клавиатуры.
Я знаю, что это старый пост, но я думаю, что это самый простой из известных мне подходов, и моим тестовым устройством является Nexus 5. Я не пробовал его на других устройствах. Надеюсь, что другие поделятся своим подходом, если они найдут мой код нехорошим :)
public static boolean isKeyboardShown(Context context, View view) {
if (context == null || view == null) {
return false;
}
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
imm.hideSoftInputFromWindow возвращает логическое значение.
Спасибо,
if (keyopen())
{
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);
}
Вышеуказанная функция используется для проверки видимости клавиатуры. Если это так, то я закрываю это.
Ниже показаны два необходимых метода.
Сначала определите работоспособную высоту окна в onCreate.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// add to onCreate method
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
sheight= rectgle.bottom;
//
}
Затем добавьте логический метод, который получает высоту окна в этом экземпляре. Если он не соответствует оригиналу (при условии, что вы не меняете его по пути ...), клавиатура открыта.
public boolean keyopen()
{
Rect rectgle= new Rect();
Window window= getWindow();
window.getDecorView().getWindowVisibleDisplayFrame(rectgle);
int curheight= rectgle.bottom;
if (curheight!=sheight)
{
return true;
}
else
{
return false;
}
}
Frotz!
Я знаю, как точно вы можете определить, скрыта клавиатура или нет.
public int getStatusBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int getNavigationBarHeight() {
int result = 0;
int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public boolean isKeyboardHidden() {
int delta = mRootView.getRootView().getHeight() - mRootView.getHeight() - getNavigationBarHeight() - getStatusBarHeight()
- getSupportActionBar().getHeight();
return delta <= 0;
}
Это работает для планшетов. Когда панель навигации отображается горизонтально.