web-dev-qa-db-ja.com

Androidでソフトウェアキーボードの可視性を確認するにはどうすればいいですか?

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

483
fhucho

新しい答え 2012年1月25日追加

以下の答えを書いて以来、誰かが ViewTreeObserver そして友人、バージョン1以来SDKに潜んでいるAPIの存在に私を締めくくりました。

カスタムのレイアウトタイプを必要とするよりも、はるかに簡単な解決策は、アクティビティのルートビューに既知の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 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
    }

    ...
}
650
Reuben Scratton

だからうまくいけば、これは誰かを手助けする。

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 > 100) { // if more than 100 pixels, its probably a keyboard...
        ... do something here
    }
 }
}); 
293
Kachi

返事が遅れて申し訳ありませんが、リスナーの通知やその他の便利なことを使ってオープン/クローズイベントを処理するための小さなヘルパークラスを作成しました。

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
51

それはコンピュータの面で永遠にされていますが、この質問はまだ信じられないほど関連しています!

だから私は上記の答えを取り、それらを少し組み合わせて洗練しました...

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 の実装をチェックしてください。

51
TacB0sS

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

  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;
    }
    
32
Orchard Cafe

私はこれを理解するために少し時間を使いました...私はそれにいくつかのCastExceptionsを実行しました、しかしあなたはクラスの名前で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>

そうすれば、キャストの問題に遭遇することはありません。

...そしてすべてのページでこれを実行したくない場合は、「AndroidのMasterPage」を使用することをお勧めします。ここにリンクを見てください: http://jnastase.alner.net/archive/2011/01/08/ldquomaster-pagesrdquo-in-Android.aspx

8

WifiKeyboardのようなキーボードの高さはゼロなので、要素の高さを確認するのは信頼できません。

代わりに、showSoftInput()およびhideSoftInput()のコールバック結果を使用してキーボードのステータスを確認できます。完全な詳細とコード例

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

7
Roger Keays

あなたのキーボードを隠して同時にソフト入力状態をチェックする必要があるなら、アイデアは、以下の解決策を使うことです:

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

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

5

私は自分のアプリケーションにメニューオプションがあったので、私はこのようなことをしました。

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

これを助けることができる隠しメソッド、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()
  }
}
3
Kevin Du

システムインセットを使った解決策もありますが、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の値で変更されるという考えです。

3
nikis

アクティビティの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);
    }
}
3
Zebulon Li

私は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
                }
            }
        });
2
PearsonArtPhoto

これらの解決策のどれもロリポップにそのままではうまくいきません。 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
    }
  }
});
2
nathanielwolf

それはコンピュータの面で永遠にされていますが、この質問はまだ信じられないほど関連しています!だから私は上記の答えを取り、それらを少し組み合わせて洗練しました...

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

わたしにはできる。

2
Roselyn Soffer

これを試して:

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

ビューページャ内でフラグメントの向きを変更するときにキーボードの状態を維持するのが困難でした。理由は定かではありませんが、それは気が狂っているように見え、標準のActivityとは異なる動作をします。

この場合、キーボードの状態を維持するには、まず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でキーボードを見せた(それは実際にFragment破壊の前にそれを隠す前にキーボードが見えるならtrueを返す).

1
Quantum Dot

上記の解決策のほとんどを使用しているときに、固定数を追加することを提案するバグに遭遇したところです。

S4は高dpiでナビゲーションバーの高さは100ピクセルになるので、私のアプリはキーボードが常に開いていると考えています。

だから、すべての新しい高解像度の携帯電話がリリースされているので、ハードコードされた値を使うのは長期的には良い考えではないと思う。

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

結論として、私の解決策は差分が10%以上であるかどうかをチェックすることでしたそれから私はそのキーボードが開いていると仮定します。

1
N Jay

私の答えは基本的にKachiの答えと同じですが、私はそれが私のアプリ全体で使われる方法を片付けるためにNiceヘルパークラスにまとめました。

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つだけを使用してください。それらはすべて同じように機能し、便宜上そこにあるだけです。

1
billylindeman

あなたはこれを試すことができます、私のために素晴らしい仕事:

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

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

