Androidで「仮想キーボードの表示/非表示」イベントをキャプチャするにはどうすればよいですか?


回答:


69

注意

このソリューションは、ソフトキーボードで onConfigurationChangedは機能せず、ソフト(仮想)キーボードでは呼び出されません。


構成の変更は自分で処理する必要があります。

http://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange

サンプル:

// from the link above
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);


    // Checks whether a hardware keyboard is available
    if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "keyboard visible", Toast.LENGTH_SHORT).show();
    } else if (newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "keyboard hidden", Toast.LENGTH_SHORT).show();
    }
}

次に、一部のビューの可視性を変更し、フィールドを更新して、レイアウトファイルを変更します。


4
試す@shiami newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO〜クリス
cimnine

3
これは、AndroidManifestで必要なconfigChangesをリッスンするアクティビティを登録した場合にのみ機能します。
Raul Agrait、2011

65
回答を更新して、ソフトキーボードでは機能しないことを伝えてください。私はあなたのコードを試す私の半日を無駄にしました。そして、これらのコメントを見ました。
Shirish Herwade 2013

17
これは、元の質問であった「仮想」キーボードでは機能しません。
brummfondel 2014年

18
さて、質問はソフトキーボードについてでした、なぜハードウェアキーボードについての受け入れられた答えはなぜですか?-1!
Denys Vitali

56

これは最も効果的な解決策ではないかもしれません。しかし、これは毎回うまくいきました... softKeyboardを聞く必要があるときはいつでも、この関数を呼び出します。

boolean isOpened = false;

public void setListenerToRootView() {
    final View activityRootView = getWindow().getDecorView().findViewById(android.R.id.content);
    activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();
            if (heightDiff > 100) { // 99% of the time the height diff will be due to a keyboard.
                Toast.makeText(getApplicationContext(), "Gotcha!!! softKeyboardup", 0).show();

                if (isOpened == false) {
                    //Do two things, make the view top visible and the editText smaller
                }
                isOpened = true;
            } else if (isOpened == true) {
                Toast.makeText(getApplicationContext(), "softkeyborad Down!!!", 0).show();
                isOpened = false;
            }
        }
    });
}

注:ユーザーがフローティングキーボードを使用している場合、このアプローチでは問題が発生します。


1
addOnGlobalLayoutListener?
coolcool1994 2014年

7
これは、メモリリークのようなにおいがします。グローバルオブジェクトにリスナーを追加します。グローバルオブジェクトは、あなたを保持し、決してあなたを離さないでしょう。
flexicious.com 2014

9
レイアウトがサイズ変更されることはないため、これはandroid:windowSoftInputMode="adjustPan"、またはadjustResizeフルスクリーンウィンドウで設定されたアクティビティでは機能しません。
Ionoclast Brigham、2015

1
これはadjustResizeでのみ機能します。adjustPanの場合、heightDiffは変更されません。
alexhilton、2015年

2
なぜブール値を比較するのですか?
Xerus

37

アクティビティからIMM(仮想)キーボードウィンドウの表示/非表示を処理する場合は、レイアウトをサブクラス化し、onMesureメソッドをオーバーライドする必要があります(レイアウトの測定された幅と測定された高さを決定できるようにするため)。その後、setContentView()を使用して、サブクラス化されたレイアウトをアクティビティのメインビューとして設定します。これで、IMMのウィンドウの表示/非表示イベントを処理できるようになります。これは複雑に聞こえるかもしれませんが、実際にはそうではありません。これがコードです:

main.xml

   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="horizontal" >
        <EditText
             android:id="@+id/SearchText" 
             android:text="" 
             android:inputType="text"
             android:layout_width="fill_parent"
             android:layout_height="34dip"
             android:singleLine="True"
             />
        <Button
             android:id="@+id/Search" 
             android:layout_width="60dip"
             android:layout_height="34dip"
             android:gravity = "center"
             />
    </LinearLayout>

次に、アクティビティ内でレイアウトのサブクラスを宣言します(main.xml)

    public class MainSearchLayout extends LinearLayout {

    public MainSearchLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");

        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight){
            // Keyboard is shown

        } else {
            // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

コードから、サブクラスコンストラクターでアクティビティのレイアウトを拡張していることがわかります。

inflater.inflate(R.layout.main, this);

次に、アクティビティのサブクラス化されたレイアウトのコンテンツビューを設定します。

public class MainActivity extends Activity {

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        MainSearchLayout searchLayout = new MainSearchLayout(this, null);

        setContentView(searchLayout);
    }

    // rest of the Activity code and subclassed layout...

}

