Androidでソフトウェアキーボードの可視性を確認する方法


516

私は非常に簡単なことをする必要があります-ソフトウェアキーボードが表示されているかどうかを確認します。これはAndroidで可能ですか?


9
ルーベン・スクラトンの答えは良いですが、タブレットでは壊れているようです。check diff> 128をdiff> screenHeight / 3に置き換えました。
キングストン

2
Reuben Scrattonの答えは良かったが、実際に使用するにはKaChiの調整が必要だった。
Cullan 2013

1
Googleが標準の組み込みメソッドをすべてのキーボードアプリで機能させないのはなぜですか?
果物

4
これはシステム機能ではないことを私に教え続けます...
longi

1
このAPIが10年経ってもまだ足りないのは間違いです。Androidから離れたことをうれしく思います。
Reuben Scratton

回答:


674

新しい回答が 2012年1月25日に追加されました

以下の回答を書いてから、誰かが私にバージョン1以降のSDKに潜んでいるAPIであるViewTreeObserverとその友人の存在を教えてくれました。

カスタムレイアウトタイプを必要とするよりもはるかに簡単な解決策は、アクティビティのルートビューに既知のIDを与え、たとえば@+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
トリックをしているようです。あなたはルートビューのIDを知らない場合にも、ここでのビューを取得することができます方法は次のとおりです。((ViewGroup) findViewById(android.R.id.content)).getChildAt(0)
ゴールドスミス

8
実際のルートビュー(android.R.id.content)を使用してこれを試すとSystem、アプリケーションではなく、アプリケーションが高さを変更するエンティティであると自信を持って言うことができます。Androidチームが休憩を取り、少なくともSoftKeyboard入力に関する基本的なことを教えてくれれば、はるかに安全になります。
Graeme

14
heightDiff常にアクションバーの高さが含まれることに注意してください。その高さが定数よりも大きいかどうかをテストすることで無視された新しい答えでは、Nexus 4などのxxhdpiデバイスでは100ピクセルでは不十分です。このハッキーな作業を本当に使用したい場合は、その値をDPに変換することを検討してください。周り。
ポールランメルツマ2013年

8
注意:WindowManager.LayoutParams.FLAG_FULLSCREENおよびフルスクリーンテーマでは機能しません。
VAV 2014年

303

うまくいけば、これは誰かを助けるのに役立ちます。

Reuben Scrattonが与えた新しい答えは素晴らしく、本当に効率的ですが、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
これは私のために働いたものです。ズームも使用していますが、stackoverflow.com / a / 5224088/530513にTwoDScrollerView類似したカスタムからキーボードの状態を検出しようとしました。子はシンプルではなくカスタムレイアウト(拡張)でしたが、設定にもかかわらず、推奨される解決策を使用してキーボードを検出できませんでした。ありがとう!ImageViewRelativeLayoutandroid:windowSoftInputMode="adjustResize"
David O'Meara 2012

1
ありがとう、ありがとう、ありがとう!adjustResizeは私のアプリでは実行可能ではなく、ソリューションは完全に機能しました。
dwemthy 2012

1
連携ActionBarしてActionBarSherlock。どうもありがとう!ちなみに、方法がありますr.height():)
Dmitry Zaytsev

1
23時間以内に賞金をここに添付して、この回答になんとかマークを付けます。
Dmitry Zaytsev

9
heightDiff > root.getRootView().getHeight() / 4高解像度デバイスでの作業に適した値です。100pxは短いです。1080x1920解像度のNexus 5の場合、1920-(996-75)>?100 = 999 1920-(1776-75)>?100 = 219 // 480x800解像度、800-(800-38)のGalaxy s2でキーボードが稼働している>?100 = 38800-(410-38)>?100 = 428 //キーボードが上がっているので、マジックナンバー100pxでは不十分です。
Flask_KR 2015

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
Kevin van Mierlo、2014年

これは、次を追加した場合にのみ機能します:android:windowSoftInputMode = "stateHidden | adjustPan"またはandroid:windowSoftInputMode = "stateHidden | adjustResize"ありがとうございます!!!!
Lena Bru

本気ですか?メモリサーバーの場合、android:windowSoftInputModeにデフォルト値があるときにもイベントを適切に取得しました...機能しなかったのは画面の動作だけなので、手動で縮小しました...
TacB0sS