Reuben ScrattonとKachiが提供するソリューションは、デバイスのピクセル密度に依存しているようです。高密度のデバイスを使用している場合、キーボードを下げても高さの差は100より大きくなる可能性があります。これを少し回避するには、最初の高さの差(キーボードを押し下げた状態)を確認してから、現在の差と比較します。

boolean isOpened = false;
int firstHeightDiff = -1;

public void setListenerToRootView(){
    final View activityRootView = getActivity().getWindow().getDecorView().findViewById(Android.R.id.content);
    Rect r = new Rect();
    activityRootView.getWindowVisibleDisplayFrame(r);
    firstHeightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (isAdded()) {
                Rect r = new Rect();
                activityRootView.getWindowVisibleDisplayFrame(r);
                int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                isOpened = heightDiff>firstHeightDiff+100;
                if (isAdded())
                    if(isOpened) {
                        //TODO stuff for when it is up
                    } else {
                        //TODO stuf for when it is down
                    }
            }
        }
    });
}
0
ericmguimaraes

このように簡単な方法があると思います。

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

また、彼が特定の観点から活動しているかどうかを確認することもできます。

imm.isActive(View v);
0
Ricardo A.
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
Belboz

がLayoutListenerを必要としないメソッド

私の場合は、フラグメントを置き換える前にキーボードの状態を保存したいと思います。 onSaveInstanceStateから hideSoftInputFromWindow メソッドを呼び出します。これはキーボードを閉じて、キーボードが表示されているかどうかを返します。

この方法は簡単ですが、キーボードの状態が変わる可能性があります。

0
Gordak

このコードはとてもうまく機能しました

このクラスをルートビューに使用します。

public class KeyboardConstraintLayout extends ConstraintLayout {

private KeyboardListener keyboardListener;
private EditText targetEditText;
private int minKeyboardHeight;
private boolean isShow;

public KeyboardConstraintLayout(Context context) {
    super(context);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}

public KeyboardConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    minKeyboardHeight = getResources().getDimensionPixelSize(R.dimen.keyboard_min_height);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (!isInEditMode()) {
        Activity activity = (Activity) getContext();
        @SuppressLint("DrawAllocation")
        Rect rect = new Rect();
        getWindowVisibleDisplayFrame(rect);

        int statusBarHeight = rect.top;
        int keyboardHeight = activity.getWindowManager().getDefaultDisplay().getHeight() - (rect.bottom - rect.top) - statusBarHeight;

        if (keyboardListener != null && targetEditText != null && targetEditText.isFocused()) {
            if (keyboardHeight > minKeyboardHeight) {
                if (!isShow) {
                    isShow = true;
                    keyboardListener.onKeyboardVisibility(true);
                }
            }else {
                if (isShow) {
                    isShow = false;
                    keyboardListener.onKeyboardVisibility(false);
                }
            }
        }
    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public boolean isShowKeyboard() {
    return isShow;
}

public void setKeyboardListener(EditText targetEditText, KeyboardListener keyboardListener) {
    this.targetEditText = targetEditText;
    this.keyboardListener = keyboardListener;
}

public interface KeyboardListener {
    void onKeyboardVisibility (boolean isVisible);
}

}

キーボードリスナーをアクティビティまたはフラグメントに設定します。

    rootLayout.setKeyboardListener(targetEditText, new KeyboardConstraintLayout.KeyboardListener() {
    @Override
    public void onKeyboardVisibility(boolean isVisible) {

    }
});
0
saleh gholamian

半透明のステータスバーモードを設定すると、Reuben Scrattonの新しい回答(HeightDiffのint heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();の計算)は実際には機能しません。

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

たとえば、Nexus 4、Android 5.0.1、Android:windowTranslucentStatusをtrueに設定すると、imeがオープンしていても、永久に1184が返されます。 Android:windowTranslucentStatusをfalseに設定すると、Heightを正しく返し、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
Loyea

さまざまな解像度の問題のいくつかを理解した後、私は相対サイズを使用することにしました。気付いたように、可視状態と非表示状態の違いは約30%です。そこで私は128 PXを0.3に置き換えることにしました。

そして、私はこのクラスリスナを追加して変更を通知しました。

これが私のバージョンです

import Android.app.*;
import Android.graphics.*;
import Android.view.*;

public class SoftKeyboardState {
  public static final int HIDDEN = 0, VISIBLE = 1;
  private OnKeyboardStateChangedListener listener;
  private View decorView;

