Jak sprawdzić widoczność klawiatury oprogramowania w systemie Android?

516

Muszę zrobić bardzo prostą rzecz - dowiedzieć się, czy pokazana jest klawiatura oprogramowania. Czy to możliwe w Androidzie?

fucho
źródło
9
Chociaż odpowiedź Reubena Scrattona jest dobra, wydaje się, że jest uszkodzona na tablecie. Zamieniłem check diff> 128 na diff> screenHeight / 3.
kingston,
2
Odpowiedź Reubena Scrattona była dobra, ale potrzebowałem dostosowania KaChi, aby z niego skorzystać.
Cullan
1
Dlaczego Google nie sprawia, że ​​standardowa metoda wbudowana działa dla wszystkich aplikacji na klawiaturze?
Fruit
4
Wciąż mnie braci, że to nie funkcje systemu ...
longi
1
To absolutnie szalone, że ten interfejs API wciąż brakuje 10 lat później . Bardzo się cieszę, że zrezygnowałem z Androida.
Reuben Scratton

Odpowiedzi:

674

NOWA ODPOWIEDŹ dodano 25 stycznia 2012 r

Od czasu napisania poniższej odpowiedzi, ktoś przywiódł mnie do istnienia ViewTreeObserver i przyjaciół, interfejsów API, które czają się w SDK od wersji 1.

Zamiast wymagać niestandardowego typu układu, o wiele prostszym rozwiązaniem jest nadanie @+id/activityRootwidokowi głównemu aktywności znanego identyfikatora, powiedzmy , podpięcie GlobalLayoutListener do ViewTreeObserver i stamtąd obliczyć różnicę rozmiaru między katalogiem głównym widoku aktywności a rozmiarem okna:

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

Korzystanie z narzędzia, takiego jak:

public static float dpToPx(Context context, float valueInDp) {
    DisplayMetrics metrics = context.getResources().getDisplayMetrics();
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}

Łatwy!

Uwaga: Twoja aplikacja musi ustawić tę flagę w Manifeście Androida, w android:windowSoftInputMode="adjustResize"przeciwnym razie powyższe rozwiązanie nie będzie działać.

ORYGINALNA ODPOWIEDŹ

Tak, jest to możliwe, ale jest o wiele trudniejsze niż powinno być.

Jeśli muszę się martwić, kiedy klawiatura pojawia się i znika (co jest dość często), to dostosowuję moją klasę układu najwyższego poziomu do tej, która zastępuje onMeasure(). Podstawowa logika jest taka, że ​​jeśli układ wypełnia się znacznie mniej niż całkowity obszar okna, prawdopodobnie pojawia się miękka klawiatura.

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

    }