2
小さな修正:private final int EstimatedKeyboardDP = DefaultKeyboardDP +(Build.VERSION.SDK_INT> = Build.VERSION_CODES.LOLLIPOP?48:0);
binaryKarmic 2014

2
優秀な!!「windowSoftInputMode」が「adjustPan」/「adjustResize」/「adjustPan | stateHidden」/「adjustResize | stateHidden」に設定されていても、このオプションがなくても、常に機能します。XiaoMi 8でテスト済み
Zhou Hongbo

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 14年

1
問題が発生した場合は、pingを実行してください:) 2つのプロジェクトで正常に使用しました
Artem Zinnatullin 2014年

上記の多くの例を試し、小さな問題が発生した後、これは多くのさまざまなデバイス(xxhdpiを含む)で私にとって最も効果的なものでした。さらに、独自の再利用可能なクラスに属しています。モノドロイドで使えるように改造しました。
kheit 14年

一部のキーボードでは、時々、上部にカスタムキーの行が追加されています(たとえば、予測された単語)。を使用するgetLastKeyboardHeightInPx()とその行の高さが含まれないため、これらは明らかにキーボード自体の一部ではありません。それを考慮に入れる方法を知っていますか?
イジェシャー2014

これは、キーボードが表示されたときにレイアウトの高さの変更を妥協する準備ができている場合にのみ機能します。正しい?
M.ウスマンカーン

33

高密度デバイスのソフトキーボードの可視性を誤って検出しないようにするためのいくつかの改善:

  1. 高さの差のしきい値は、128ピクセルではなく、128 dpとして定義する必要があります。 参照してくださいメトリクスとグリッドについて、Googleの設計ドキュメント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
これは受け入れられる答えに値する。密度を無視すると、フォームファクタは異なるがピクセルサイズが類似しているデバイスでは、結果が大きく異なります。ありがとう!
Ginger McMurray

2
ありがとう...これは良い状態です!
TacB0sS 2014

1
優秀な。isKeyboardShown()メソッドが必要です。ありがとう
Danylo Volokh

2020年にも機能しています。完璧な答え。私はすべてのコードを試しましたが、これで完全に動作します。
Mitesh Jain

8

少し時間をかけてこれを理解しました...いくつかのCastExceptionを実行しましたが、layout.xmlのLinearLayoutをクラスの名前に置き換えることができることがわかりました。

このような:

<?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 in Android」を使用することをお勧めします。ここのリンクを参照してください:http : //jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-android.aspx


同じパッケージ/クラス名がない場合は、XMLにこれを貼り付けることに注意してください。Eclipseはフリーズすることを決めただけなので、シャットダウンする必要があります。そんなプロフェッショナルな商品。/ s
スペンサールポート、2011

5
@SpencerRuport、それが無料である理由です。
コーディ

@DoctorOreo-IntelliJを入手してください。それは無料で、吸いません。
マーク

@Mark-私がこれを投稿してから数か月後、私は実際にIntelliJを試しました。IMOはEclipseよりもはるかに優れています。すべての製品(ほとんどの場合)は優れていると思います。私もいくつか購入しました。
コーディ

このような古いコメントスレッドを復活させて申し訳ありません。ご利用いただき、ありがとうございます。IntelliJとAppCode for iOSおよびPyCharm for Pythonを使用するのが大好きです。乾杯!
マーク・


5

アイデアは、キーボードを非表示にすると同時にソフト入力の状態をチェックする必要がある場合は、次のソリューションを使用することです。

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

このメソッドは、キーボードが非表示になる前に表示された場合はtrueを返します。


これは、高さなどを使用せずに動作するものです...ありがとう... u時間を節約できました...
Vji

4

@Reuben_Scrattonのメソッドと@Yogeshのメソッドの組み合わせが最も効果的であることがわかりました。これらのメソッドを組み合わせると、次のような結果になります。

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    if (getResources().getConfiguration().keyboardHidden == Configuration.KEYBOARDHIDDEN_NO) { // Check if keyboard is not hidden
       // ... do something here
    }
  }
});

常にConfiguration.KEYBOARDHIDDEN_NOです。
ファンタッチ2014

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でも機能するように修正を教えてください
Shirish Herwade 2013

4

システムインセットの解決策もありますが、API >= 21Android 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でも機能します)、メソッドの名前はまったく同じであり、同じ方法で(InputMethodManagerのClassプロパティを介して)アクセスする必要があります。
Konstantin Severy 2018年

