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


516

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


9
Хотя ответ Рубена Скрэттона хорош, на планшете он кажется разбитым. Я заменил проверку diff> 128 на diff> screenHeight / 3.
Кингстон

2
Ответ Рубена Скрэттона был хорош, но мне потребовалась настройка КаЧи, чтобы реально использовать его.
Каллан

1
Почему Google не делает стандартный встроенный метод работающим для всех приложений клавиатуры?
Фрукты

4
Он по- прежнему братья меня, что это не система функционирует ...
Longi

1
Абсолютно безумно, что этот API все еще отсутствует 10 лет спустя . Очень рад, что я отошел от Android.
Рувим Скреттон

Ответы:


674

НОВЫЙ ОТВЕТ добавлен 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
    }

    ...
}

62
Это не работало для меня, пока я не понял, что вы должны установить следующий атрибут в вашей активности: android: windowSoftInputMode = "AdjustResize"
ajh158

9
Кажется, делает трюк. Кроме того, если вы не знаете идентификатор корневого представления, вот как вы можете получить представление:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Голдсмит

8
Если вы попробуете это с использованием действительного корневого представления ( android.R.id.content), вы сможете с большей уверенностью сказать, что Systemобъект, а не ваше приложение, изменяет свою высоту. Для команды Android было бы намного безопаснее дать нам передышку и сообщить нам хотя бы основные сведения о вводе SoftKeyboard.
Грэм

14
Помните, что heightDiffвсегда будет указана высота панели действий. В новом ответе, который был проигнорирован при тестировании, если эта высота больше некоторой константы, но 100 пикселей недостаточно для устройств xxhdpi, таких как Nexus 4. Рассмотрите возможность преобразования этого значения в DP, если вы действительно хотите использовать эту хакерскую работу. около.
Пол Ламмерцма

8
Примечание: не работает с WindowManager.LayoutParams.FLAG_FULLSCREEN и с полноэкранной темой.
VAV

303

Надеюсь, это поможет кому-то.

Новый ответ, который дал Рубен Скраттон, великолепен и действительно эффективен, но он действительно работает, только если вы установили свой 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
    }
 }
}); 

1
Это тот, который работал для меня. Я пытался определить состояние клавиатуры из пользовательского интерфейса, TwoDScrollerViewпохожего на stackoverflow.com/a/5224088/530513, хотя и с масштабированием. Ребенок был не простой, ImageViewа пользовательской раскладкой (расширяет RelativeLayout), но не смог обнаружить клавиатуру, используя рекомендованное решение, несмотря на настройки android:windowSoftInputMode="adjustResize". Спасибо!
Дэвид О'Мира

1
Спасибо Спасибо спасибо! AdjustResize просто не подходит для моего приложения, и ваше решение работает отлично.
dwemthy

1
Работает с ActionBarи ActionBarSherlock. Большое спасибо! Кстати, есть способ r.height():)
Дмитрий Зайцев

1
Я приложу вознаграждение здесь в течение 23 часов, чтобы как-то отметить этот ответ.
Дмитрий Зайцев

9
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 недостаточно хорошо.
Flask_KR

55

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

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

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

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


2
+1 Спасибо большое !! Я пробовал другие ответы, но они не работают. Потом я нашел твой, и он работает как шарм. Отличный код! : D
Кевин ван Мирло

это работает, только если вы добавляете: android: windowSoftInputMode = "stateHidden | AdjustPan" или android: windowSoftInputMode = "stateHidden | AdjustResize" Спасибо !!!!
Лена Брю

Ты уверен? Если серверы памяти, я получал события должным образом также, когда android: windowSoftInputMode имел свое значение по умолчанию ... единственное, что не сработало, это поведение экрана, поэтому я вручную
сжал

2
небольшая поправка: private final int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
binaryKarmic

2
Отлично!! Независимо от того, установлено ли для "windowSoftInputMode" значение "AdjustPan" / "AdjustResize" / "AdjustPan | StateHidden" / "AdjustResize | StateHidden", или даже без этой опции, это всегда работает! Протестировано на XiaoMi 8.
Чжоу Хунбо

52

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

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

2
Класс не маленький, но реализация, безусловно, есть :). Спасибо, я попробую это :)
Atul O Holic

1
Звоните мне, если у вас возникли проблемы с этим :) Я успешно использовал его в 2 проектах
Артем Зиннатуллин

Попробовав много примеров, приведенных выше, и столкнувшись с незначительными проблемами, этот лучше всего работал для меня на разных устройствах (включая xxhdpi). Плюс это в своем собственном хорошем классе многоразового использования. Я преобразовал его для использования в моно дроид.
Хейт

Некоторые клавиатуры, в некоторых случаях, имеют дополнительный ряд пользовательских клавиш сверху (например, предсказанные слова). Они, очевидно, не являются частью самой клавиатуры, так как использование getLastKeyboardHeightInPx()строки не включает высоту этого ряда. Знаете ли вы, как принять это во внимание?
ygesher

