在項(xiàng)目開發(fā)中,用到編輯框的地方經(jīng)常涉及到要監(jiān)聽或者控制軟鍵盤的顯示/隱藏狀態(tài)。本以為這是很容易解決的一個(gè)小問題,沒想到當(dāng)初碰到這個(gè)問題才明白還得花點(diǎn)小心思才能整好?,F(xiàn)將針對(duì)軟鍵盤的顯示/隱藏狀態(tài)的監(jiān)聽/監(jiān)控方法做一些總結(jié),以備后用。
10年積累的成都網(wǎng)站建設(shè)、成都網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有紅崗免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
一、點(diǎn)擊空白處隱藏軟鍵盤
這是具有編輯框焦點(diǎn)的頁面對(duì)輸入法軟鍵盤狀態(tài)監(jiān)聽的一般需求和解決方法.
首先獲得InputMethodManager:
InputMethodManager manager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
監(jiān)聽點(diǎn)擊:
/** * 點(diǎn)擊監(jiān)聽 */ @Override public boolean onTouchEvent(MotionEvent event) { onHideSoftInput(event); return super.onTouchEvent(event); } /** * 點(diǎn)擊空白處,關(guān)閉輸入法軟鍵盤 */ public void onHideSoftInput(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (getCurrentFocus() != null && getCurrentFocus().getWindowToken() != null) { manager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } }
二、popwindow與輸入法軟鍵盤的結(jié)合
先說下自己想實(shí)現(xiàn)的效果:點(diǎn)擊頂部按鈕,打開編輯菜單popwindow并自動(dòng)彈出軟鍵盤;再次點(diǎn)擊頂部按鈕,或者點(diǎn)擊編輯菜單popwindow上面的底部按鈕,關(guān)閉菜單并隱藏軟鍵盤;菜單打開狀態(tài),點(diǎn)擊返回鍵,若菜單已顯示先關(guān)閉軟鍵盤,再點(diǎn)擊則關(guān)閉菜單。
大致效果圖如下:
1.重寫根布局,監(jiān)聽根布局高度變化
對(duì)于這個(gè)需求,簡單的用上面第一點(diǎn)的方法是無效的。這里沒法直接通過getCurrentFocus()方法判斷頁面是否獲取焦點(diǎn)來控制,需要通過對(duì)popwindow的高度變化進(jìn)行判斷。同時(shí)也試過下面的方法,同樣無效。
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); boolean isOpen=imm.isActive();//isOpen若返回true,則表示輸入法打開
popwindow的根布局我這里用的是RelativeLayout,RelativeLayout類可以通過重寫onSizeChanged方法來監(jiān)聽布局大小變化。重寫一個(gè)RelativeLayout類便實(shí)現(xiàn)了對(duì)popwindow的高度變化的監(jiān)聽了。
代碼如下:
/** * 監(jiān)聽輸入法軟鍵盤顯示狀態(tài)的自定義RelativeLayout * * @author zeng * */ public class ResizeRelativeLayout extends RelativeLayout { public ResizeRelativeLayout(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mListener != null) { mListener.OnResizeRelative(w, h, oldw, oldh); } } // 監(jiān)聽接口 private OnResizeRelativeListener mListener; public interface OnResizeRelativeListener { void OnResizeRelative(int w, int h, int oldw, int oldh); } public void setOnResizeRelativeListener(OnResizeRelativeListener l) { mListener = l; } }
2.配置布局文件,初始化UI等,代碼如下:
初始化UI代碼:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initUI(); } private void initUI() { mBtn_open = findViewById(R.id.button1); mBtn_open.setOnClickListener(this); // 編輯窗口 LayoutInflater inflater = getLayoutInflater(); View menuLayout = inflater.inflate(R.layout.menu_window, null); mEditMenuWindow = new PopupWindow(menuLayout, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, true); mEditMenuWindow.setBackgroundDrawable(getResources().getDrawable(R.color.white)); mEditMenuWindow.setTouchable(true); mEditMenuWindow.setFocusable(true); mEditMenuWindow.setAnimationStyle(R.style.MenuAnimation); mEditMenuWindow.setOutsideTouchable(false); mEditMenuWindow.update(); //監(jiān)聽菜單消失 mEditMenuWindow.setOnDismissListener(this); // 菜單控件 mEt_menu = (EditText) menuLayout.findViewById(R.id.menu_edit); TextView btn_send = (TextView) menuLayout.findViewById(R.id.menu_send); btn_send.setOnClickListener(this); // 監(jiān)聽主布局大小變化,監(jiān)控輸入法軟鍵盤狀態(tài) listenerKeyBoardState(menuLayout); }
其中menu_window.xml文件代碼如下:
3.打開或關(guān)閉編輯窗口,同時(shí)自動(dòng)顯示或隱藏輸入法軟鍵盤。
此方法兩者的控制順序:先顯示軟鍵盤,再打開編輯窗口;先關(guān)閉編輯窗口,若軟鍵盤當(dāng)前已顯示則再隱藏軟鍵盤。代碼如下:
//點(diǎn)擊頂部發(fā)送按鈕,打開/關(guān)閉編輯窗口 private void clickTopSend() { if (mEditMenuWindow.isShowing()) { //先關(guān)閉窗口再隱藏軟鍵盤 mEditMenuWindow.dismiss(); // 隱藏輸入法軟鍵盤 // hideKeyBoard(); } else { // 窗口顯示前顯示輸入法軟鍵盤 showKeyBoard(); // 顯示輸入窗口 mEditMenuWindow.showAsDropDown(mBtn_open, 0, 0); } } // 窗口顯示前顯示輸入法軟鍵盤 private void showKeyBoard() { InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);// 調(diào)用此方法才能自動(dòng)打開輸入法軟鍵盤 mEditMenuWindow.setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mEditMenuWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // 在顯示popupwindow之后調(diào)用,否則輸入法會(huì)在窗口底層 } // 隱藏輸入法軟鍵盤 private void hideKeyBoard() { InputMethodManager inputMgr = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMgr.toggleSoftInput(InputMethodManager.HIDE_NOT_ALWAYS, 0);// 輸入法軟鍵盤打開時(shí)關(guān)閉,關(guān)閉時(shí)打開 mEditMenuWindow.setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mEditMenuWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); // 在顯示popupwindow之后調(diào)用,否則輸入法會(huì)在窗口底層 //此方法無效 // if(this.getCurrentFocus().getWindowToken() != null) // { // ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), // InputMethodManager.HIDE_NOT_ALWAYS);// 關(guān)閉輸入法軟鍵盤 // } } //點(diǎn)擊底部發(fā)送按鈕,關(guān)閉編輯窗口 private void closeButtomSend() { mEditMenuWindow.dismiss(); } //編輯窗口關(guān)閉時(shí),隱藏輸入法軟鍵盤 @Override public void onDismiss() { // 如果軟鍵盤打開,隱藏輸入法軟鍵盤 if(mIsKeyboardOpened) { hideKeyBoard(); } }
4.判斷軟鍵盤的顯示/隱藏狀態(tài)。
通過對(duì)編輯窗口的根布局高度變化來判斷軟鍵盤是否處于顯示狀態(tài),在接口方法OnResizeRelative(int w, int h, int oldw, int oldh)里實(shí)現(xiàn)對(duì)高度變化的判斷。這里主要判斷以下幾種高度變化場景:
(1)布局的當(dāng)前高度小于上一次的高度,即h < oldh,因?yàn)椴季直卉涙I盤頂上去了,高度變小了。這種場景同樣適用于點(diǎn)擊按鈕第一次打開窗口時(shí)的場景,雖然點(diǎn)擊按鈕時(shí),肉眼看到的是窗口一下就充滿了大半個(gè)屏幕,也就是當(dāng)前h > oldh(oldh = 0)。但事實(shí)上是,第一次打開窗口時(shí),窗口菜單首先是充滿整個(gè)屏幕然后再根據(jù)軟鍵盤高度自動(dòng)縮進(jìn)的。以下是點(diǎn)擊【打開/發(fā)送】按鈕后,根布局的高度變化日志:
可以看出,首次打開時(shí),窗口高度先是1038,然后自動(dòng)縮進(jìn)成544,并非一打開便已計(jì)算好填充高度。
(2)還有一種特殊情況,就是三星輸入法在軟鍵盤初次打開時(shí),輸入字符后軟鍵盤高度會(huì)產(chǎn)生變化,同時(shí)造成根布局高度變??;若再清除已輸入的字符,此時(shí)軟鍵盤高度變小,根布局高度變大。而這兩種情況下,也就是h < oldh 或者 h > oldh時(shí),軟鍵盤都是處于顯示狀態(tài)。
針對(duì)這種情況,我的解決方法是記錄下初次打開時(shí)根布局的初始高度值,由于h < oldh時(shí)在第(1)步已經(jīng)進(jìn)行了判斷了,所以這里只要判斷 h > oldh時(shí)的情況。而無論 h > oldh變化多大,只要h不超過初始高度值(且初始高度值不為0),那么便可認(rèn)為當(dāng)前軟鍵盤仍是處于打開狀態(tài)。
此方法運(yùn)行后的日志如下:
兩種狀況下,軟鍵盤都是顯示狀態(tài)。
以下是監(jiān)聽高度變化判斷的代碼:
/** * 監(jiān)聽主布局大小變化,監(jiān)控輸入法軟鍵盤狀態(tài) * @param menuLayout */ private void listenerKeyBoardState(View menuLayout) { ResizeRelativeLayout mMenuLayout = (ResizeRelativeLayout) menuLayout.findViewById(R.id.menu_layout); mMenuLayout.setOnResizeRelativeListener(new ResizeRelativeLayout.OnResizeRelativeListener() { @Override public void OnResizeRelative(int w, int h, int oldw, int oldh) { mIsKeyboardOpened = false; Log.e("菜單高度", "h = " + h + ",oldh = " + oldh); //記錄第一次打開輸入法時(shí)的布局高度 if (h < oldh && oldh > 0 && mMenuOpenedHeight == 0) { mMenuOpenedHeight = h; } // 布局的高度小于之前的高度 if (h < oldh ) { mIsKeyboardOpened = true; } //或者輸入法打開情況下, 輸入字符后再清除(三星輸入法軟鍵盤在輸入后,軟鍵盤高度增加一行,清除輸入后,高度變小,但是軟鍵盤仍是打開狀態(tài)) else if((h <= mMenuOpenedHeight) && (mMenuOpenedHeight != 0)) { mIsKeyboardOpened = true; } Log.e("是否打開", "軟鍵盤 = " + mIsKeyboardOpened); } }); }
最后附上DEMO源碼,詳見附件。
三、InputMethodManager的一些相關(guān)方法(未有效使用過,僅作筆記)
1.調(diào)用顯示系統(tǒng)默認(rèn)的輸入法
方法一、
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(m_receiverView,InputMethodManager.SHOW_FORCED);
m_receiverView(接受軟鍵盤輸入的視圖(View)
InputMethodManager.SHOW_FORCED(提供當(dāng)前操作的標(biāo)記,SHOW_FORCED表示強(qiáng)制顯示)
方法二、
InputMethodManager m=(InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); m.toggleSoftInput(0, InputMethodManager.HIDE_NOT_ALWAYS);
這個(gè)InputMethodManager類里面的toggleSoftInput方法的API中的解釋是:
This method toggles the input method window display. If the input window is already displayed, it gets hidden. If not the input window will be displayed.
這個(gè)方法在界面上切換輸入法的功能,如果輸入法出于現(xiàn)實(shí)狀態(tài),就將他隱藏,如果處于隱藏狀態(tài),就顯示輸入法。
2.調(diào)用隱藏系統(tǒng)默認(rèn)的輸入法
((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(WidgetSearchActivity.this.getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
(WidgetSearchActivity是當(dāng)前的Activity)
3.獲取輸入法打開的狀態(tài)
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); boolean isOpen=imm.isActive();
isOpen若返回true,則表示輸入法打開
四、SoftInputMode輸入法軟鍵盤模式相關(guān)說明
輸入法軟鍵盤模式選項(xiàng):
public int softInputMode;
以下與輸入法模式有關(guān)的各選項(xiàng)說明:
軟輸入?yún)^(qū)域是否可見。
public static final int SOFT_INPUT_MASK_STATE = 0x0f;
未指定狀態(tài)。
public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
不要修改軟輸入法區(qū)域的狀態(tài)。
public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
隱藏輸入法區(qū)域(當(dāng)用戶進(jìn)入窗口時(shí))。
public static final int SOFT_INPUT_STATE_HIDDEN = 2;
當(dāng)窗口獲得焦點(diǎn)時(shí),隱藏輸入法區(qū)域。
public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
顯示輸入法區(qū)域(當(dāng)用戶進(jìn)入窗口時(shí))。
public static final int SOFT_INPUT_STATE_VISIBLE = 4;
當(dāng)窗口獲得焦點(diǎn)時(shí),顯示輸入法區(qū)域。
public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
窗口應(yīng)當(dāng)主動(dòng)調(diào)整,以適應(yīng)軟輸入窗口。
public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;
未指定狀態(tài),系統(tǒng)將根據(jù)窗口內(nèi)容嘗試選擇一個(gè)輸入法樣式。
public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
當(dāng)輸入法顯示時(shí),允許窗口重新計(jì)算尺寸,使內(nèi)容不被輸入法所覆蓋。
不可與SOFT_INPUT_ADJUSP_PAN混合使用;如果兩個(gè)都沒有設(shè)置,系統(tǒng)將根據(jù)窗口內(nèi)容自動(dòng)設(shè)置一個(gè)選項(xiàng)。
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
輸入法顯示時(shí)平移窗口。它不需要處理尺寸變化,框架能夠移動(dòng)窗口以確保輸入焦點(diǎn)可見。
不可與SOFT_INPUT_ADJUST_RESIZE混合使用;如果兩個(gè)都沒有設(shè)置,系統(tǒng)將根據(jù)窗口內(nèi)容自動(dòng)設(shè)置一個(gè)選項(xiàng)。
public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
當(dāng)用戶轉(zhuǎn)至此窗口時(shí),由系統(tǒng)自動(dòng)設(shè)置,所以你不要設(shè)置它。
當(dāng)窗口顯示之后該標(biāo)志自動(dòng)清除。
public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;