SoftKeyboard在Android中的活动中打开和closures监听器?
我有Activity
,有5个EditText
。 当用户点击第一个EditText
然后打开软键盘input一些值。 我想设置一些其他View
可见性Gone
当软键盘打开时,用户单击第一个EditText
和软键盘closures从同一个EditText
上回来然后我想设置一些其他View
可见性。
有没有任何听众或callback或任何黑客软键盘打开时点击Android中的第一个EditText
?
这只适用于你的活动的android:windowSoftInputMode
设置为adjustResize
中的adjustResize
。 您可以使用布局侦听器来查看您的活动的根布局是否被键盘resize。
我为我的活动使用类似下面的基类:
public class BaseActivity extends Activity { private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int heightDiff = rootLayout.getRootView().getHeight() - rootLayout.getHeight(); int contentViewTop = getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop(); LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(BaseActivity.this); if(heightDiff <= contentViewTop){ onHideKeyboard(); Intent intent = new Intent("KeyboardWillHide"); broadcastManager.sendBroadcast(intent); } else { int keyboardHeight = heightDiff - contentViewTop; onShowKeyboard(keyboardHeight); Intent intent = new Intent("KeyboardWillShow"); intent.putExtra("KeyboardHeight", keyboardHeight); broadcastManager.sendBroadcast(intent); } } }; private boolean keyboardListenersAttached = false; private ViewGroup rootLayout; protected void onShowKeyboard(int keyboardHeight) {} protected void onHideKeyboard() {} protected void attachKeyboardListeners() { if (keyboardListenersAttached) { return; } rootLayout = (ViewGroup) findViewById(R.id.rootLayout); rootLayout.getViewTreeObserver().addOnGlobalLayoutListener(keyboardLayoutListener); keyboardListenersAttached = true; } @Override protected void onDestroy() { super.onDestroy(); if (keyboardListenersAttached) { rootLayout.getViewTreeObserver().removeGlobalOnLayoutListener(keyboardLayoutListener); } } }
以下示例活动使用此function在显示键盘时隐藏视图,并在隐藏键盘时再次显示该视图。
xml布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/rootLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ScrollView android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" > <!-- omitted for brevity --> </ScrollView> <LinearLayout android:id="@+id/bottomContainer" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <!-- omitted for brevity --> </LinearLayout> </LinearLayout>
而活动:
public class TestActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.test_activity); attachKeyboardListeners(); } @Override protected void onShowKeyboard(int keyboardHeight) { // do things when keyboard is shown bottomContainer.setVisibility(View.GONE); } @Override protected void onHideKeyboard() { // do things when keyboard is hidden bottomContainer.setVisibility(View.VISIBLE); } }
正如维克拉姆在评论中指出的那样,检测软键盘是显示还是已经消失只能通过一些丑陋的黑客来实现。
也许这足以在edittext上设置焦点侦听器 :
yourEditText.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { //got focus } else { //lost focus } } });
一块蛋糕与这个令人敬畏的图书馆: https : //android-arsenal.com/details/1/2519
在Android M上testing
支持min SDK的版本是14.0 for 2.0.0
对于活动:
final View activityRootView = findViewById(R.id.activityRoot); activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect r = new Rect(); activityRootView.getWindowVisibleDisplayFrame(r); int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top); if (heightDiff > 100) { //enter your code here }else{ //enter code for hid } } });
对于片段:
view = inflater.inflate(R.layout.live_chat_fragment, null); view.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. view.getWindowVisibleDisplayFrame(r); int heightDiff = view.getRootView().getHeight() - (r.bottom - r.top); if (heightDiff > 500) { // if more than 100 pixels, its probably a keyboard... } } });
Jaap的答案不适用于AppCompatActivity。 而是获得状态栏和导航栏等的高度,并与您的应用程序的窗口大小进行比较。
像这样:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { // navigation bar height int navigationBarHeight = 0; int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { navigationBarHeight = getResources().getDimensionPixelSize(resourceId); } // status bar height int statusBarHeight = 0; resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { statusBarHeight = getResources().getDimensionPixelSize(resourceId); } // display window size for the app layout Rect rect = new Rect(); getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); // screen height - (user app height + status + nav) ..... if non-zero, then there is a soft keyboard int keyboardHeight = rootLayout.getRootView().getHeight() - (statusBarHeight + navigationBarHeight + rect.height()); if (keyboardHeight <= 0) { onHideKeyboard(); } else { onShowKeyboard(keyboardHeight); } } };
你可以尝试一下:
private void initKeyBoardListener() { // Минимальное значение клавиатуры. Threshold for minimal keyboard height. final int MIN_KEYBOARD_HEIGHT_PX = 150; // Окно верхнего уровня view. Top-level window decor view. final View decorView = getWindow().getDecorView(); // Регистрируем глобальный слушатель. Register global layout listener. decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { // Видимый прямоугольник внутри окна. Retrieve visible rectangle inside window. 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 + MIN_KEYBOARD_HEIGHT_PX) { Log.d("Pasha", "SHOW"); } else if (lastVisibleDecorViewHeight + MIN_KEYBOARD_HEIGHT_PX < visibleDecorViewHeight) { Log.d("Pasha", "HIDE"); } } // Сохраняем текущую высоту view до следующего вызова. // Save current decor view height for the next call. lastVisibleDecorViewHeight = visibleDecorViewHeight; } }); }
使用这个类,
import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; public class SoftKeyboard implements View.OnFocusChangeListener { private static final int CLEAR_FOCUS = 0; private ViewGroup layout; private int layoutBottom; private InputMethodManager im; private int[] coords; private boolean isKeyboardShow; private SoftKeyboardChangesThread softKeyboardThread; private List<EditText> editTextList; private View tempView; // reference to a focused EditText public SoftKeyboard(ViewGroup layout, InputMethodManager im) { this.layout = layout; keyboardHideByDefault(); initEditTexts(layout); this.im = im; this.coords = new int[2]; this.isKeyboardShow = false; this.softKeyboardThread = new SoftKeyboardChangesThread(); this.softKeyboardThread.start(); } public void openSoftKeyboard() { if(!isKeyboardShow) { layoutBottom = getLayoutCoordinates(); im.toggleSoftInput(0, InputMethodManager.SHOW_IMPLICIT); softKeyboardThread.keyboardOpened(); isKeyboardShow = true; } } public void closeSoftKeyboard() { if(isKeyboardShow) { im.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0); isKeyboardShow = false; } } public void setSoftKeyboardCallback(SoftKeyboardChanged mCallback) { softKeyboardThread.setCallback(mCallback); } public void unRegisterSoftKeyboardCallback() { softKeyboardThread.stopThread(); } public interface SoftKeyboardChanged { public void onSoftKeyboardHide(); public void onSoftKeyboardShow(); } private int getLayoutCoordinates() { layout.getLocationOnScreen(coords); return coords[1] + layout.getHeight(); } private void keyboardHideByDefault() { layout.setFocusable(true); layout.setFocusableInTouchMode(true); } /* * InitEditTexts now handles EditTexts in nested views * Thanks to Francesco Verheye (verheye.francesco@gmail.com) */ private void initEditTexts(ViewGroup viewgroup) { if(editTextList == null) editTextList = new ArrayList<EditText>(); int childCount = viewgroup.getChildCount(); for(int i=0; i<= childCount-1;i++) { View v = viewgroup.getChildAt(i); if(v instanceof ViewGroup) { initEditTexts((ViewGroup) v); } if(v instanceof EditText) { EditText editText = (EditText) v; editText.setOnFocusChangeListener(this); editText.setCursorVisible(true); editTextList.add(editText); } } } /* * OnFocusChange does update tempView correctly now when keyboard is still shown * Thanks to Israel Dominguez (dominguez.israel@gmail.com) */ @Override public void onFocusChange(View v, boolean hasFocus) { if(hasFocus) { tempView = v; if(!isKeyboardShow) { layoutBottom = getLayoutCoordinates(); softKeyboardThread.keyboardOpened(); isKeyboardShow = true; } } } // This handler will clear focus of selected EditText private final Handler mHandler = new Handler() { @Override public void handleMessage(Message m) { switch(m.what) { case CLEAR_FOCUS: if(tempView != null) { tempView.clearFocus(); tempView = null; } break; } } }; private class SoftKeyboardChangesThread extends Thread { private AtomicBoolean started; private SoftKeyboardChanged mCallback; public SoftKeyboardChangesThread() { started = new AtomicBoolean(true); } public void setCallback(SoftKeyboardChanged mCallback) { this.mCallback = mCallback; } @Override public void run() { while(started.get()) { // Wait until keyboard is requested to open synchronized(this) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } int currentBottomLocation = getLayoutCoordinates(); // There is some lag between open soft-keyboard function and when it really appears. while(currentBottomLocation == layoutBottom && started.get()) { currentBottomLocation = getLayoutCoordinates(); } if(started.get()) mCallback.onSoftKeyboardShow(); // When keyboard is opened from EditText, initial bottom location is greater than layoutBottom // and at some moment equals layoutBottom. // That broke the previous logic, so I added this new loop to handle this. while(currentBottomLocation >= layoutBottom && started.get()) { currentBottomLocation = getLayoutCoordinates(); } // Now Keyboard is shown, keep checking layout dimensions until keyboard is gone while(currentBottomLocation != layoutBottom && started.get()) { synchronized(this) { try { wait(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } currentBottomLocation = getLayoutCoordinates(); } if(started.get()) mCallback.onSoftKeyboardHide(); // if keyboard has been opened clicking and EditText. if(isKeyboardShow && started.get()) isKeyboardShow = false; // if an EditText is focused, remove its focus (on UI thread) if(started.get()) mHandler.obtainMessage(CLEAR_FOCUS).sendToTarget(); } } public void keyboardOpened() { synchronized(this) { notify(); } } public void stopThread() { synchronized(this) { started.set(false); notify(); } } } }
在Android Manifest
, android:windowSoftInputMode="adjustResize"
是必要的。
/* Somewhere else in your code */ RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use the layout root 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 } @Override public void onSoftKeyboardShow() { // Code here } }); /* Open or close the soft keyboard easily */ softKeyboard.openSoftKeyboard(); softKeyboard.closeSoftKeyboard(); /* Prevent memory leaks:*/ @Override public void onDestroy() { super.onDestroy(); softKeyboard.unRegisterSoftKeyboardCallback(); }
PS – 完全取自这里 。
如果可以,请尝试扩展EditText并覆盖“onKeyPreIme”方法。
@Override public void setOnEditorActionListener(final OnEditorActionListener listener) { mEditorListener = listener; //keep it for later usage super.setOnEditorActionListener(listener); } @Override public boolean onKeyPreIme(final int keyCode, final KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { if (mEditorListener != null) { //you can define and use custom listener, //OR define custom R.id.<imeId> //OR check event.keyCode in listener impl //* I used editor action because of ButterKnife @ mEditorListener.onEditorAction(this, android.R.id.closeButton, event); } } return super.onKeyPreIme(keyCode, event); }
你怎么能扩展它:
- 实现onFocus倾听并声明'onKeyboardShown'
- 声明'onKeyboardHidden'
我想,如前所述,重新计算屏幕高度并不是100%。 要清楚的是,'onKeyPreIme'的重写不会被'隐藏软键盘编程'方法所调用,但是如果你正在做任何事情,你应该在那里做'onKeyboardHidden'逻辑,而不是创build一个全面的解决scheme。
public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mainactivity); attachKeyboardListeners(); .... yourEditText1.setOnFocusChangeListener(new OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { yourEditText2.setVisibility(View.GONE); yourEditText3.setVisibility(View.GONE); yourEditText4.setVisibility(View.GONE); yourEditText5.setVisibility(View.GONE); } else { yourEditText2.setVisibility(View.VISIBLE); yourEditText3.setVisibility(View.VISIBLE); yourEditText4.setVisibility(View.VISIBLE); yourEditText5.setVisibility(VISIBLE); } } }); } }
不幸的是,我对Jaap van Hengstum的回答没有足够的评价。 但我读了一些人的意见,有contentViewTop
总是0
的问题, onShowKeyboard(...)
总是被调用。
我有同样的问题,并找出了我的问题。 我使用AppCompatActivity
而不是“普通” Activity
。 在这种情况下, Window.ID_ANDROID_CONTENT
引用一个ContentFrameLayout
而不是FrameLayout
的右顶端值。 在我的情况下,使用“正常” Activity
是很好的,如果你必须使用另一种活动types(我只是testing了AppCompatActivity
,也许这是其他活动types的问题,如FragmentActivity
),你必须访问FrameLayout
,它是ContentFrameLayout
的祖先。
对于adjustResize
和FragmentActivity从@Jaap接受的解决scheme的情况不适用于我。
这是我的解决scheme:
private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { private int contentDiff; private int rootHeight; @Override public void onGlobalLayout() { View contentView = getWindow().findViewById(Window.ID_ANDROID_CONTENT); if (rootHeight != mDrawerLayout.getRootView().getHeight()) { rootHeight = mDrawerLayout.getRootView().getHeight(); contentDiff = rootHeight - contentView.getHeight(); return; } int newContentDiff = rootHeight - contentView.getHeight(); if (contentDiff != newContentDiff) { if (contentDiff < newContentDiff) { onShowKeyboard(newContentDiff - contentDiff); } else { onHideKeyboard(); } contentDiff = newContentDiff; } } };
当键盘显示
rootLayout.getHeight() < rootLayout.getRootView().getHeight() - getStatusBarHeight()
是真的,否则隐藏
不同的软键盘监听器的实现,不依赖于窗口大小,因此也适用于多窗口的世界。 它当然有它自己的怪癖,但是它们与多窗口和窗口调整 – 一起检测的完全破碎相比毫无意义。
将不胜感激的改进意见!
private boolean isKeyboardShown = false; private int prevContentHeight = 0; private ViewGroup contentLayout; private ViewTreeObserver.OnGlobalLayoutListener keyboardLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int contentHeight = contentLayout.getHeight(); int rootViewHeight = contentLayout.getRootView().getHeight(); if (contentHeight > 0) { if (!isKeyboardShown) { if (contentHeight < prevContentHeight) { isKeyboardShown = true; onShowKeyboard(rootViewHeight - contentHeight); } } else { if (contentHeight > prevContentHeight) { isKeyboardShown = false; onHideKeyboard(); } } prevContentHeight = contentHeight; } } };
我已经修改了Jaap接受的答案。 但在我的情况下,有一些假设,如android:windowSoftInputMode=adjustResize
和键盘不显示在应用程序启动时的开始。 而且,我认为屏幕与父母的身高相符。
contentHeight > 0
这个检查让我知道关于屏幕是隐藏的还是显示为应用键盘事件监听这个特定的屏幕。 另外,我在主Activity的onCreate()
方法中attachKeyboardListeners(<your layout view here>)
中关于屏幕attachKeyboardListeners(<your layout view here>)
。 每当关于屏幕的高度发生变化时,我将它保存到prevContentHeight
variables中,以便稍后检查键盘是显示还是隐藏。
对我来说,到目前为止,它一直工作得很好。 我希望它也适用于其他人。
不同的方法是检查用户何时停止键入…
当TextEdit处于焦点状态(用户正在/正在input)时,您可以隐藏视图(焦点侦听器)
并使用Handler + Runnable和文本更改监听器来closures键盘(不pipe其可见性),并在延迟后显示视图。
要注意的主要是你使用的延迟,这将取决于这些文本编辑的内容。
Handler timeoutHandler = new Handler(); Runnable typingRunnable = new Runnable() { public void run() { // current TextEdit View view = getCurrentFocus(); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); // reset focus view.clearFocus(); // close keyboard (whether its open or not) imm.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.RESULT_UNCHANGED_SHOWN); // SET VIEWS VISIBLE } }; editText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { // SET VIEWS GONE // reset handler timeoutHandler.removeCallbacks(typingRunnable); timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT); } } }); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // Reset Handler... timeoutHandler.removeCallbacks(typingRunnable); } @Override public void afterTextChanged(Editable s) { // Reset Handler Cont. if (editText.getText().toString().trim().length() > 0) { timeoutHandler.postDelayed(typingRunnable, TYPING_TIMEOUT); } } });