3
私はさらに調査する必要がありますが、レイアウトの測定がキーボードの存在によって影響されない大画面デバイスの小さなダイアログで私の場合にこれが機能するかどうかについては疑問があります。
PJL 2013年

4
android:windowSoftInputMode = "adjustPan"では機能しません。ソフトキーボードが表示された後、画面が縮小しないようにしたいのですが。adjustPanでも機能するように修正を教えてください
Shirish Herwade 2013

これは機能していません。if(actualHeight> proposalheight){//キーボードが表示されている場合は、常にelse部分に移動します} else {//キーボードが非表示になっている}
Aamirkhan

同じ考えでカスタムビューを使用することもできます。例はgist.github.com/juliomarcos/8ca307cd7eca607c8547
Julio Rodrigues

1
レイアウトはサイズ変更されないため、で設定されたアクティビティandroid:windowSoftInputMode="adjustPan"またはadjustResizeフルスクリーンウィンドウで動作しません。
イオノクラストブリガム2015

35

私はこのようにしました:

OnKeyboardVisibilityListenerインターフェースを追加します。

public interface OnKeyboardVisibilityListener {
    void onVisibilityChanged(boolean visible);
}

HomeActivity.java

public class HomeActivity extends Activity implements OnKeyboardVisibilityListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sign_up);
    // Other stuff...
    setKeyboardVisibilityListener(this);
}

private void setKeyboardVisibilityListener(final OnKeyboardVisibilityListener onKeyboardVisibilityListener) {
    final View parentView = ((ViewGroup) findViewById(android.R.id.content)).getChildAt(0);
    parentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        private boolean alreadyOpen;
        private final int defaultKeyboardHeightDP = 100;
        private final int EstimatedKeyboardDP = defaultKeyboardHeightDP + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? 48 : 0);
        private final Rect rect = new Rect();

        @Override
        public void onGlobalLayout() {
            int estimatedKeyboardHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, EstimatedKeyboardDP, parentView.getResources().getDisplayMetrics());
            parentView.getWindowVisibleDisplayFrame(rect);
            int heightDiff = parentView.getRootView().getHeight() - (rect.bottom - rect.top);
            boolean isShown = heightDiff >= estimatedKeyboardHeight;

            if (isShown == alreadyOpen) {
                Log.i("Keyboard state", "Ignoring global layout change...");
                return;
            }
            alreadyOpen = isShown;
            onKeyboardVisibilityListener.onVisibilityChanged(isShown);
        }
    });
}


@Override
public void onVisibilityChanged(boolean visible) {
    Toast.makeText(HomeActivity.this, visible ? "Keyboard is active" : "Keyboard is Inactive", Toast.LENGTH_SHORT).show();
  }
}

これがお役に立てば幸いです。


2
Hirenに感謝します。これは完璧なソリューションです+1
Harin Kaklotar 2017

1
ありがとう、私のために働きました!RecyclerViewを調整するだけの場合は、こちらのソリューションを参照してください:stackoverflow.com/a/43204258/373106
David Papirov

1
完璧な再利用可能な実装、アクティビティまたはフラグメントへの取り組み、ありがとう
Pelanes 2017年

1
本当にいいタイ。
ZaoTaoBao

@ DavidPapirov、RecyclerViewへのリンクを貼り付けましたが、ここでは言及していません。
CoolMind 2018

22

Nebojsa Tomcicのコードに基づいて、次のRelativeLayout-Subclassを開発しました。

import java.util.ArrayList;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;

public class KeyboardDetectorRelativeLayout extends RelativeLayout {

    public interface IKeyboardChanged {
        void onKeyboardShown();
        void onKeyboardHidden();
    }

    private ArrayList<IKeyboardChanged> keyboardListener = new ArrayList<IKeyboardChanged>();

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public KeyboardDetectorRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public KeyboardDetectorRelativeLayout(Context context) {
        super(context);
    }

    public void addKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.add(listener);
    }

    public void removeKeyboardStateChangedListener(IKeyboardChanged listener) {
        keyboardListener.remove(listener);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();

        if (actualHeight > proposedheight) {
            notifyKeyboardShown();
        } else if (actualHeight < proposedheight) {
            notifyKeyboardHidden();
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    private void notifyKeyboardHidden() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardHidden();
        }
    }

    private void notifyKeyboardShown() {
        for (IKeyboardChanged listener : keyboardListener) {
            listener.onKeyboardShown();
        }
    }

}

