Android 解決表情皮膚和軟鍵盤切換時跳閃的問題

leavesC發表於2019-02-21

現在很多應用都會在讓使用者輸入各種文字資訊的時候同時多提供一個表情皮膚,這樣就會出現一個問題,即表情皮膚的跳閃問題
要輸入文字資訊,那固然是需要彈出軟鍵盤,在軟鍵盤顯示的情況下,此時如果要切換顯示出表情皮膚,由於表情皮膚不可能和使用者的軟鍵盤高度恰好一樣,此外由於控制元件的上下移位,就會出現表情皮膚的跳閃現象

在點選切換按鈕的時候,表情皮膚會先向上跳,然後再往下移,這樣就會帶來很差的使用者體驗,效果如下圖所示:

Android 解決表情皮膚和軟鍵盤切換時跳閃的問題

這裡提供一個解決方案,使軟鍵盤和表情皮膚可以很自然地切換,效果如下圖所示:

Android 解決表情皮膚和軟鍵盤切換時跳閃的問題

解決思路主要是這樣:系統在彈出軟鍵盤時,會將內容輸入框View以及其之上的View都給頂上去,當切換到表情皮膚時,只有將表情皮膚的高度保持為和軟鍵盤的高度一致,才能自然地切換。此外,還需要將內容輸入框View以及其之上的View的位置固定住,這樣才不會出現跳閃問題

主要的操作邏輯都在 EmojiKeyboard 類中

/**
 * 作者: chenZY
 * 時間: 2017/8/26 18:12
 * 描述:
 */
public class EmojiKeyboard {

    private Activity activity;

    //文字輸入框
    private EditText editText;

    //表情皮膚
    private View emojiPanelView;

    //內容View,即除了表情佈局和輸入框佈局以外的佈局
    //用於固定輸入框一行的高度以防止跳閃
    private View contentView;

    private InputMethodManager inputMethodManager;

    private SharedPreferences sharedPreferences;

    private static final String EMOJI_KEYBOARD = "EmojiKeyboard";

    private static final String KEY_SOFT_KEYBOARD_HEIGHT = "SoftKeyboardHeight";

    private static final int SOFT_KEYBOARD_HEIGHT_DEFAULT = 654;

    private Handler handler;

    public EmojiKeyboard(Activity activity, EditText editText, View emojiPanelView, View emojiPanelSwitchView, View contentView) {
        init(activity, editText, emojiPanelView, emojiPanelSwitchView, contentView);
    }