  public SoftKeyboardState(Activity activity) {
    this.decorView = activity.findViewById(Android.R.id.content);
    initKeyboardListener();
  }

  private void initKeyboardListener() {
    decorView.getViewTreeObserver().addOnGlobalLayoutListener(
      new ViewTreeObserver.OnGlobalLayoutListener(){
        private final Rect windowVisibleDisplayFrame = new Rect();
        private int lastVisibleDecorViewHeight;

        @Override
        public void onGlobalLayout() {
          decorView.getWindowVisibleDisplayFrame(windowVisibleDisplayFrame);
          final int visibleDecorViewHeight = windowVisibleDisplayFrame.height();

          if (lastVisibleDecorViewHeight != 0) {
            if ((lastVisibleDecorViewHeight > visibleDecorViewHeight) && (lastVisibleDecorViewHeight / visibleDecorViewHeight >= 0.3f)) {
              // visible
              if (listener != null)listener.onKeyboardStateChanged(VISIBLE);
            } else if ((lastVisibleDecorViewHeight < visibleDecorViewHeight) && (visibleDecorViewHeight / lastVisibleDecorViewHeight >= 0.3f)) {
              // hidden
              if (listener != null)listener.onKeyboardStateChanged(HIDDEN);
            }
          }
          lastVisibleDecorViewHeight = visibleDecorViewHeight;
        }
      });
  }

  public void setOnKeyboardStateChangedListener(OnKeyboardStateChangedListener listener) {
    this.listener = listener;
  }

  public interface OnKeyboardStateChangedListener {
    public void onKeyboardStateChanged(int state);
  }
}
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
Fran Ceriu

キーボードが隠れているかどうかを正確に判断できることはわかっています。

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

これはタブレットに有効です。ナビゲーションバーが水平に表示されているとき。

0

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

Android:windowSoftInputMode="adjustResize"

0
Rahul Mandaliya

これが私の解決策です、そしてそれはうまくいきます。ピクセルサイズを探す代わりに、コンテンツビューの高さが変更されたかどうかを確認してください。

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

これを見つける直接的な方法があります。また、レイアウトの変更は不要です。
つまり、没入型フルスクリーンモードでも機能します。

トリックは、あなたがソフトキーボードを隠したり表示したりしようとし、その試みの結果を捉えることです。
パニックはありません。これはキーボードの表示や非表示を切り替えるものではありません。私達はちょうど国家を求めます。

最新の状態に保つには、単純に操作を繰り返すことができます。 Handlerを使用して、200ミリ秒ごとに.

ここに実装があります: https://stackoverflow.com/a/27567074/2525452

0
fies

ソフトキーボードが表示されているかどうかを知るための回避策は次のとおりです。

  1. ActivityManager.getRunningServices(max_count_of_services)を使用して、システム上で実行中のサービスを確認します。
  2. 返されたActivityManager.RunningServiceInfoインスタンスから、 clientCount ソフトキーボードサービスの値を確認します。
  3. 前述のclientCountは毎回インクリメントされ、ソフトキーボードが表示されます。たとえば、clientCountが最初は1だった場合、キーボードが表示されているときは2になります。
  4. キーボードを閉じると、clientCountは減少します。この場合、1にリセットされます。

一般的なキーボードの中には、classNameに特定のキーワードを持つものがあります。

Google AOSP = IME
Swype = IME
Swiftkey = KeyboardService
Fleksy = keyboard
Adaptxt = IME (KPTAdaptxtIME)
Smart = Keyboard (SmartKeyboard)

ActivityManager.RunningServiceInfoから、ClassNamesの上記のパターンを確認します。また、ActivityManager.RunningServiceInfoの clientPackage = Androidは、キーボードがシステムにバインドされていることを示します。

上記の情報は、ソフトキーボードが表示されているかどうかを調べる厳密な方法のために組み合わせることができます。

0
Satishkumar

多分これはあなたを助けるでしょう:

InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);

この方法は、キーボードが表示されているかどうかを確認するのに役立つと思います。

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

}
0
John smith