Як перевірити видимість клавіатури програмного забезпечення в Android?


516

Мені потрібно зробити дуже просту річ - з’ясувати, чи відображається клавіатура програмного забезпечення. Чи можливо це в Android?


9
Хоча відповідь Рубен Скреттона хороша, на планшеті здається, що вона зламана. Я замінив чек diff> 128 на diff> screenHeight / 3.
Кінгстон

2
Відповідь Рубена Скреттона була хорошою, але я вимагав від KaChi налаштування, щоб фактично її використовувати.
Cullan

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

    }

Потім у вашому класі активності ...

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 = "prilagodResize"
ajh158

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

8
Якщо ви спробуєте це, використовуючи фактичний вигляд кореня ( android.R.id.content), ви зможете впевнено сказати, що Systemзамість вашої програми є суб'єкт, що змінює свою висоту. Команді Android було б набагато безпечніше відпочити і дати нам знати хоча б основні речі про вхід SoftKeyboard.
Graeme

14
Остерігайтеся, щоб heightDiffзавжди містилася висота смуги дій. У новій відповіді, яку було проігноровано тестуванням, чи не більше цієї константи висоти, але 100 пікселів недостатньо для пристроїв xxhdpi, таких як Nexus 4. Поміркуйте про перетворення цього значення в DP, якщо ви дійсно хочете використовувати цю хакітну роботу- навколо.
Пол Ламмерцма

8
Примітка: не працює з WindowManager.LayoutParams.FLAG_FULLSCREEN і з повноекранною темою.
VAV

303

Тож сподіваємось, що це комусь допоможе.

Нова відповідь, яку дав Реубен Скреттон, є чудовою і дійсно ефективною, але вона справді працює лише в тому випадку, якщо ви встановите вікноSoftInputMode для настройкиResize. Якщо встановити його для настройки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 > 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". Дякую!
David O'Meara

1
Дякую, дякую, дякую! customResize просто не піддається моєму додатку, і ваше рішення працювало чудово.
dwemthy

1
Працює з ActionBarта ActionBarSherlock. Дуже дякую! До речі, є метод r.height():)
Дмитро Зайцев

1
Я прикладу сюди щедрування протягом 23 годин, щоб якось відзначити цю відповідь.
Дмитро Зайцев

9
heightDiff > root.getRootView().getHeight() / 4хороша цінність для роботи з пристроєм високої роздільної здатності. 100px - короткий. у Nexus 5 з 1080x1920 res, 1920 - (996-75)>? 100 = 999 1920 - (1776-75)>? 100 = 219 // клавіатура знаходиться в галактиці 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 не відповідає вашому пристрою, відтворюється зі значенням, і опублікуйте коментар, щоб усі знали, яким має бути значення ... зрештою, ми отримаємо правильне значення для всіх пристроїв!

Для отримання більш детальної інформації ознайомтеся з реалізацією на Cyborg


2
+1 Дуже дякую !! Я намагався інші відповіді, але вони не працюють. Тоді я знайшов вашу, і це працює як принадність. Дивовижний код! : D
Кевін ван Мерло

це працює, лише якщо ви додали: android: windowSoftInputMode = "stateHidden | prilagodPan" або android: windowSoftInputMode = "stateHidden | prilagodResize" Дякую !!!!
Lena Bru Bru

Ти впевнений? Якщо сервери пам'яті, я отримав події належним чином і тоді, коли для android: windowSoftInputMode було значення за замовчуванням ... єдине, що не вийшло, - це поведінка екрана, тому я вручну
зменшив

2
невелика корекція: приватний остаточний int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
binaryKarmic

2
Відмінно !! Незалежно від параметра "windowSoftInputMode" встановлено значення "prilagodPan" / "prilagodResize" / "prilagodPan | stateHidden" / "prilagodResize | 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
Пінг мене, якщо у вас виникли проблеми з цим :) Я успішно використовував його у двох проектах
Артем Зіннатуллін

Спробувавши багато прикладів вище та зіткнувшись з незначними проблемами, цей мій найкращий варіант працював для мене на багатьох різних пристроях (включаючи xxhdpi). Плюс це у власному хорошому багаторазовому класі. Я перетворив його на використання в монодроїді.
kheit