    private void init(Activity activity, EditText editText, View emojiPanelView, View emojiPanelSwitchView, View contentView) {
        this.activity = activity;
        this.editText = editText;
        this.emojiPanelView = emojiPanelView;
        this.contentView = contentView;
        this.editText.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(final View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_UP && EmojiKeyboard.this.emojiPanelView.isShown()) {
                    lockContentViewHeight();
                    hideEmojiPanel(true);
                    unlockContentViewHeight();
                }
                return false;
            }
        });
        this.contentView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                    if (EmojiKeyboard.this.emojiPanelView.isShown()) {
                        hideEmojiPanel(false);
                    } else if (isSoftKeyboardShown()) {
                        hideSoftKeyboard();
                    }
                }
                return false;
            }
        });
        //用於彈出表情皮膚的View
        emojiPanelSwitchView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (EmojiKeyboard.this.emojiPanelView.isShown()) {
                    lockContentViewHeight();
                    hideEmojiPanel(true);
                    unlockContentViewHeight();
                } else {
                    if (isSoftKeyboardShown()) {
                        lockContentViewHeight();
                        showEmojiPanel();
                        unlockContentViewHeight();
                    } else {
                        showEmojiPanel();
                    }
                }
            }
        });
        this.inputMethodManager = (InputMethodManager) this.activity.getSystemService(Context.INPUT_METHOD_SERVICE);
        this.sharedPreferences = this.activity.getSharedPreferences(EMOJI_KEYBOARD, Context.MODE_PRIVATE);
        this.activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        this.handler = new Handler();
        init();
    }

    /**
     * 如果之前沒有儲存過鍵盤高度值
     * 則在進入Activity時自動開啟鍵盤,並把高度值儲存下來
     */
    private void init() {
        if (!sharedPreferences.contains(KEY_SOFT_KEYBOARD_HEIGHT)) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    showSoftKeyboard(true);
                }
            }, 200);
        }
    }

    /**
     * 當點選返回鍵時需要先隱藏表情皮膚
     */
    public boolean interceptBackPress() {
        if (emojiPanelView.isShown()) {
            hideEmojiPanel(false);
            return true;
        }
        return false;
    }

    /**
     * 鎖定內容View以防止跳閃
     */
    private void lockContentViewHeight() {
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) contentView.getLayoutParams();
        layoutParams.height = contentView.getHeight();
        layoutParams.weight = 0;
    }

    /**
     * 釋放鎖定的內容View
     */
    private void unlockContentViewHeight() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                ((LinearLayout.LayoutParams) contentView.getLayoutParams()).weight = 1;
            }
        }, 200);
    }

    /**
     * 獲取鍵盤的高度
     */
    private int getSoftKeyboardHeight() {
        Rect rect = new Rect();
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        //螢幕當前可見高度,不包括狀態列
        int displayHeight = rect.bottom - rect.top;
        //螢幕可用高度
        int availableHeight = ScreenUtils.getAvailableScreenHeight(activity);
        //用於計算鍵盤高度
        int softInputHeight = availableHeight - displayHeight - ScreenUtils.getStatusBarHeight(activity);
        Log.e("TAG-di", displayHeight + "");
        Log.e("TAG-av", availableHeight + "");
        Log.e("TAG-so", softInputHeight + "");
        if (softInputHeight != 0) {
            // 因為考慮到使用者可能會主動調整鍵盤高度,所以只能是每次獲取到鍵盤高度時都將其儲存起來
            sharedPreferences.edit().putInt(KEY_SOFT_KEYBOARD_HEIGHT, softInputHeight).apply();
        }
        return softInputHeight;
    }

    /**
     * 獲取本地儲存的鍵盤高度值或者是返回預設值
     */
    private int getSoftKeyboardHeightLocalValue() {
        return sharedPreferences.getInt(KEY_SOFT_KEYBOARD_HEIGHT, SOFT_KEYBOARD_HEIGHT_DEFAULT);
    }

    /**
     * 判斷是否顯示了鍵盤
     */
    private boolean isSoftKeyboardShown() {
        return getSoftKeyboardHeight() != 0;
    }

    /**
     * 令編輯框獲取焦點並顯示鍵盤
     */
    private void showSoftKeyboard(boolean saveSoftKeyboardHeight) {
        editText.requestFocus();
        inputMethodManager.showSoftInput(editText, 0);
        if (saveSoftKeyboardHeight) {
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    getSoftKeyboardHeight();
                }
            }, 200);
        }
    }

    /**
     * 隱藏鍵盤
     */
    private void hideSoftKeyboard() {
        inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0);
    }

    /**
     * 顯示錶情皮膚
     */
    private void showEmojiPanel() {
        int softKeyboardHeight = getSoftKeyboardHeight();
        if (softKeyboardHeight == 0) {
            softKeyboardHeight = getSoftKeyboardHeightLocalValue();
        } else {
            hideSoftKeyboard();
        }
        emojiPanelView.getLayoutParams().height = softKeyboardHeight;
        emojiPanelView.setVisibility(View.VISIBLE);
        if (emojiPanelVisibilityChangeListener != null) {
            emojiPanelVisibilityChangeListener.onShowEmojiPanel();
        }
    }

    /**
     * 隱藏表情皮膚,同時指定是否隨後開啟鍵盤
     */
    private void hideEmojiPanel(boolean showSoftKeyboard) {
        if (emojiPanelView.isShown()) {
            emojiPanelView.setVisibility(View.GONE);
            if (showSoftKeyboard) {
                showSoftKeyboard(false);
            }
            if (emojiPanelVisibilityChangeListener != null) {
                emojiPanelVisibilityChangeListener.onHideEmojiPanel();
            }
        }
    }

    public interface OnEmojiPanelVisibilityChangeListener {

        void onShowEmojiPanel();

        void onHideEmojiPanel();
    }

    private OnEmojiPanelVisibilityChangeListener emojiPanelVisibilityChangeListener;

    public void setEmoticonPanelVisibilityChangeListener(OnEmojiPanelVisibilityChangeListener emojiPanelVisibilityChangeListener) {
        this.emojiPanelVisibilityChangeListener = emojiPanelVisibilityChangeListener;
    }

}
複製程式碼

###這裡也提供程式碼下載:Android 解決表情皮膚和軟鍵盤切換時跳閃的問題

相關文章