Как проверить видимость программной клавиатуры в Android?

Мне нужно сделать очень простую вещь - узнать, показана ли клавиатура программного обеспечения. Возможно ли это в Android?

Ответ 1

NEW ANSWER добавлено 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 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 Manifest 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);       
    }

    }

Затем в вашем классе Activity...

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
    }

    ...
}

Ответ 2

Итак, надеюсь, это поможет кому-то выйти.

Новый ответ, который дал Реубен Скрэттон, великолепный и действительно эффективный, но он действительно работает, только если вы установите для параметра windowSoftInputMode значение adjustResize. Если вы установите его для настройкиPan, все еще невозможно определить, будет ли клавиатура видна с помощью его фрагмента кода. Чтобы обойти это, я сделал эту крошечную модификацию вышеприведенного кода.

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
    }
 }
}); 

Ответ 3

Это было всегда с точки зрения компьютера, но этот вопрос все еще невероятно актуален!

Итак, я взял приведенные выше ответы и немного их объединил и уточнил...

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 не подходит для вашего устройства, играйте со значением и оставьте комментарий, чтобы все знали, каково должно быть значение... в конце концов мы получим правильное значение для всех устройств!

Для более подробной информации, проверьте реализацию на Киборг

Ответ 4

Извините за поздний ответ, но я создал небольшой вспомогательный класс для работы с открытыми/закрытыми событиями с уведомлением слушателей и другими полезными вещами, может быть, кто-то сочтет это полезным:

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

Ответ 5

Некоторые улучшения во избежание ошибочного обнаружения видимости мягкой клавиатуры на устройствах с высокой плотностью:

  • Порог разницы высот должен быть определен как 128 dp, а не 128 пикселей.
    Обратитесь к проекту дизайна Google по метрикам и сетке, 48 дп - удобный размер для сенсорного объекта, а 32 дп - для кнопок. Обычная мягкая клавиатура должна включать в себя 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;
    }
    

Ответ 6

Я потратил немного времени, чтобы понять это... Я запустил несколько 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

Ответ 7

Проверка высоты элементов не надежна, потому что некоторые клавиатуры, такие как WifiKeyboard, имеют нулевую высоту.

Вместо этого вы можете использовать результат обратного вызова showSoftInput() и hideSoftInput() для проверки состояния клавиатуры. Полная информация и пример кода на

https://rogerkeays.com/how-to-check-if-the-software-keyboard-is-shown-in-android

Ответ 8

Идея состоит в том, что если вам нужно скрыть клавиатуру и проверить состояние мягкого ввода в одно и то же время, используйте следующее решение:

public boolean hideSoftInput() {
    InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
    return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);
}

Этот метод возвращает true, если клавиатура была показана перед скрытием.

Ответ 9

Вместо того, чтобы предполагать разностное кодирование, я сделал что-то вроде этого, поскольку у меня были опции меню в моем приложении.

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
        }
     }
});

Ответ 10

Вы можете наблюдать скрытую скрытую клавиатуру с помощью функции 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);
    }
}

Ответ 11

Я обнаружил, что комбинация метода @Reuben_Scratton вместе с методом @Yogesh работает лучше всего. Объединение их методов даст что-то вроде этого:

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
    }
  }
});

Ответ 12

Существует также решение с системными вставками, но оно работает только с 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.

Ответ 13

Там может помочь скрытый метод, 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()
  }
}

Ответ 14

Я использовал небольшой вариант ответа Reuban, который оказался более полезным при определенных обстоятельствах, особенно с устройствами с высоким разрешением.

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
                }
            }
        });

Ответ 15

Ни одно из этих решений не будет работать для 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
    }
  }
});

Ответ 16

Это было навсегда с точки зрения компьютера, но этот вопрос по-прежнему невероятно уместен! Поэтому я принял приведенные выше ответы и немного их усовершенствовал...

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);
        }
    });
}

Это работает для меня.

Ответ 17

Мой ответ в основном такой же, как ответ Качи, но я завернул его в класс помощника, чтобы очистить способ, которым он пользовался в моем приложении.

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
                    }
                }
            });

Примечание. Используйте только один из вызовов "register". Все они работают одинаково и доступны только для удобства.

Ответ 18

вы можете попробовать это, отлично поработайте для меня:

InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}

Ответ 19

Я только что столкнулся с ошибкой при использовании большинства вышеперечисленных решений, которые предлагают добавить фиксированное число.

S4 имеет высокий dpi, что приводит к тому, что высота полосы навигации составляет 100 пикселей, поэтому мое приложение думает, что клавиатура открыта все время.

Итак, когда все новые телефоны с высоким разрешением выпущены, я считаю, что использование жесткого кодированного значения не является хорошей идеей для долгосрочного использования.

