Java代码动态设置SoftInputMode,友好进行底部界面呈现

来源:互联网 发布:迷宫寻路算法 编辑:程序博客网 时间:2024/05/16 06:59

前几天在定制短信应用,其中我们经过讨论把短信编辑界面的添加各类附件入口,由原来弹框的形式改为类似微信底部添加附件的方式。涉及到软键盘的呈现以及附件界面的呈现,以及它们之间的切换。如下这几种情况:

这里写图片描述
这个时候如果我点击输入框,希望软键盘能够把聊天界面往上顶起。

这里写图片描述
这个时候如果我点击输入框,希望软键盘能够覆盖在附件界面之上

这里写图片描述
这种时候如果我点击右方的加号按钮,我希望软键盘隐藏,附件直接出现。
如果我按返回按键或者输入法下箭头,整个聊天界面下移到正常。

首先我们先了解软键盘呈现的方式,方式可以在AndroidManifest.xml里面进行配置,
adjustNothing 布局不变,软键盘覆盖当前布局
adjustResize 布局会被软键盘顶起,布局高度缩小
adjustPan 布局高度不变,上移动,腾出足够空间给软键盘,ActionBar上移
adjustUnspecified 系统根据条件选择adjustResize 和 adjustPan

这几种方式也可以在代码里通过调用当前窗口的setSoftInputMode方法进行设置。

了解了以上,我们知道如果想要满足需求,我们有以下几方面需要做到:
1、获取软键盘的高度并且设置附件的高度与它一致
2、在不同的条件下,让软键盘以不同的方式呈现

获取软键盘的高度

有以下两种方式:
1、

        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                //获取屏幕的高度                int screenHeight = ((Activity)context).getWindowManager().getDefaultDisplay().getHeight();                Rect rect = new Rect();                getWindowVisibleDisplayFrame(rect); // 获取可见区域                if (screenHeight - rect.bottom > 0.4 * screenHeight) {                    int softInputHeight = screenHeight - rect.bottom;                    // 通知对附件的高度进行计算等                }            }        });

2、
在最外层布局的onSizeChanged回调方法中,告知监听者高度已改变,由监听者去计算软键盘的高度,分两部分,
a、自定义HeightChangedLinearLayout布局,重写onSizeChanged方法

public class HeightChangedLinearLayout extends LinearLayout {    @Override    protected void onSizeChanged(int w, int h, int oldw, int oldh) {        super.onSizeChanged(w, h, oldw, oldh);        if (DEBUG)Log.d(TAG, "onSizeChanged(): oldw = " + oldw + ", oldh = " + oldh                + ",w = " + w + ", h = " + h);        final int fw = w;        final int fh = h;        final int fow = oldw;        final int foh = oldh;        if (mListener != null) {            new Thread(new Runnable() {                @Override                public void run() {                    mListener.onLayoutSizeChanged(fw, fh, fow, foh);                }            }).start();        }    }}

b、实现监听者

        HeightChangedLinearLayout heightChangedLinearLayout = (HeightChangedLinearLayout)findViewById(R.id.height_change_layout);        heightChangedLinearLayout.setListener(new HeightChangedLinearLayout.LayoutSizeChangedListener() {            @Override            public void onLayoutSizeChanged(int w, int h, int oldw, int oldh) {                int softInputHeight = mSoftInputHeight;                Log.d(TAG, "h: " + h + ", oldh: " + oldh);                if (h - oldh >  SOFT_KEY_BOARD_MIN_HEIGHT) {                    mIsSoftInputShown = false;                } else {                    /* 获取到软键盘的高度*/                    softInputHeight = Math.abs(h - oldh);                    mIsSoftInputShown = true;                }                Log.d(TAG, "onLayoutSizeChanged, mIsSoftInputShown: " + mIsSoftInputShown);                if (softInputHeight != mSoftInputHeight) {                    if (DEBUG)Log.d(TAG, "New mSoftInputHeight: " + softInputHeight);                    mSoftInputHeight = softInputHeight;                    /*更新附件的高度*/                    mUiHandler.post(new Runnable() {                        @Override                        public void run() {                            setAttachmentPanelHeight();                        }                    });                }            }        });

