如何捕捉Android中的“虚拟键盘显示/隐藏”事件?

我想根据是否显示虚拟键盘来改变布局。 我已经搜索了API和各种博客,但似乎无法找到任何有用的东西。

可能吗?

谢谢!

你必须自己处理配置更改。

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

然后,只需更改某些视图的可见性,更新字段并更改布局文件即可。

注意

此解决方案不适用于软键盘, onConfigurationChanged不适用于软键盘。

这可能不是最有效的解决方案。 但是,这每次都为我工作…我呼吁这个功能,我需要听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; } } }); } 

注意:如果用户使用浮动键盘,则此方法会导致问题。

如果你想处理你的活动中的IMM(虚拟)键盘窗口的显示/隐藏,你需要继承你的布局并重写onMesure方法(这样你可以确定测量的宽度和布局的测量高度)。 之后,通过setContentView()将subclassed布局设置为Activity的主视图。 现在您将能够处理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> 

现在在你的Activity中为你的布局声明子类(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); } } 

你可以从代码中看到,我们在子类的构造函数中为我们的Activity充气布局

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

基于来自Nebojsa Tomcic的代码,我开发了以下RelativeLayout子类:

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

这工作得很好…马克,当你的Activity的软输入模式设置为“WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE”

就像@ 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。

Nebojsa的解决方案几乎为我工作。 当我点击一个多行的EditText时,它知道键盘被显示,但是当我开始在EditText里面输入时,actualHeight和proposedHeight仍然是一样的,所以它不知道它们仍然显示键盘。 我做了一个微小的修改来存储最大高度,它工作正常。 这是修改后的子类:

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

不知道是否有人张贴这个。 找到这个简单易用的解决方案! 。 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 ... } }); } }); 

我通过在我的自定义EditText中覆盖onKeyPreIme(int keyCode,KeyEvent事件)来解决这个问题。

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

我这样做了:

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

希望这会帮助你。

我有一个黑客要做到这一点。 虽然似乎没有办法检测到软键盘显示或隐藏的时间,但实际上,您可以通过在正在监听的EditText上设置OnFocusChangeListener来检测何时显示或隐藏软键盘。

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

注意:有一件事要注意这个黑客是这个回调是在EditText获得或失去焦点时立即触发的。 这实际上会在软键盘显示或隐藏之前触发。 在键盘显示或隐藏之后 ,我发现最好的方法是使用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); } }); 

桑德,我相信你试图显示被软键盘阻挡的视图。 试试这个http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html

我已经解决了单行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); } } 

您也可以检查第一个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); } 

隐藏|显示键盘的事件可以通过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是你Activity的根视图。

Nebojsa Tomcic的回答对我没有帮助。 里面有TextViewAutoCompleteTextView RelativeLayout 。 当显示键盘和隐藏时,我需要将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); } } }