開局就是二張圖。先看效果是不是自己想要的,然後再慢慢往下看。第一張圖是默認高度,第二張圖是高度增加之後的變化。可以看到,切換還是蠻順暢的,沒有出現跳閃的情況。
這兩天在弄一個留言板功能,可進行文字、語音、圖片還有其他功能的留言。這時就需要一個面板來存放這些附加功能。一般都是輸入框旁邊有個加號,來進行面板的彈出。但是我當我底部面板出現時,會出現跳閃問題,就是面板和輸入框會被軟鍵盤頂到上面去,然後等軟鍵盤收起來,面板才降下來,這種效果體驗起來不是很好,就先自己想了想,但也沒想到好的解決辦法,又看了看微信、QQ、支付寶的聊天界面,體驗效果很好,並且當軟鍵盤的高度變化時,面板的高度也會隨之變化。體驗效果很好。
算了,還是百度,搜了搜知道了大致的解決方案。就是當面板和輸入框切換時去改變軟鍵盤的顯示模式。一般我們改變軟鍵盤的顯示模式都是在清單文件AndroidManifest.xml中對Activity的windowSoftInputMode標籤進行修改。但這次我們需要在代碼總動態的去改變。主要是針對這兩種模式進行改變。如下
當屬性爲adjustResize的時候,軟鍵盤彈出時會擠壓Activity窗口大小,這樣可以保證輸入法不會覆蓋到輸入框;
當屬性爲adjustNothing的時候,軟鍵盤彈出時會覆蓋到當前窗口上,窗口大小不變,這種情況輸入法有可能會覆蓋輸入框。
知道解決的關鍵點之後,還需知道軟鍵盤的高度,以及軟鍵盤的隱藏和顯示狀態。這樣就可以對不同的情況進行相應的改變。
先上代碼獲取軟鍵盤的高度和顯示、隱藏狀態的代碼。
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;
private int getScreenHeight() {
if (mScreenHeight > 0) {
return mScreenHeight;
}
mScreenHeight = ((WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getHeight();
return mScreenHeight;
}
@Override
public void onGlobalLayout() {
Rect rect = new Rect();
// 獲取當前頁面窗口的顯示範圍
((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int screenHeight = getScreenHeight();
int keyboardHeight = screenHeight - rect.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);
}
}
這個類是一個自定義佈局類,只是用來監聽軟鍵盤的狀態,使用時直接放到對應Activity的佈局下即可。不需用給寬高屬性。
<com.dfzb.bloodinstitutepatient.widget.KeyboardLayout
android:id="@+id/conversation_keyboard_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
主體的邏輯代碼。
public class ConversationActivity extends BaseActivity {
//底部面板View
@BindView(R.id.conversation_rl_bottom_other)
LinearLayout llBottomPanel;
//輸入框
@BindView(R.id.conversation_et_chat)
EditText et;
//內容顯示區域,當點擊此區域時,隱藏軟鍵盤和麪板
@BindView(R.id.conversation_rl_content)
RelativeLayout rlContent;
//監聽軟鍵盤View
@BindView(R.id.conversation_keyboard_layout)
KeyboardLayout keyboardLayout;
private int llBottomOtherHeight;
private int screenHeight = 0;
private boolean isShowingInput = false;//軟鍵盤是否正在顯示
private Context context = ConversationActivity.this;
private int mKeyboardHeight = 400; // 輸入法默認高度爲400
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_conversation);
ButterKnife.bind(this);
initTitleBar(true, true, "留言板");
initData();
initView();
}
private void initData() {
llBottomOtherHeight = DimesionUtils.getDimensionPixelSize(R.dimen.x550);
//獲取屏幕高度
screenHeight = this.getWindowManager().getDefaultDisplay().getHeight();
}
private void initView() {
rootView.addOnLayoutChangeListener(this);
et.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//點擊輸入框時,軟鍵盤會直接彈出。如果面板顯示,則進行300ms的延時隱藏,防止跳閃。
if (llBottomPanel.getVisibility() == View.VISIBLE) {
llBottomPanel.postDelayed(mHideEmotionPanelTask, 300);
}
}
});
keyboardLayout.setKeyboardListener(new KeyboardLayout.KeyboardLayoutListener() {
@Override
public void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {
isShowingInput = isActive;
if (isActive) { // 輸入法打開
Logger.i("", "-----輸入法打開--鍵盤高度:" + keyboardHeight);
if (mKeyboardHeight != keyboardHeight) { // 鍵盤發生改變時才設置底部面板的高度,因爲會觸發onGlobalLayoutChanged,導致onKeyboardStateChanged再次被調用
mKeyboardHeight = keyboardHeight;
updatePanelHeight(); // 每次輸入法彈起時,設置底部面板的高度爲鍵盤的高度,以便下次底部面板彈出時剛好等於鍵盤高度
}
} else {
Logger.i("", "-----輸入法關閉--鍵盤高度:" + keyboardHeight);
}
}
});
//觸摸空白處進行軟鍵盤和麪板的隱藏
rlContent.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (getCurrentFocus() != null && getCurrentFocus().getWindowToken() != null) {
hidePanelOrInput();
}
}
return false;
}
});
}
// 每當輸入框高度發生變化時,去改變面板的高度
private void updatePanelHeight() {
ViewGroup.LayoutParams layoutParams = llBottomPanel.getLayoutParams();
layoutParams.height = mKeyboardHeight;
llBottomOtherHeight = mKeyboardHeight;//將面板的高度設置爲軟鍵盤高度
llBottomPanel.setLayoutParams(layoutParams);
}
private Runnable mHideEmotionPanelTask = new Runnable() {
@Override
public void run() {
hidePanel(false, 0);
}
};
@OnClick({R.id.conversation_iv_add, R.id.conversation_iv_switch})
public void onClick(View view) {
switch (view.getId()) {
case R.id.conversation_iv_add:
if (isShowingInput) {//如果鍵盤顯示
if (llBottomPanel.getVisibility() == View.GONE) {//面板隱藏,則顯示面板
showPanel(false, 0);
}
} else {//鍵盤不顯示
if (llBottomPanel.getVisibility() == View.VISIBLE) {//如果面板顯示 ,則隱藏面板,顯示鍵盤
showInput();
llBottomPanel.postDelayed(mHideEmotionPanelTask, 300);
} else {
showPanel(true, 300);
}
}
break;
case R.id.conversation_iv_switch:
hidePanelOrInput();
break;
default:
break;
}
}
/**
* 顯示底部面板 (如果鍵盤已經出現,此時展示面板不需用動畫,直接展示即可,如果鍵盤未顯示,則增加一個向上的動畫)
*
* @param isAnimal 是否以動畫形式展現
* @param duration 動畫時長(默認爲300ms)
*/
private void showPanel(boolean isAnimal, long duration) {
//設置軟鍵盤爲覆蓋模式
updateSoftInputMethod(this, WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
if (isAnimal) {
ValueAnimator animator = ValueAnimator.ofInt(0, llBottomOtherHeight);
setAnimaUpdateListener(animator, llBottomPanel);
animator.setDuration(duration);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
llBottomPanel.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animator animation) {
}
});
animator.start();
} else {
//恢復面板高度
ViewGroup.LayoutParams layoutParams = llBottomPanel.getLayoutParams();
layoutParams.height = llBottomOtherHeight;
llBottomPanel.setLayoutParams(layoutParams);
llBottomPanel.setVisibility(View.VISIBLE);
hideInput();
}
}
/**
* 隱藏底部面板 (如果鍵盤已經出現,此時隱藏面板不需用動畫,直接隱藏即可,如果鍵盤未顯示,則增加一個向下的動畫)
*
* @param isAnimal 是否以動畫形式展現
* @param duration 動畫時長(默認爲300ms)
*/
private void hidePanel(boolean isAnimal, long duration) {
//設置軟鍵盤爲擠壓模式
updateSoftInputMethod(this, WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
if (isAnimal) {
ValueAnimator animator = ValueAnimator.ofInt(llBottomOtherHeight, 0);
setAnimaUpdateListener(animator, llBottomPanel);
animator.setDuration(duration);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
llBottomPanel.setVisibility(View.GONE);
}
});
animator.start();
} else {
if (llBottomPanel.getVisibility() == View.VISIBLE) {
llBottomPanel.setVisibility(View.GONE);
}
}
}
//更改輸入法軟鍵盤彈出方式
public static void updateSoftInputMethod(Activity activity, int softInputMode) {
if (!activity.isFinishing()) {
WindowManager.LayoutParams params = activity.getWindow().getAttributes();
if (params.softInputMode != softInputMode) {
params.softInputMode = softInputMode;
activity.getWindow().setAttributes(params);
}
}
}
//隱藏軟鍵盤
protected void hideInput() {
InputMethodManager imm = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(et.getWindowToken(), 0);
}
//顯示軟鍵盤
protected void showInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(et, InputMethodManager.SHOW_FORCED);
}
//屬性動畫是可以直接改變view的寬高的 通過不斷改變view的屬性來產生動畫的效果
public void setAnimaUpdateListener(ValueAnimator valueAnimator, final View view) {
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator arg0) {
int value = (int) arg0.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
layoutParams.height = value;
view.setLayoutParams(layoutParams);
}
});
}
//隱藏輸入框或底部面板
public void hidePanelOrInput() {
//點擊空白區域 如果軟鍵盤顯示則隱藏
if (isShowingInput) {//
hideInput();
}
//如果面板顯示,則進行下降動畫的隱藏
if (llBottomPanel.getVisibility() == View.VISIBLE) {
hidePanel(true, 300);
}
}
@Override
public void onBackPressed() {
if (llBottomPanel.getVisibility() == View.VISIBLE) {
hidePanel(true, 300);
return;
}
super.onBackPressed();
}
}
佈局代碼我就不貼了,主要就是Activity代碼。上面我都寫了相應的註釋。這裏面我還增加了底部面板的顯示和隱藏動畫(屬性動畫),這樣不會太生硬。當軟鍵盤隱藏時,顯示面板和隱藏面板都會以動畫形式展現。當軟鍵盤顯示時,則直接顯示和隱藏面板。
還有一個特別需要注意的地方。需要在清單文件中對Activity設置一個默認的windowSoftInputMode,不然跳閃的情況就還會出現。
嗯,差不多到這就結束了。有不懂的地方可以加我 QQ1902366297