Następnie w klasie aktywności ...

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
    }

    ...
}
Reuben Scratton
źródło
62
Nie działało to dla mnie, dopóki nie zdałem sobie sprawy, że musisz ustawić następujący atrybut dla swojej aktywności: android: windowSoftInputMode = "adjustResize"
ajh158
9
Wydaje się, że robi to samo. Ponadto, jeśli nie znasz identyfikatora widoku głównego, oto jak możesz uzyskać widok:((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
Goldsmith
8
Jeśli spróbujesz tego za pomocą rzeczywistego widoku głównego ( android.R.id.content), będziesz w stanie z większą pewnością powiedzieć, że Systemzamiast aplikacji zmienia się jej wysokość. Byłoby o wiele bezpieczniej dla zespołu Androida, aby dać nam przerwę i dać nam znać przynajmniej podstawowe rzeczy na temat wejścia SoftKeyboard.
Graeme
14
Uwaga, heightDiffzawsze będzie zawierać wysokość paska akcji. W nowej odpowiedzi, która została zignorowana przez testowanie, czy ta wysokość jest większa niż jakaś stała, ale 100 pikseli nie jest wystarczająca dla urządzeń xxhdpi, takich jak Nexus 4. Rozważ przekonwertowanie tej wartości na DP, jeśli naprawdę chcesz użyć tej cholernej pracy - na około.
Paul Lammertsma
8
Uwaga: nie działa z WindowManager.LayoutParams.FLAG_FULLSCREEN iz motywem pełnoekranowym.
VAV
303

Mam nadzieję, że to komuś pomoże.

Nowa odpowiedź, którą udzielił Reuben Scratton, jest świetna i bardzo wydajna, ale naprawdę działa tylko wtedy, gdy ustawisz parametr windowSoftInputMode na wartość dostosowywania. Jeśli ustawisz opcję adjustPan, nadal nie można wykryć, czy klawiatura jest widoczna za pomocą fragmentu kodu. Aby obejść ten problem, wprowadziłem niewielką modyfikację powyższego kodu.

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
    }
 }
}); 
Kachi
źródło
1
Ten działał dla mnie. Próbowałem wykryć stan klawiatury z niestandardowego TwoDScrollerViewpodobnego do stackoverflow.com/a/5224088/530513, chociaż z powiększaniem. Dziecko nie było proste, ImageViewale układ niestandardowy (rozszerza się RelativeLayout), ale mimo ustawienia nie było w stanie wykryć klawiatury przy użyciu zalecanego rozwiązania android:windowSoftInputMode="adjustResize". Dzięki!
David O'Meara,
1
Dziekuję Dziekuję Dziękuję! AdjustResize po prostu nie jest wykonalne dla mojej aplikacji, a twoje rozwiązanie działało idealnie.
dwemthy
1
Współpracuje z ActionBari ActionBarSherlock. Wielkie dzięki! Nawiasem mówiąc, istnieje metoda r.height():)
Dmitrij Zajcew
1
Załączę nagrodę tutaj w ciągu 23 godzin, aby jakoś zaznaczyć tę odpowiedź.
Dmitrij Zajcew
9
heightDiff > root.getRootView().getHeight() / 4ma dobrą wartość do pracy z urządzeniem o wysokiej rozdzielczości. 100px jest za krótkie. w Nexusie 5 z rozdzielczością 1080x1920, 1920 - (996-75)>? 100 = 999 1920 - (1776–75)>? 100 = 219 // klawiatura jest w Galaxy S2 z rozdzielczością 480x800, 800 - (800-38)>? 100 = 38 800 - (410-38)>? 100 = 428 // klawiatura jest uruchomiona, więc magiczna liczba 100px nie jest wystarczająco dobra.
Flask_KR
55

Od zawsze było to związane z komputerem, ale to pytanie jest nadal niewiarygodnie istotne!

Więc wziąłem powyższe odpowiedzi i połączyłem je i udoskonaliłem trochę ...

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

Pracuje dla mnie :)

UWAGA: Jeśli zauważysz, że DefaultKeyboardDP nie pasuje do twojego urządzenia, zagraj z tą wartością i opublikuj komentarz, aby wszyscy wiedzieli, jaka powinna być wartość ... w końcu otrzymamy prawidłową wartość, która pasuje do wszystkich urządzeń!

Aby uzyskać więcej informacji, sprawdź wdrożenie na Cyborgu

TacB0sS
źródło
2
+1 Dziękuję bardzo !! Próbowałem innych odpowiedzi, ale one nie działają. Potem znalazłem twój i działa jak urok. Świetny kod! : D
Kevin van Mierlo
działa to tylko wtedy, gdy dodasz: android: windowSoftInputMode = "stateHidden | adjustPan" lub android: windowSoftInputMode = "stateHidden | adjustResize" Dziękuję !!!!
Lena Bru,
Jesteś pewny? Jeśli serwery pamięci poprawnie dostaję zdarzenia również wtedy, gdy android: windowSoftInputMode miał swoją wartość domyślną ... jedyne, co nie zadziałało, to zachowanie ekranu, więc ręcznie go zmniejszyłem ...
TacB0sS
2
mała poprawka: prywatny końcowy int EstimatedKeyboardDP = DefaultKeyboardDP + (Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP? 48: 0);
binaryKarmic
2
Świetny!! Bez względu na to, czy „windowSoftInputMode” jest ustawione na „AdjustPan” / „adjustResize” / „adjustPan | stateHidden” / „adjustResize | stateHidden”, a nawet bez tej opcji, zawsze działa! Testowane na XiaoMi 8.
Zhou Hongbo,
52

Przepraszam za późną odpowiedź, ale stworzyłem małą klasę pomocnika do obsługi zdarzeń otwierania / zamykania z powiadamianiem słuchaczy i innych przydatnych rzeczy, być może ktoś uznałby to za pomocne:

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

