Как проверить видимость программной клавиатуры в Android?
Мне нужно сделать очень простую вещь - выяснить, отображается ли программная клавиатура. Возможно ли это в Android?
46 ответов
Существует прямой способ выяснить это. И это не требует каких-либо изменений макета.
Таким образом, он работает и в полноэкранном режиме с погружением.
Хитрость заключается в том, что вы пытаетесь скрыть или показать программную клавиатуру и зафиксировать результат этой попытки.
Никакой паники, это не совсем показывает или скрывает клавиатуру. Мы просто просим о состоянии.
Чтобы оставаться в курсе, вы можете просто повторить операцию, например, каждые 200 миллисекунд, используя обработчик.
Вы найдете реализацию здесь: /questions/40931761/kak-opredelit-vidna-li-programmnaya-klaviatura-na-ustrojstve-android/40931785#40931785
Новый ответ Рубена Скрэттона (вычислите HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
) не будет работать в действии, если вы установите режим полупрозрачной строки состояния.
если вы используете полупрозрачную строку состояния, activityRootView.getHeight()
никогда не изменит погоду, видна мягкая клавиатура. он всегда будет возвращать высоту активности и статусную строку.
Например, Nexus 4, Android 5.0.1, установить android:windowTranslucentStatus
правда, он вернет 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
, который закрывает клавиатуру и возвращает мне, была ли клавиатура видимой или нет.
Этот метод прост, но может изменить состояние вашей клавиатуры.
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!
View#setOnApplyWindowInsetsListener можно использовать для получения обратного вызова вставок окна
public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) {
getListenerInfo().mOnApplyWindowInsetsListener = listener;
}
//OnApplyWindowInsetsListener
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets);
А также
boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime())
может дать состояние видимости.
Это, вероятно, не подходит для производства, потому что откроет клавиатуру. Обратите внимание, что логическое значение, возвращаемое аналогичными функциями, не указано в API и поэтому ненадежно. Обратитесь к документации здесь ...
public boolean showSoftInput (View view,
int flags,
ResultReceiver resultReceiver)
Обратите внимание, что этот метод принимает ResultReceiver. Он может получить результаты: RESULT_UNCHANGED_SHOWN, RESULT_UNCHANGED_HIDDEN, RESULT_SHOWN или RESULT_HIDDEN. Если вы получили RESULT_UNCHANGED_SHOWN, клавиатура была видна. Если вам нужно, чтобы он оставался закрытым, если он был закрыт, вам нужно будет закрыть его.
Этот код прекрасно работает
используйте этот класс для просмотра root:
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);
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}
public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}
@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);
}
}
и установите слушателя клавиатуры в деятельности или фрагмент:
rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
@Override
public void onKeyboardVisibility(boolean isVisible) {
}
});
Может быть, это поможет вам:
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
Вот обходной путь, чтобы узнать, видна ли программная клавиша.
- Проверьте наличие запущенных служб в системе с помощью ActivityManager.getRunningServices(max_count_of_services);
- Из возвращенных экземпляров ActivityManager.RunningServiceInfo проверьте значение clientCount для службы программной клавиатуры.
- Вышеупомянутый clientCount будет увеличиваться каждый раз, когда отображается программная клавиатура. Например, если clientCount изначально был 1, он будет 2, когда отображается клавиатура.
- При отклонении клавиатуры значение clientCount уменьшается. В этом случае он сбрасывается до 1.
У некоторых популярных клавиатур есть определенные ключевые слова в их classNames:
Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
Smart = Keyboard (SmartKeyboard)
Из ActivityManager.RunningServiceInfo проверьте наличие вышеупомянутых шаблонов в ClassNames. Кроме того, ActivityManager.RunningServiceInfo's clientPackage= android указывает, что клавиатура привязана к системе.
Вышеупомянутая информация может быть объединена для строгого способа выяснить, видна ли программная клавиатура.
Я знаю, что это старый пост, но я думаю, что это самый простой из известных мне подходов, и моим тестовым устройством является 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 возвращает логическое значение.
Спасибо,
Ссылаясь на этот ответ @TacB0sS, я разработал один класс в Kotlin. Надеюсь, это будет полезно. Дайте мне знать, если это требует улучшения.
class KeyboardVisibilityObserver(val layRootContainer: View?, val keyboardVisibilityListener: KeyboardVisibilityListener?) {
var isKeyboardOpen = false
private set
private var keyBoardObserver = object : ViewTreeObserver.OnGlobalLayoutListener {
private val DefaultKeyboardDP = 100
// Lollipop includes button bar in the root. Add height of button bar (48dp) to maxDiff
private val EstimatedKeyboardDP = DefaultKeyboardDP + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 48 else 0
private val r = Rect()
override fun onGlobalLayout() {
if (layRootContainer != null) {
// Convert the dp to pixels.
val estimatedKeyboardHeight = TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP.toFloat(), layRootContainer.resources.displayMetrics).toInt()
// Conclude whether the keyboard is shown or not.
layRootContainer.getWindowVisibleDisplayFrame(r)
val heightDiff = layRootContainer.rootView.height - (r.bottom - r.top)
val isShown = heightDiff >= estimatedKeyboardHeight
if (isShown == isKeyboardOpen) {
// Log.d("Keyboard state", "Ignoring global layout change...");
return
}
isKeyboardOpen = isShown
keyboardVisibilityListener?.onKeyboardVisibilityChanged(isKeyboardOpen)
}
}
}
init {
layRootContainer?.viewTreeObserver?.addOnGlobalLayoutListener(keyBoardObserver)
}
// call this in onDestroy
fun removeObserver(){
layRootContainer?.viewTreeObserver?.removeOnGlobalLayoutListener(keyBoardObserver)
}
interface KeyboardVisibilityListener {
fun onKeyboardVisibilityChanged(isKeyboardOpen: Boolean)
}
}
Я знаю, как точно вы можете определить, скрыта клавиатура или нет.
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;
}
Это работает для планшетов. Когда панель навигации отображается горизонтально.
Решение, предложенное Reuben Scratton и Kachi, похоже, основывается на плотности пикселей устройств, если у вас устройство с высокой плотностью, разница в высоте может быть больше 100, даже если клавиатура не нажата. Немного поработаем, чтобы увидеть начальную разницу высот (с клавиатурой вниз), а затем сравнить с текущей разницей.
boolean isOpened = false;
int firstHeightDiff = -1;
public void setListenerToRootView(){
final View activityRootView = getActivity().getWindow().getDecorView().findViewById(android.R.id.content);
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
firstHeightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (isAdded()) {
Rect r = new Rect();
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
isOpened = heightDiff>firstHeightDiff+100;
if (isAdded())
if(isOpened) {
//TODO stuff for when it is up
} else {
//TODO stuf for when it is down
}
}
}
});
}
В дополнение к правильному ответу мне пришлось добавить это в конец onCreateView при использовании веб-просмотра внутри фрагмента.
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
Может быть, из-за того, что я запускаю Webview внутри фрагмента или, может быть, новое поведение API 30, моя проблема заключалась в том, что высота фрагмента никогда не изменялась, даже если отображалась клавиатура.
Итак, для фрагмента весь код должен быть
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
//mWebView.postUrl("https://www.google.com/");
final View activityRootView = view;
layoutListener = 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);
// This variable was created only for Debug purposes and
// to see the height change when clicking on a field inside mWebView
int screenHeight = activityRootView.getRootView().getHeight();
Log.d("onGlobalLayout", "rect: " + r.toString());
Log.d("onGlobalLayout", "screenHeight: " + screenHeight);
//The difference on the heights from bottom to top and on the root height
int heightDiff = screenHeight - (r.bottom - r.top);
Log.d("onGlobalLayout", "heightDiff: " + heightDiff);
//I suggest to put 250 on resources to have better order
float dpx = dpToPx(getActivity(), 250);
if (previousHeightDiff != heightDiff) {
if (heightDiff > dpx) {
isSoftKeyboardPresent = true;
} else {
isSoftKeyboardPresent = false;
}
previousHeightDiff = heightDiff;
}
}
};
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(layoutListener);
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
return view;
}
private static float dpToPx(Context context, float valueInDp) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
activity_main.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
activity_main.getWindowVisibleDisplayFrame(r);
int screenHeight = activity_main.getRootView().getHeight();
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;
Log.d("", "keypadHeight = " + keypadHeight);
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
AppLog.e("keyboard open", "keyboard open");
} else {
AppLog.e("keyboard closed", "keyboard closed");
// keyboard is closed
}
}
});
InputMethodManager содержит информацию о программной клавиатуре. Вы получаете это от деятельности через:
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
Вы можете поиграть с этим, чтобы увидеть, что он может вам сказать. Вы можете использовать его, чтобы показать или скрыть программный ввод...