Это работает только в том случае, если вы готовы скомпрометировать изменение высоты макета при появлении клавиатуры. правильно?
М. Усман Хан

34

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

  1. Порог разности высот должен быть определен как 128 dp , а не 128 пикселей .
    Обратитесь к Google Design Doc о метриках и сетке , 48 dp - удобный размер для сенсорного объекта и 32 dp - минимум для кнопок. Стандартная программная клавиатура должна включать 4 ряда клавиш, поэтому минимальная высота клавиатуры должна быть: 32 dp * 4 = 128 dp , что означает, что пороговый размер должен передаваться в пикселях путем умножения плотности устройства. Для устройств xxxhdpi (плотность 4) порог высоты программной клавиатуры должен составлять 128 * 4 = 512 пикселей.

  2. Разница высот между корневым представлением и его видимой областью:
    высота корневого представления - высота строки состояния - видимая высота рамки = нижняя часть корневого представления - нижняя часть видимой рамки, поскольку высота строки состояния равна верхней части видимой рамки корневого представления.

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

4
Это заслуживает того, чтобы быть принятым ответом. Игнорирование плотности дало мне очень разные результаты на устройствах с разными форм-факторами, но с одинаковыми размерами пикселей. Спасибо!
Джинджер МакМюррей

2
Спасибо ... это лучшее состояние!
TacB0sS

1
Отлично. Метод isKeyboardShown () - это то, что нам нужно. Спасибо
Данило Волох

Это работает и в 2020 году. Идеальный ответ. Я перепробовал весь код, но на этом работает отлично.
Митеш Джайн

8

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


Будьте осторожны, вставив это в ваш XML, если у вас нет одинакового имени пакета / класса. Затмение просто решает заморозить, и вы должны выключить его. Такой профессиональный продукт. / s
Спенсер Рупорт

5
@SpencerRuport, поэтому он бесплатный.
Коди

@DoctorOreo - получить IntelliJ. Это бесплатно и не сосет.
Марк

@Mark - через несколько месяцев после публикации я действительно попробовал IntelliJ. Это намного лучше, IMO, чем Eclipse. Все их продукты (по большей части), я думаю, превосходны. Я даже купил несколько.
Коди

Извините за возрождение такой старой ветки комментариев. Я рад, что вы используете это и наслаждаетесь этим. Я люблю использовать IntelliJ, а также AppCode для iOS и PyCharm для Python. Ура!
Марк

7

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

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

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


5

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

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

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


Это тот, который работает без использования высоты и все ... Спасибо ... ты сэкономил мое время ...
Vji

4

Я обнаружил, что комбинация метода @ 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
    }
  }
});

всегда Configuration.KEYBOARDHIDDEN_NO.
Fantouch

4

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

4

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

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

Это не работает для Android: windowSoftInputMode = "AdjustPan". Я хотел, чтобы мой экран не уменьшался после появления мягкой клавиатуры. Можете ли вы сказать какое-либо исправление, чтобы оно работало даже для AdjustPan
Шириш Хервейд

4

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


4

Для этого может помочь скрытый метод 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()
  }
}

Если кому-то это понадобится - это работает и в Xamarin, имя метода точно такое же, и к нему нужно обращаться одинаково - через свойство Class в InputMethodManager.
Константин Севери

Будьте осторожны при использовании этого, это неподдерживаемый API (он скрыт по причине), и для начала он не работает на KitKat.
Даниэле Риччи

3

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

Почему подобное решение от stackoverflow.com/a/18992807/2914140 не работает для вас и чем ваше решение отличается?
CoolMind

3

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

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

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

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

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


3

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

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
ranjith

2
вместо того, чтобы создавать и использовать R.id.activityRoot, вы можете просто использовать android.R.id.contentименно то, что вам нужно.
Марцин Орловски

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

Меня устраивает.


3

Попробуй это:

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

2

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

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

Примечание: используйте только один из «зарегистрированных» вызовов. Все они работают одинаково и только для удобства


2

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

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

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

1
Не используйте это, оно возвращает true, если клавиатура была скрыта с помощью
кнопки

2
всегда отвечает что видно
jose920405

2

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

Чтобы сохранить состояние клавиатуры в этом случае, сначала вы должны добавить 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, если клавиатура видна до фактического ее скрытия до уничтожения фрагмента).


1

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

// 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.isActive () всегда возвращает true для меня, независимо от того,
включена

1

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

android:windowSoftInputMode="adjustResize"


1

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

Хитрость в том, что вы пытаетесь скрыть или показать программную клавиатуру и зафиксировать результат этой попытки.
Никакой паники, это не совсем показывает или скрывает клавиатуру. Мы просто просим о состоянии.

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

Вы найдете реализацию здесь: https://stackoverflow.com/a/27567074/2525452


1

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

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

}

всегда отвечает что видно
jose920405

0

Новый ответ Рубена Скрэттона (рассчитать 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
    }
 }
}); 

0

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

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

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


0

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

Спасибо,


0
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!


0

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.