Przykład użycia:

final SoftKeyboardStateWatcher softKeyboardStateWatcher 
    = new SoftKeyboardStateWatcher(findViewById(R.id.activity_main_layout);

// Add listener
softKeyboardStateWatcher.addSoftKeyboardStateListener(...);
// then just handle callbacks
Artem Zinnatullin
źródło
2
Klasa nie jest mała, ale implementacja na pewno jest :). Dzięki, spróbuję tego :)
Atul O Holic
1
Ping mnie, jeśli masz z tym problemy :) Z powodzeniem wykorzystałem go w 2 projektach
Artem Zinnatullin
Po wypróbowaniu wielu powyższych przykładów i napotkaniu drobnych problemów, ten był najlepszy dla mnie na wielu różnych urządzeniach (w tym xxhdpi). Plus jest w swojej ładnej klasie wielokrotnego użytku. Przekształciłem go do użycia w mono droidie.
kheit
Niektóre klawiatury, czasami, mają dodatkowy rząd niestandardowych klawiszy na górze (na przykład przewidywane słowa). Najwyraźniej nie są one częścią samej klawiatury, ponieważ użycie getLastKeyboardHeightInPx()nie obejmuje wysokości tego rzędu. Czy znasz także sposób, aby wziąć to pod uwagę?
ygesher
Działa to tylko wtedy, gdy jesteś gotowy na kompromis Zmiana wysokości układu, gdy pojawi się klawiatura. dobrze?
M. Usman Khan,
33

Niektóre ulepszenia, aby uniknąć nieprawidłowego wykrycia widoczności miękkiej klawiatury na urządzeniach o wysokiej gęstości:

  1. Próg różnicy wysokości należy zdefiniować jako 128 dp , a nie 128 pikseli .
    Zapoznaj się z dokumentacją projektową Google o metrykach i siatce , 48 dp to wygodny rozmiar dla obiektu dotykowego, a 32 dp to minimum dla przycisków. Ogólna miękka klawiatura powinna zawierać 4 rzędy klawiszy, więc minimalna wysokość klawiatury powinna wynosić: 32 dp * 4 = 128 dp , co oznacza, że ​​rozmiar progu powinien być przenoszony na piksele przez pomnożenie gęstości urządzenia. W przypadku urządzeń xxxhdpi (gęstość 4) próg wysokości klawiatury miękkiej powinien wynosić 128 * 4 = 512 pikseli.

  2. Różnica wysokości między widokiem korzenia a jego widocznym obszarem:
    wysokość widoku korzenia - wysokość paska stanu - widoczna wysokość ramki = dół widoku korzenia - widoczne dno ramy, ponieważ wysokość paska stanu jest równa górze widocznej ramki widoku korzenia.

    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;
    }
Orchard Cafe
źródło
4
To zasługuje na zaakceptowaną odpowiedź. Zignorowanie gęstości dało mi bardzo różne wyniki na urządzeniach o różnych kształtach, ale o podobnych rozmiarach pikseli. Dzięki!
Ginger McMurray,
2
Dziękuję ... to lepszy stan!
TacB0sS,
1
Świetny. Potrzebujemy metody isKeyboardShown (). Dzięki
Danylo Volokh
Działa również w 2020 roku. Idealna odpowiedź. Próbowałem całego kodu, ale na tym działa idealnie.
Mitesh Jain
8

Poświęciłem trochę czasu, aby to rozgryźć ... Uruchomiłem kilka wyjątków CastException, ale zorientowałem się, że możesz zastąpić Cię LinearLayout w layout.xml nazwą klasy.

Lubię to:

<?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>

W ten sposób nie napotkasz żadnych problemów z rzutowaniem.

... a jeśli nie chcesz tego robić na każdej stronie, zalecamy użycie „MasterPage w Androidzie”. Zobacz link tutaj: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx

