在QQ或者微信的聊天頁面,當輸入法和表情欄互相切換時,過分很是天然,並且表情欄高度恰好跟輸入法同樣。我的感受這種用戶體驗特別的好,別看這個細節小,但代碼實現處理起來仍是有必定難度。今天就帶你們來實現這種效果,下面是效果圖:php
首先,咱們須要知道輸入法的高度,使表情欄的高度與之保持一致。可是Android是沒有提供現成的接口給開發者監聽輸入法的狀態,所以須要自定義的KeyboardLayout,監聽佈局的改變。視圖樹ViewTreeObserver發生變化時會回調OnGlobalLayoutListener.onGlobalLayout()方法,經過變化先後佈局高度差計算出輸入法的高度。java
public class KeyboardLayout extends FrameLayout {
private KeyboardLayoutListener mListener;
private boolean mIsKeyboardActive = false; // 輸入法是否激活
private int mKeyboardHeight = 0; // 輸入法高度
public KeyboardLayout(Context context) {
this(context, null, 0);
}
public KeyboardLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public KeyboardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 經過視圖樹監聽佈局變化
getViewTreeObserver().addOnGlobalLayoutListener(new KeyboardOnGlobalChangeListener());
}
private class KeyboardOnGlobalChangeListener implements ViewTreeObserver.OnGlobalLayoutListener {
int mScreenHeight = 0;
Rect mRect = new Rect();
private int getScreenHeight() {
if (mScreenHeight > 0) {
return mScreenHeight;
}
mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getHeight();
return mScreenHeight;
}
@Override
public void onGlobalLayout() {
// // 獲取當前頁面窗口的顯示範圍
getWindowVisibleDisplayFrame(mRect);
int screenHeight = getScreenHeight(); //屏幕高度
int keyboardHeight = screenHeight - mRect.bottom; // 輸入法的高度
boolean isActive = false;
if (Math.abs(keyboardHeight) > screenHeight / 5) {
isActive = true; // 超過屏幕五分之一則表示彈出了輸入法
mKeyboardHeight = keyboardHeight;
}
mIsKeyboardActive = isActive;
if (mListener != null) {
mListener.onKeyboardStateChanged(isActive, keyboardHeight);
}
}
}
public void setKeyboardListener(KeyboardLayoutListener listener) {
mListener = listener;
}
public KeyboardLayoutListener getKeyboardListener() {
return mListener;
}
public boolean isKeyboardActive() {
return mIsKeyboardActive;
}
/** * 獲取輸入法高度 * * @return */
public int getKeyboardHeight() {
return mKeyboardHeight;
}
public interface KeyboardLayoutListener {
/** * @param isActive 輸入法是否激活 * @param keyboardHeight 輸入法面板高度 */
void onKeyboardStateChanged(boolean isActive, int keyboardHeight);
}
}
複製代碼
KeyboardLayout加入佈局文件中便可,無其餘使用限制。從代碼中可知,當佈局變化時並不須要知道KeyboardLayout的高度來計算輸入法高度,KeyboardLayout只是充當一個佈局監聽器的做用。android
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
...
<cn.forward.androids.views.KeyboardLayout android:id="@+id/keyboard_layout" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
...
</FrameLayout>
複製代碼
mKeyboardLayout = (KeyboardLayout) findViewById(R.id.keyboard_layout);
mKeyboardLayout.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() {
@Override
public void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {
if (isActive) { // 輸入法打開
//do something
}else {
}
}
});
複製代碼
輸入法和表情欄切換時,若是隻是簡單的在切換到輸入法時隱藏表情欄,或者切換到表情欄時隱藏輸入法,這樣過分過程形成佈局閃爍一下,以下所示:git
這樣的效果簡直會逼死像我這樣有強迫症的人,所以咱們須要解決它!形成這種問題的緣由是,在顯示錶情欄時,輸入法還沒消失,所以表情欄會出如今輸入法上面,當輸入法消失時,表情欄的位置又被從新調整到底部,所以會形成佈局閃爍,同理能夠解釋切換到輸入法時形成閃爍的緣由。解決問題的關鍵主要靠以下兩句代碼:github
// 設置輸入法彈起時自動調整佈局,使之在輸入法之上
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
// 設置輸入法彈起時不調整當前佈局
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
複製代碼
當從輸入法切換到表情欄時,設置佈局爲不會從新調整SOFT_INPUT_ADJUST_NOTHING,同時隱藏輸入法,顯示錶情欄,這樣當再次切換輸入法時,恰好輸入法能夠擋住表情欄,再把佈局設爲可自動調整SOFT_INPUT_ADJUST_RESIZE,代碼以下:微信
public class KeyboardLayoutDemo extends Activity {
private KeyboardLayout mKeyboardLayout;
private View mEmojiView;
private Button mEmojiBtn;
private EditText mInput;
int mKeyboardHeight = 400; // 輸入法默認高度爲400
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_keyboard_layout);
// 起初的佈局可自動調整大小
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
mKeyboardLayout = (KeyboardLayout) findViewById(R.id.keyboard_layout);
mEmojiView = findViewById(R.id.emoji);
mEmojiBtn = (Button) findViewById(R.id.emoji_btn);
mInput = (EditText) findViewById(R.id.input);
// 點擊輸入框
mInput.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mKeyboardLayout.postDelayed(new Runnable() {
@Override
public void run() { // 輸入法彈出以後,從新調整
mEmojiBtn.setSelected(false);
mEmojiView.setVisibility(View.GONE);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
}, 250); // 延遲一段時間,等待輸入法徹底彈出
}
});
mEmojiBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mEmojiBtn.setSelected(!mEmojiBtn.isSelected());
if (mKeyboardLayout.isKeyboardActive()) { // 輸入法打開狀態下
if (mEmojiBtn.isSelected()) { // 打開表情
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); // 不改變佈局,隱藏鍵盤,emojiView彈出
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mInput.getApplicationWindowToken(), 0);
mEmojiView.setVisibility(View.VISIBLE);
} else {
mEmojiView.setVisibility(View.GONE);
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mInput.getApplicationWindowToken(), 0);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
} else { // 輸入法關閉狀態下
if (mEmojiBtn.isSelected()) {
// 設置爲不會調整大小,以便輸入彈起時佈局不會改變。若不設置此屬性,輸入法彈起時佈局會閃一下
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
mEmojiView.setVisibility(View.VISIBLE);
} else {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
mEmojiView.setVisibility(View.GONE);
}
}
}
});
mKeyboardLayout.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() {
@Override
public void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {
if (isActive) { // 輸入法打開
if (mKeyboardHeight != keyboardHeight) { // 鍵盤發生改變時才設置emojiView的高度,由於會觸發onGlobalLayoutChanged,致使onKeyboardStateChanged再次被調用
mKeyboardHeight = keyboardHeight;
initEmojiView(); // 每次輸入法彈起時,設置emojiView的高度爲鍵盤的高度,以便下次emojiView彈出時恰好等於鍵盤高度
}
if (mEmojiBtn.isSelected()) { // 表情打開狀態下
mEmojiView.setVisibility(View.GONE);
mEmojiBtn.setSelected(false);
}
}
}
});
}
// 設置表情欄的高度
private void initEmojiView() {
ViewGroup.LayoutParams layoutParams = mEmojiView.getLayoutParams();
layoutParams.height = mKeyboardHeight;
mEmojiView.setLayoutParams(layoutParams);
}
}
複製代碼
(Androids是本人根據平時的項目實踐經驗,爲了提升Android開發效率而寫的一個工具SDK;裏面提供了一些工具類以及自定義View,可在實際項目開發時直接使用。)工具