Деякі клавіатури, в деяких випадках, мають додатковий ряд спеціальних клавіш зверху (наприклад, передбачувані слова). Вони, мабуть, не є частиною самої клавіатури, оскільки використання getLastKeyboardHeightInPx()не включає висоту цього рядка. Чи знаєте ви про спосіб його врахування?
ygesher

Це працює лише в тому випадку, якщо ур готовий скомпрометувати зміни висоти макета, коли з'явиться клавіатура. правильно?
М. Усман Хан

33

Деякі вдосконалення, щоб уникнути помилкового виявлення видимості м’якої клавіатури на пристроях високої щільності:

  1. Поріг різниці висоти слід визначати як 128 dp , а не 128 пікселів .
    Зверніться до дизайнерської документації Google про метрику та сітку , 48 дп - це зручний розмір для сенсорного об'єкта, а 32 дп - мінімум для кнопок. Універсальна м'яка клавіатура повинна містити 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

Я використав трохи часу, щоб розібратися в цьому ... Я запустив це кілька 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


Вау будьте обережні, щоб вставити це у свій XML, якщо у вас немає однакової назви пакета / класу. Затемнення просто вирішує заморозити, і вам доведеться його закрити. Такий професійний продукт. / с
Спенсер Рупорт

5
@SpencerRuport, тому це безкоштовно.
Коді

@DoctorOreo - отримати IntelliJ. Це безкоштовно і не смокче.
Марк

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

Вибачте за відродження такої старої нитки коментарів. Я радий, що ти цим користуєшся і насолоджуєшся. Я люблю використовувати IntelliJ, а також AppCode для iOS і PyCharm для роботи Python. Ура!
Марк

7

Перевірка висоти елементів не є надійною, оскільки деякі клавіатури типу WifiKeyboard мають нульову висоту.

Натомість ви можете використовувати результат зворотного виклику showSoftInput () та hidSoftInput (), щоб перевірити стан клавіатури. Повна інформація та приклад коду на

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

Цей метод повертає істину, якщо клавіатура була показана перед приховуванням.


Це той, хто працює без використання висоти, і все ... Дякую ... ти врятував мій час ...
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
    }
  }
});

завжди конфігурація.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 = "коригуватиPan". Я хотів, щоб мій екран не стискався після появи м’якої клавіатури. Чи можете ви скажіть будь-яке виправлення, щоб воно працювало навіть для налаштуванняPan
Shirish Herwade

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
Не використовуйте це, воно повертається істинно, якщо клавіатура була прихована за допомогою спинки, але погляд зосереджений, принаймні, на Marshmallow
Maragues

2
завжди відповідає, що видно
jose920405

2

У мене виникли труднощі з підтримкою стану клавіатури при зміні орієнтації фрагментів у вікні перегляду. Я не впевнений, чому, але це, здається, химерно і діє не так, як у стандартній діяльності.

Щоб підтримувати стан клавіатури в цьому випадку, спочатку слід додати android:windowSoftInputMode = "stateUnchanged"його AndroidManifest.xml. Ви можете помітити, що це насправді не вирішує всю проблему - клавіатура не відкривалася для мене, якщо вона була раніше відкрита перед зміною орієнтації. У всіх інших випадках поведінка здавалася правильною.

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

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

Я зберігав це значення в onSaveInstanceStateметоді мого фрагмента і отримував його onCreate. Потім я насильно показав клавіатуру, onCreateViewякщо вона мала значення 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 () для мене завжди повертає справжнє значення, незалежно від того, клавіатура піднята чи ні
EionRobb

1

Не робіть жодного жорсткого коду. Найкращий спосіб - вам змінити розмір переглядів, перебуваючи під час фокусування на EditText за допомогою KeyBord Show. Ви можете зробити це, додавши властивість зміни розміру за активністю у файл 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 назавжди, навіть ім’я відкриється. Якщо встановлено android:windowTranslucentStatusзначення false, воно вірно поверне Height, якщо ім’я невидиме, воно поверне 1134 (не включаючи рядок стану)

Я не знаю, погода це помилка, я спробував 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

У своєму випадку я хотів би зберегти стан клавіатури перед тим, як замінити мій фрагмент. Я називаю метод hidSoftInputFromWindow від 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;
//

} 

Потім додайте булевий метод, який отримує висоту Window у цьому екземплярі. Якщо він не відповідає оригіналу (якщо припустити, що ви його не змінюєте по дорозі ...), клавіатура відкрита.

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

Фроц!


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.