Janus Kamp Hansen
źródło
Wow, bądź ostrożny z wklejeniem tego do swojego XML, jeśli nie masz tej samej nazwy pakietu / klasy. Eclipse po prostu decyduje się na zamrożenie i musisz go wyłączyć. Taki profesjonalny produkt. / s
Spencer Ruport,
5
@SpencerRuport, dlatego jest bezpłatny.
Cody,
@DoctorOreo - pobierz IntelliJ. To nic nie kosztuje i nie jest do bani.
Mark
@ Mark - kilka miesięcy po opublikowaniu tego rzeczywiście wypróbowałem IntelliJ. IMO jest o wiele lepsze niż Eclipse. Wszystkie ich produkty (w przeważającej części) uważam za doskonałe. Kupiłem nawet kilka.
Cody
Przepraszamy za przywrócenie tak starego wątku komentarza. Cieszę się, że go używasz i sprawia ci to przyjemność. Uwielbiam korzystać z IntelliJ oraz AppCode dla iOS i PyCharm dla Pythona. Twoje zdrowie!
Mark
5

Chodzi o to, że jeśli chcesz ukryć klawiaturę i jednocześnie sprawdzić stan miękkiego wejścia, skorzystaj z następującego rozwiązania:

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

Ta metoda zwraca wartość true, jeśli klawiatura była pokazywana przed ukryciem.

George Maisuradze
źródło
To działa bez użycia wysokości i wszystko ... Dzięki ... oszczędziłeś mój czas ...
Vji
4

Odkryłem, że kombinacja metody @ Reuben_Scratton i metody @ Yogesh wydaje się działać najlepiej. Połączenie ich metod dałoby coś takiego:

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
    }
  }
});
Cbradley
źródło
zawsze Konfiguracja.KEYBOARDHIDDEN_NO.
fantouch
4

Możesz obserwować ukrycie klawiatury programowej za pomocą decorView działania.

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);
    }
}
Zebulon Li
źródło
4

Zamiast zakładać kodowanie różnic, zrobiłem coś takiego, ponieważ nie miałem opcji menu w mojej aplikacji.

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
        }
     }
});
Santhosh Shettigar
źródło
Nie działa na Androidzie: windowSoftInputMode = "adjustPan". Chciałem, żeby mój ekran nie kurczył się po pojawieniu się miękkiej klawiatury. Czy możesz podać jakąkolwiek poprawkę, aby działała nawet dla AdjustPan
Shirish Herwade
4

Istnieje również rozwiązanie z wstawkami systemowymi, ale działa tylko z API >= 21( Android L). Powiedzmy, że masz BottomNavigationView, co jest dzieckiem LinearLayouti musisz go ukryć, gdy wyświetlana jest klawiatura:

> LinearLayout
  > ContentView
  > BottomNavigationView

Wszystko, co musisz zrobić, to rozszerzyć LinearLayoutw następujący sposób:

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

Chodzi o to, że gdy pokazana jest klawiatura, wstawki systemowe są zmieniane z dość dużą .bottomwartością.

nikis
źródło
4

Jest ukryta metoda może pomóc w tym InputMethodManager.getInputMethodWindowVisibleHeight. Ale nie wiem, dlaczego jest ukryty.

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()
  }
}
Kevin Du
źródło
Jeśli ktoś by tego potrzebował - działa również w Xamarin, nazwa metody jest dokładnie taka sama i należy uzyskać do niej dostęp w ten sam sposób - poprzez właściwość Class na InputMethodManager.
Konstantin Severy
Uważaj, używając tego, to nie jest obsługiwany interfejs API (jest ukryty z jakiegoś powodu), a na początek nie działa na KitKat.
Daniele Ricci
3

Żadne z tych rozwiązań nie będzie działać dla Lollipop w obecnej postaci. W Lollipop activityRootView.getRootView().getHeight()zawiera wysokość paska przycisków, a pomiar widoku nie. Zaadaptowałem najlepsze / najprostsze rozwiązanie powyżej do pracy z 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
    }
  }
});
natanielwolf
źródło
Dlaczego podobne rozwiązanie z stackoverflow.com/a/18992807/2914140 nie działało dla Ciebie i czym różni się Twoje rozwiązanie?
CoolMind
3

Właśnie napotkałem błąd podczas korzystania z większości powyższych rozwiązań, które sugerują dodanie stałej liczby.

S4 ma wysoką rozdzielczość, co spowodowało, że wysokość paska nawigacji wynosi 100 pikseli, więc moja aplikacja myśli, że klawiatura jest otwarta przez cały czas.