让软键盘以不同的方式呈现

这里就要求应用能够根据当前条件切换软键盘呈现方式了,以及尽量做到不要让软键盘与附件同时存在,否则 【如果我按返回按键或者输入法下箭头,整个聊天界面下移到正常】 该需求难以实现,我在下面贴出我的例子Activity代码,里面有注释的。

package com.jc.softinput;import android.content.Context;import android.os.Handler;import android.os.Looper;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.KeyEvent;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;import android.view.WindowManager;import android.view.inputmethod.InputMethodManager;import android.widget.EditText;import android.widget.ImageButton;import android.widget.LinearLayout;import android.widget.TextView;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    private static final boolean DEBUG = true;    private static final String TAG = "MainActivity";    /*软键盘最小高度,这个的来源有点有点奇怪*/    private static final int SOFT_KEY_BOARD_MIN_HEIGHT = 150;    private static final int DELAY_BEFORE_HIDE_ATTACHMENT = 400; // ms    private boolean mIsSoftInputShown = false;    /* 软键盘的初始化高度,可以根据不同分辨率分别指定*/    private int mSoftInputHeight = 554;    /*假设该界面是类似微信的附件界面,包含拍照、录像等入口*/    private LinearLayout mAttachmentPanel;    private Handler mUiHandler;    private ImageButton mImageButton;    private EditText mTextEditor;    /*聊天内容界面通常是以ListView的形式呈现*/    private TextView mContentPanel;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mContentPanel = (TextView)findViewById(R.id.contentPanel);        mContentPanel.setOnTouchListener(new View.OnTouchListener() {            @Override            public boolean onTouch(View view, MotionEvent motionEvent) {                setSoftInputAdjustResize();                mAttachmentPanel.setVisibility(View.GONE);                hideSoftInputMethod();                return true;            }        });        mUiHandler = new Handler(Looper.getMainLooper());        /*初始化附件的高度,这里通常设置为272dp*/        mSoftInputHeight = getResources().getDimensionPixelSize(R.dimen.init_soft_input_height);        mAttachmentPanel = (LinearLayout)findViewById(R.id.attachment_panel);        setAttachmentPanelHeight();        mImageButton = (ImageButton)findViewById(R.id.img);        mImageButton.setOnClickListener(this);        mTextEditor = (EditText)findViewById(R.id.editText);        mTextEditor.setFocusable(true);        mTextEditor.setFocusableInTouchMode(true);        mTextEditor.setOnFocusChangeListener(new View.OnFocusChangeListener() {            @Override            public void onFocusChange(View view, boolean hasFocus) {                if (DEBUG) Log.d(TAG, "mTextEditor hasFocus: " + hasFocus);                /* 输入框获取到焦点之后,弹起了软键盘,这个时候如果附件是可见的,说明软键盘是以覆盖的形式出现的,我们延迟 0.4 s之后,                    先把软键盘呈现的方式设置成往上顶,然后偷偷的把附件隐藏,神不知鬼不觉,非常友好的让软键盘接替了附件。                 */                if (hasFocus) {                    if (View.VISIBLE == mAttachmentPanel.getVisibility()) {                        mUiHandler.postDelayed(new Runnable() {                            @Override                            public void run() {                                if (DEBUG)Log.d(TAG, "hide attachment panel");                                setSoftInputAdjustResize();                                mAttachmentPanel.setVisibility(View.GONE);                            }                        }, DELAY_BEFORE_HIDE_ATTACHMENT);                    }                }            }        });        mImageButton.setOnFocusChangeListener(new View.OnFocusChangeListener() {            @Override            public void onFocusChange(View v, boolean hasFocus) {                if (DEBUG) Log.d(TAG, "mImageButton hasFocus: " + hasFocus);                if (hasFocus) {                    onClick(mImageButton);                }            }        });        HeightChangedLinearLayout heightChangedLinearLayout = (HeightChangedLinearLayout)findViewById(R.id.height_change_layout);        heightChangedLinearLayout.setListener(new HeightChangedLinearLayout.LayoutSizeChangedListener() {            @Override            public void onLayoutSizeChanged(int w, int h, int oldw, int oldh) {                int softInputHeight = mSoftInputHeight;                Log.d(TAG, "h: " + h + ", oldh: " + oldh);                if (h - oldh >  SOFT_KEY_BOARD_MIN_HEIGHT) {                    mIsSoftInputShown = false;                } else {                    /* 获取到软键盘的高度*/                    softInputHeight = Math.abs(h - oldh);                    mIsSoftInputShown = true;                }                Log.d(TAG, "onLayoutSizeChanged, mIsSoftInputShown: " + mIsSoftInputShown);                if (softInputHeight != mSoftInputHeight) {                    if (DEBUG)Log.d(TAG, "New mSoftInputHeight: " + softInputHeight);                    mSoftInputHeight = softInputHeight;                    /*更新附件的高度*/                    mUiHandler.post(new Runnable() {                        @Override                        public void run() {                            setAttachmentPanelHeight();                        }                    });                }            }        });    }    /* 重新计算附件的高度*/    private void setAttachmentPanelHeight() {        ViewGroup.LayoutParams layoutParams = mAttachmentPanel.getLayoutParams();        layoutParams.height = mSoftInputHeight;        mAttachmentPanel.setLayoutParams(layoutParams);    }    /* 隐藏收入法 */    private void hideSoftInputMethod() {        InputMethodManager im = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);        im.hideSoftInputFromWindow(getCurrentFocus().getApplicationWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);    }    @Override    public boolean onKeyDown(int keyCode, KeyEvent event) {        if (keyCode == KeyEvent.KEYCODE_BACK && mAttachmentPanel.getVisibility() == View.VISIBLE) {            setSoftInputAdjustResize();            mAttachmentPanel.setVisibility(View.GONE);            return true;        }        return super.onKeyDown(keyCode, event);    }    /*软键盘以覆盖当前界面的形式出现*/    private void setSoftInputAdjustNothing(){        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING);    }    /*软键盘以顶起当前界面的形式出现, 注意这种方式会使得当前布局的高度发生变化,触发当前布局onSizeChanged方法回调,这里前后高度差就是软键盘的高度了*/    private void setSoftInputAdjustResize(){        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);    }    @Override    public void onClick(View view) {        if (view == mImageButton) {            Log.d(TAG, "mIsSoftInputShown: " + mIsSoftInputShown  + ", visible: " + mAttachmentPanel.getVisibility());            if (mIsSoftInputShown) {                /*软键盘存在,但附件不存在的时候,显示附件,隐藏软键盘,设置下一次出现方式为覆盖 */                if (mAttachmentPanel.getVisibility() == View.GONE) {                    mAttachmentPanel.setVisibility(View.VISIBLE);                    hideSoftInputMethod();                    mTextEditor.clearFocus();                    setSoftInputAdjustNothing();                } else {                    /* 在用户点击弹附件按钮的时候,附件和软键盘同时存在的可能性很小,只隐藏软键盘吧 */                    setSoftInputAdjustResize();                    hideSoftInputMethod();                }            } else { /* 软键盘不存在 */                if (mAttachmentPanel.getVisibility() == View.GONE) {                    mTextEditor.clearFocus();                    setSoftInputAdjustNothing();                    mAttachmentPanel.setVisibility(View.VISIBLE);                } else {                    mAttachmentPanel.setVisibility(View.GONE);                    setSoftInputAdjustResize();                }            }        }    }}

以上是部分代码实现,关键点已经添加注释。

注意点: 在做短信信息编辑界面的时候,发现新建一条没有收件人的新短信时候,通过代码设置SoftInputMode能友好切换。但是从已有收件人进入到编辑界面后发现设置adjustNothing,软键盘还是把布局给顶上去。纠结了很久(一个Activity 一万 多行,实在不知道怎么找出原因),最才发现新建的信息实在初始化的时候设置了 adjustReszie 先, 最后在有收件人的情况下也先设置adjustResize先,莫名其妙就正常了。

所以记得在AndroidManifest.xml 或者 Activity 初始化的时候 设置 adjustResize.

完整代码请查看这里: https://git.oschina.net/jcxxxxx/SoftInput
欢迎交流。

1 0
原创粉丝点击