これは非常にうまく機能します...マーク、このソリューションは、アクティビティのソフト入力モードが "WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE"に設定されている場合にのみ機能することをマークします


3
android:windowSoftInputMode = "adjustPan"では機能しません。ソフトキーボードが表示された後、画面が縮小しないようにしたいのですが。adjustPanでも機能するように修正を教えてください
Shirish Herwade 2013

1
レイアウトがサイズ変更されることはないため、これはandroid:windowSoftInputMode="adjustPan"、またはadjustResizeフルスクリーンウィンドウで設定されたアクティビティでは機能しません。
イオノクラストブリガム2015

それはかなりの回数トリガーされます。
zionpi 2016年

22

@amalBitの回答のように、リスナーをグローバルレイアウトに登録し、dectorViewの表示されている下部とその提案された下部の差を計算します。差が特定の値より大きい場合(IMEの高さを推測)、IMEは上がっていると考えます。

    final EditText edit = (EditText) findViewById(R.id.edittext);
    edit.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            if (keyboardShown(edit.getRootView())) {
                Log.d("keyboard", "keyboard UP");
            } else {
                Log.d("keyboard", "keyboard Down");
            }
        }
    });

private boolean keyboardShown(View rootView) {

    final int softKeyboardHeight = 100;
    Rect r = new Rect();
    rootView.getWindowVisibleDisplayFrame(r);
    DisplayMetrics dm = rootView.getResources().getDisplayMetrics();
    int heightDiff = rootView.getBottom() - r.bottom;
    return heightDiff > softKeyboardHeight * dm.density;
}

高さのしきい値100は、IMEの推定最小高さです。

これは、adjustPanとadjustResizeの両方で機能します。


2
髪を引っ張るところです!あなたは私の髪を救った;)
Vijay Singh Chouhan

1
これが唯一の良い答えです。ソフトキーボードに最適です。ありがとう
Z3nk

12

Nebojsaのソリューションはほとんど私にとってうまくいきました。複数行のEditText内をクリックすると、キーボードが表示されていることがわかりましたが、EditText内で入力し始めたとき、actualHeightとproposalHeightはまだ同じだったため、キーボードがまだ表示されていることがわかりませんでした。最大の高さを保存するように少し変更しましたが、問題なく動作します。次に、改訂されたサブクラスを示します。

public class CheckinLayout extends RelativeLayout {

    private int largestHeight;

    public CheckinLayout(Context context, AttributeSet attributeSet) {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.checkin, this);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        largestHeight = Math.max(largestHeight, getHeight());

        if (largestHeight > proposedheight)
            // Keyboard is shown
        else
            // Keyboard is hidden

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

10

誰かがこれを投稿するかどうかわかりません。見つかったこのソリューション簡単な使用に!SoftKeyboardクラスはgist.github.comです。しかし、キーボードポップアップ/非表示イベントコールバックの間、UIで適切に処理するためのハンドラーが必要です。

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);

/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{

    @Override
    public void onSoftKeyboardHide() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });
    }

    @Override
    public void onSoftKeyboardShow() 
    {
        // Code here
        new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    // Code here will run in UI thread
                    ...
                }
            });

    }   
});

SoftkeyBoard「gist.github.com/felHR85/…」を取得するためのGitはこちら
douarbou

9

これを解決するには、カスタムEditTextでonKeyPreIme(int keyCode、KeyEvent event)をオーバーライドします。

@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
        //keyboard will be hidden
    }
}

フラグメントまたはアクティビティでの使用方法?@qbait
Maulik Dodia

これは機能しません。私の場合、ページを離れたときにのみ呼び出すことができます。
DysaniazzZ 2017

:これはのEditTextからの方法で、この答えを参照stackoverflow.com/a/5993196/2093236
Dmide

4

これを行うためのハックがあります。ソフトキーボードが表示または非表示したときに検出する方法があるように思えませんが、あなたができることがあるときに実際に検出について設定することで、表示または非表示にするOnFocusChangeListenerEditTextすることができているよリスニング。

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            //hasFocus tells us whether soft keyboard is about to show
        }
    });

注:このハックで知っておくべきことの1つは、このコールバックがEditTextフォーカスを取得または失ったときにすぐに発生することです。これは実際は、ソフトキーボードが表示または非表示になる直前に発生します。キーボードが表示または非表示になったに何かを実行するために私が見つけた最良の方法は、次のようにaを使用してHandler何かを400ms遅延させることです。