これを使用する場合は注意してください。これはサポートされていないAPI(理由により非表示になっています)であり、まず第一に、KitKatでは機能しません。
Daniele Ricci

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は高いため、ナビゲーションバーの高さが100pxになり、私のアプリはキーボードが常に開いていると考えています。

だから、すべての新しいハイレゾ電話がリリースされているので、ハードコードされた値を使用することは長期的には良い考えではないと思います。

さまざまな画面やデバイスでいくつかのテストを行った後に見つけたより良いアプローチは、パーセンテージを使用することでした。decorViewとurアプリのコンテンツの違いを取得し、その違いの割合を確認してください。私が得た統計から、ほとんどのナビゲーションバー(サイズ、解像度などに関係なく)は画面の3%から5%の間です。キーボードが開いているかのように、画面の47%から55%を占めていました。

結論として、私の解決策は、diffが10%を超えているかどうかを確認することでした。


3

私はReubanの回答のわずかな変形を使用しました。これは、特定の状況、特に高解像度デバイスでより役立つことが判明しました。

final View activityRootView = findViewById(android.R.id.content);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
        new OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int heightView = activityRootView.getHeight();
                int widthView = activityRootView.getWidth();
                if (1.0 * widthView / heightView > 3) {
                    //Make changes for Keyboard not visible
                } else {
                    //Make changes for keyboard visible
                }
            }
        });

このR.id.activityRootとは何ですか
ランジス

2
を作成して使用する代わりに、必要なものをR.id.activityRootそのまま使用できandroid.R.id.contentます。
Marcin Orlowski、2016

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

注:「登録」呼び出しの1つのみを使用してください。それらはすべて同じように機能し、便宜上そこにのみあります


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でtrueを返します
Maragues

2
常に表示されているように応答します
jose920405 2016

2

ビューページャー内でフラグメントの方向を変更するときに、キーボードの状態を維持するのが困難でした。理由はわかりませんが、不安定で、標準のアクティビティとは異なる動作をするだけです。

この場合にキーボードの状態を維持するには、最初ににを追加android:windowSoftInputMode = "stateUnchanged"する必要がありますAndroidManifest.xml。ただし、これで問題全体が実際に解決されるわけではないことに気付かれるかもしれません-方向を変更する前にキーボードが開かれていた場合、キーボードが開かれませんでした。他のすべての場合では、動作は正しいように見えました。

次に、ここで説明したソリューションの1つを実装する必要があります。私が見つけた最もクリーンなものはGeorge Maisuradzeのものでした-hideSoftInputFromWindowからのブールコールバックを使用します。

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

この値をFragmentの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を返します
EionRobb

1

ハードコードを作成しないでください。最良の方法は、KeyBord Showを使用してEditTextでフォーカスを取得するときにビューのサイズを変更する必要があることです。これを行うには、以下のコードを使用して、アクティビティのサイズ変更プロパティをマニフェストファイルに追加します。

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 2016

0

int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();半透明のステータスバーモードを設定すると、Reuben Scrattonの新しい回答(HeightDiffを計算する )が動作しなくなります。

半透明のステータスバーを使用する場合activityRootView.getHeight()、ソフトキーボードが表示されている天候が変わることはありません。常にアクティビティの高さとステータスバーを返します。

たとえば、Nexus 4、Android 5.0.1、android:windowTranslucentStatustrueに設定すると、imeが開いていても1184が永久に返されます。android:windowTranslucentStatusfalseに設定すると、高さが正しく返されます。imeが非表示の場合は、1134が返されます(ステータスバーは含まれません)。imeを閉じると、5xxが返される場合があります(imeの高さに依存します)。

天気がわからないのでバグです。4.4.4と5.0.1を試してみましたが、結果は同じです。

したがって、これまで2番目に合意された答えであるカチのソリューションは、imeの高さを計算する最も安全な方法です。ここにコピーがあります:

final View activityRootView = findViewById(R.id.activityRoot);
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new        OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);

int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
    ... do something here
    }
 }
}); 

0

LayoutListenerを必要しないメソッド

私の場合、フラグメントを交換する前にキーボードの状態を保存したいと思います。メソッドhideSoftInputFromWindow from を呼び出すと、 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);            
}

上記の関数は、キーボードが表示されているかどうかを確認するために使用するものです。もしそうなら、私はそれを閉じます。

以下に、必要な2つの方法を示します。

まず、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;
    }
}

フロッツ!


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

これはタブレットで機能します。ナビゲーションバーが水平に表示されている場合。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.