Tak więc po wydaniu wszystkich nowych telefonów wysokiej rozdzielczości uważam, że użycie wartości zakodowanej na stałe nie jest dobrym pomysłem na dłuższą metę.

Lepszym podejściem, które znalazłem po kilku testach na różnych ekranach i urządzeniach, było użycie wartości procentowej. Uzyskaj różnicę między decorView a treścią aplikacji, a następnie sprawdź, jaki jest procent tej różnicy. Ze statystyk, które otrzymałem, większość paska nawigacyjnego (niezależnie od rozmiaru, rozdzielczości itp.) Zajmie od 3% do 5% ekranu. Gdy klawiatura była otwarta, zajmowała od 47% do 55% ekranu.

Podsumowując, moim rozwiązaniem było sprawdzenie, czy różnica jest większa niż 10%, a następnie zakładam, że klawiatura jest otwarta.

N Jay
źródło
3

Użyłem niewielkiego wariantu odpowiedzi Reubana, który okazał się bardziej pomocny w pewnych okolicznościach, szczególnie w przypadku urządzeń o wysokiej rozdzielczości.

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
                }
            }
        });
PearsonArtPhoto
źródło
czym jest ta aktywność R.id.Root
ranjith
2
zamiast tworzyć i używać R.id.activityRoot, możesz po prostu użyć android.R.id.contentdokładnie tego, czego potrzebujesz.
Marcin Orłowski
3

Od zawsze było to związane z komputerem, ale to pytanie jest nadal niewiarygodnie istotne! Więc wziąłem powyższe odpowiedzi i połączyłem je i udoskonaliłem trochę ...

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

Mi to pasuje.

Roselyn Soffer
źródło
3

Spróbuj tego:

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 ... \\
        }
    }
});
Ofek Ashery
źródło
2

Moja odpowiedź jest w zasadzie taka sama jak odpowiedź Kachi, ale zapakowałem ją w fajną klasę pomocników, aby wyczyścić sposób, w jaki jest używana w mojej aplikacji.

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

Możesz użyć tego do wykrywania zmian klawiatury w dowolnym miejscu aplikacji:

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

Uwaga: używaj tylko jednego z połączeń „rejestruj”. Wszystkie działają tak samo i są dostępne tylko dla wygody

billylindeman
źródło
2

możesz spróbować tego, działa świetnie dla mnie:

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

if (imm.isAcceptingText()) {
    //Software Keyboard was shown..
} else {
    //Software Keyboard was not shown..
}
IRvanFauziE
źródło
1
Nie używaj tego, zwraca prawdę, jeśli klawiatura była ukryta za pomocą wstecz, ale widok jest skupiony, przynajmniej na Marshmallow
Maragues
2
zawsze odpowiada na to, co jest widoczne
jose920405 27.09.16
2

Miałem trudności z utrzymaniem stanu klawiatury podczas zmiany orientacji fragmentów w przeglądarce. Nie jestem pewien dlaczego, ale wydaje się, że jest nieprzyjemny i działa inaczej niż standardowa aktywność.

Aby w tym przypadku utrzymać stan klawiatury, najpierw dodaj ją android:windowSoftInputMode = "stateUnchanged"do swojego AndroidManifest.xml. Możesz jednak zauważyć, że tak naprawdę nie rozwiązuje to całego problemu - klawiatura nie otworzyła się dla mnie, jeśli była wcześniej otwarta przed zmianą orientacji. We wszystkich innych przypadkach zachowanie wydawało się prawidłowe.

Następnie musimy wdrożyć jedno z wymienionych tutaj rozwiązań. Najczystszym, jaki znalazłem, był George Maisuradze - użyj boolean callback z hideSoftInputFromWindow:

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

Zapisałem tę wartość w onSaveInstanceStatemetodzie mojego fragmentu i odzyskałem ją onCreate. Następnie na siłę pokazałem klawiaturę, onCreateViewjeśli miała wartość true(zwraca wartość true, jeśli klawiatura jest widoczna przed faktycznym ukryciem jej przed zniszczeniem fragmentu).

Kropka kwantowa
źródło
1

Oto moje rozwiązanie i działa. Zamiast szukać rozmiaru w pikselach, po prostu sprawdź, czy wysokość widoku zawartości zmieniła się, czy nie:

// 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();
}
Meanman
źródło
inputMethodManager.isActive () zawsze zwraca mi wartość true, niezależnie od tego, czy klawiatura jest
włączona,
1

Nie twórz żadnego twardego kodu. Najlepszym sposobem jest zmiana rozmiaru widoków podczas ustawiania ostrości na EditText dzięki KeyBord Show. Możesz to zrobić, dodając właściwość zmiany rozmiaru działania do pliku manifestu, używając poniższego kodu.

android:windowSoftInputMode="adjustResize"

Rahul Mandaliya
źródło
1

Istnieje bezpośredni sposób, aby się tego dowiedzieć. I nie wymaga żadnych zmian w układzie.
Działa więc również w trybie pełnego ekranu.

Sztuka polega na tym, że próbujesz ukryć lub pokazać miękką klawiaturę i uchwycić wynik tej próby.
Bez paniki, to tak naprawdę nie pokazuje ani nie ukrywa klawiatury. Po prostu pytamy o państwo.

Aby być na bieżąco, możesz po prostu powtórzyć operację, np. Co 200 milisekund, używając programu obsługi.

Implementacja znajduje się tutaj: https://stackoverflow.com/a/27567074/2525452

fies
źródło
1

myślę, że ta metoda pomoże ci dowiedzieć się, czy klawiatura jest widoczna czy nie.

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

}
John Smith
źródło
zawsze odpowiada na to, co jest widoczne
jose920405 27.09.16
0

Nowa odpowiedź Reubena Scrattona (oblicz różnicę wysokości int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight(); ) nie będzie działać w aktywności, jeśli ustawisz tryb półprzezroczystego paska stanu.

jeśli używasz półprzezroczystego paska stanu, activityRootView.getHeight() nigdy nie zmieni pogody, widoczna jest klawiatura programowa. zawsze zwróci wysokość aktywności i pasek stanu.

Na przykład Nexus 4, Android 5.0.1, ustawiony android:windowTranslucentStatusna true, zwróci 1184 na zawsze, nawet ime mają opend. Jeśli ustawiszandroid:windowTranslucentStatus false, zwróci poprawnie Wysokość, jeśli będzie niewidoczny, zwróci 1134 (nie obejmuje paska stanu)。 zamknij ime, może zwróci 5xx może (zależy od wysokości ime)

Nie wiem, czy to błąd, próbowałem na 4.4.4 i 5.0.1, wynik jest taki sam.

Tak więc, do tej pory, druga najbardziej uzgodniona odpowiedź, rozwiązanie Kachi będzie najbezpieczniejszym sposobem obliczenia wysokości obrazu. Oto kopia:

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
    }
 }
}); 
Loyea
źródło
0

Metoda, która nie wymaga LayoutListener

W moim przypadku chciałbym zapisać stan klawiatury przed wymianą mojego fragmentu. I wywołać metodę hideSoftInputFromWindow zonSaveInstanceState , która zamyka klawiaturę i powraca mi czy klawiatura była widoczna lub nie.

Ta metoda jest prosta, ale może zmienić stan klawiatury.

Gordak
źródło
0

Wiem, że to stary post, ale myślę, że jest to najprostsze podejście, jakie znam, a moim urządzeniem testowym jest Nexus 5. Nie próbowałem tego na innych urządzeniach. Mam nadzieję, że inni podzielą się swoim podejściem, jeśli stwierdzą, że mój kod nie jest dobry :)

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 zwraca wartość logiczną.

Dzięki,

Fran Ceriu
źródło
0
if (keyopen())
{
                InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY,0);            
}

Powyższej funkcji używam do sprawdzenia, czy klawiatura jest widoczna. Jeśli tak, to go zamykam.

Poniżej pokazano dwie wymagane metody.

Najpierw zdefiniuj wykonalną wysokość okna w programie 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;
//

} 

Następnie dodaj metodę logiczną, która pobiera wysokość okna w tym przypadku. Jeśli nie pasuje do oryginału (zakładając, że nie zmieniasz go po drodze ...), klawiatura jest otwarta.

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!

Belboz
źródło
0

Wiem, jak dokładnie można ustalić, czy klawiatura jest ukryta, czy nie.

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

Działa to na tabletach. Gdy pasek nawigacyjny jest wyświetlany poziomo.

Valentin Baryshev
źródło