EditText et = (EditText) findViewById(R.id.et);
et.setOnFocusChangeListener(new View.OnFocusChangeListener()
    {
        @Override
        public void onFocusChange(View view, boolean hasFocus)
        {
            new Handler().postDelayed(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        //do work here
                    }
                }, 400);
        }
    });

1
それ以外の場合は機能しません。状態が変更された後にOnFocusChangeListenerのみEditTextフォーカスがあるかどうかを通知します。しかし、フォーカスIMEがあると非表示になる可能性がありますEditText。このケースを検出するにはどうすればよいですか?
DysaniazzZ 2017

3

サンダー、ソフトキーボードで遮られた景色を見せようとしていると思います。こちらのhttp://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.htmlをお試しください


このURLの最初のトラックバックは、アクティビティのマニフェストにandroid:windowSoftInputMode = "adjustPan"を追加することについて言及しているRussenReaktorのウェブログを指しています。これは私にとってはうまくいきました。
JohnnyLambada、2011年

2

単一行のtextviewバックコーディングの問題を解決しました。

package com.helpingdoc;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;

public class MainSearchLayout extends LinearLayout {
    int hieght = 0;
    public MainSearchLayout(Context context, AttributeSet attributeSet) {

        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        Log.d("Search Layout", "Handling Keyboard Window shown");
       if(getHeight()>hieght){
           hieght = getHeight();
       }
        final int proposedheight = MeasureSpec.getSize(heightMeasureSpec);
        final int actualHeight = getHeight();
        System.out.println("....hieght = "+ hieght);
        System.out.println("....actualhieght = "+ actualHeight);
        System.out.println("....proposedheight = "+ proposedheight);
        if (actualHeight > proposedheight){
            // Keyboard is shown


        } else if(actualHeight<proposedheight){
            // Keyboard is hidden

        }

        if(proposedheight == hieght){
             // Keyboard is hidden
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

2
android:windowSoftInputMode = "adjustPan"では機能しません。ソフトキーボードが表示された後、画面が縮小しないようにしたいのですが。adjustPanでも機能するように修正を教えていただけますか
Shirish Herwade 2013

関数hide / showの場合、このリスナーメソッドは2回または3回呼び出されます。正確には何が問題なのかわかりません。
Jagveer Singh Rajput 2014

2

また、最初のDecorViewの子の下のパディングを確認することもできます。キーボードが表示されると、ゼロ以外の値に設定されます。

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    View view = getRootView();
    if (view != null && (view = ((ViewGroup) view).getChildAt(0)) != null) {
        setKeyboardVisible(view.getPaddingBottom() > 0);
    }
    super.onLayout(changed, left, top, right, bottom);
}

1

キーボードのHide | ShowイベントはOnGlobalLayoutListenerの簡単なハックでリッスンできます:

 final View activityRootView = findViewById(R.id.top_root);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                int heightDiff = activityRootView.getRootView().getHeight() - activityRootView.getHeight();

                if (heightDiff > 100) {
                    // keyboard is up
                } else {
                    // keyboard is down
                }
            }
        });

ここでactivityRootViewは、アクティビティのルートビューです。


私のheightDiffは最初は160で、kbdは742なので、最初にinitialHeightDiffを導入して設定する必要がありました
djdance

0

キーボードイベントを簡単に取得するには、viewTreeObserverを使用します。

layout_parent.viewTreeObserver.addOnGlobalLayoutListener {
            val r = Rect()
            layout_parent.getWindowVisibleDisplayFrame(r)
            if (layout_parent.rootView.height - (r.bottom - r.top) > 100) { // if more than 100 pixels, its probably a keyboard...
                Log.e("TAG:", "keyboard open")
            } else {
                Log.e("TAG:", "keyboard close")
            }
        }

** layout_parentは次のようなビューですedit_text.parent


-2

Nebojsa Tomcicの答えは私には役に立たなかった。私が持っているRelativeLayoutTextViewし、AutoCompleteTextViewその中。TextViewキーボードが表示されているときと非表示のときに、を一番下までスクロールする必要があります。これを達成するために私はonLayoutメソッドをオーバーライドしましたが、それは私にとってはうまくいきます。

public class ExtendedLayout extends RelativeLayout
{
    public ExtendedLayout(Context context, AttributeSet attributeSet)
    {
        super(context, attributeSet);
        LayoutInflater inflater = (LayoutInflater)
                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        inflater.inflate(R.layout.main, this);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);

        if (changed)
        {
            int scrollEnd = (textView.getLineCount() - textView.getHeight() /
                textView.getLineHeight()) * textView.getLineHeight();
            textView.scrollTo(0, scrollEnd);
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.