Лучший подход, который я нашел после некоторого тестирования на разных экранах и устройствах, должен был использовать процент. Получите разницу между содержимым decorView и ur, а затем проверьте, какой процент от этой разницы. Из статистики, которую я получил, большинство навигационных бар (независимо от размера, разрешения и т.д.) Будут занимать от 3% до 5% экрана. Там, где клавиатура открыта, она занимает от 47% до 55% экрана.

В качестве вывода мое решение состояло в том, чтобы проверить, превышает ли diff более 10%, тогда я предполагаю, что его клавиатура открыта.

Ответ 20

У меня возникли проблемы с сохранением состояния клавиатуры при изменении ориентации фрагментов внутри viewpager. Я не уверен, почему, но он просто кажется неуклюжим и действует иначе, чем стандартная активность.

Чтобы сохранить состояние клавиатуры в этом случае, сначала вы должны добавить android:windowSoftInputMode = "stateUnchanged" в свой AndroidManifest.xml. Вы можете заметить, однако, что это фактически не решает всей проблемы - клавиатура не открылась для меня, если она была ранее открыта перед изменением ориентации. Во всех остальных случаях поведение казалось правильным.

Тогда нам нужно реализовать одно из упомянутых здесь решений. Самый чистый, который я нашел, был Джордж Майсурадзе - используйте логический обратный вызов из hideSoftInputFromWindow:

InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
return imm.hideSoftInputFromWindow(mViewPager.getWindowToken(), 0);

Я сохранил это значение в моем методе Fragment onSaveInstanceState и получил его onCreate. Затем я принудительно показал клавиатуру в onCreateView, если она имела значение true (она возвращает true, если клавиатура видна до фактического скрытия ее до разрушения фрагмента).

Ответ 21

Попробуйте следующее:

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 ... \\
        }
    }
});

Ответ 22

Вот мое решение, и оно работает. Вместо того, чтобы искать размер пикселя, просто проверьте, изменилась ли высота представления контента или нет:

// 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();
}

Ответ 23

Не делайте жесткий код. Лучший способ - вы должны изменить размер своих просмотров, а в режиме "Фокус на EditText" с помощью KeyBord Show. Вы можете сделать это, добавив свойство resize в действие в файл манифеста, используя код ниже.

android:windowSoftInputMode="adjustResize"

Ответ 24

Существует прямой способ найти это. И это не требует каких-либо изменений макета.
Таким образом, он также работает в полноэкранном режиме. Фокус в том, что вы пытаетесь скрыть или показать мягкую клавиатуру и зафиксировать результат этой попытки.
Нет паники, на самом деле это не показывает или не скрывает клавиатуру. Мы просто просим государство.

Чтобы оставаться в курсе последних событий, вы можете просто повторить операцию, например. каждые 200 миллисекунд, используя обработчик.

Здесь вы найдете реализацию: fooobar.com/questions/16394/...

Ответ 25

Я думаю, что этот метод поможет вам узнать, является ли keybord видимым или нет.

 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;
    }

}

Ответ 26

Новый ответ Reuben Scratton (расчет HeightDiff int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();) не будет работать в активности, если вы установите режим полупрозрачной строки состояния.

Если вы используете полупрозрачную строку состояния, activityRootView.getHeight() никогда не изменит погоду, будет видна мягкая клавиатура. он всегда будет возвращать высоту активности и строку состояния.

Например, Nexus 4, Android 5.0.1, установите android:windowTranslucentStatus в true, он вернет 1184 навсегда, даже у ime есть opend. Если вы установите для параметра android:windowTranslucentStatus значение false, оно вернет высоту правильно, если ime невидимо, оно вернет 1134 (не включает строку состояния).close ime, он вернет 5xx, возможно (зависит от высоты ime)

Я не знаю погоды, это ошибка, я стараюсь 4.4.4 и 5.0.1, результат такой же.

Итак, до сих пор второй наиболее согласованный ответ, решение Kachi будет самым безопасным способом расчета высоты ime. Вот копия:

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
    }
 }
}); 

Ответ 27

Метод, который не нуждается в LayoutListener

В моем случае я хотел бы сохранить состояние клавиатуры перед заменой моего фрагмента. Я вызываю метод hideSoftInputFromWindow из onSaveInstanceState, который закрывает клавиатуру и возвращает мне, была ли клавиатура видимой или нет.

Этот метод прост, но может изменить состояние вашей клавиатуры.

Ответ 28

Я знаю, что это старый пост, но я думаю, что это самый простой подход, который я знаю, и моим тестовым устройством является 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 возвращает значение boolean.

Спасибо,

Ответ 29

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!

Ответ 30

Я знаю, насколько точно вы можете определить, скрыта ли клавиатура или нет.

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;
}

Это работает для планшетов. Когда навигационная панель отображается горизонтально.