在QQ或者微信的聊天页面,当输入法和表情栏互相切换时,过度非常自然,而且表情栏高度刚好跟输入法一样。个人感觉这种用户体验特别的好,别看这个细节小,但代码实现处理起来还是有一定难度。今天我就带大家来实现这种效果,下面是效果图:

首先,我们需要知道输入法的高度,使表情栏的高度与之保持一致。但是Android是没有提供现成的接口给开发者监听输入法的状态,因此需要自定义的KeyboardLayout,监听布局的改变,通过变化前后布局高度差计算出输入法的高度。

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;}@Overridepublic 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);}}

使用

KeyboardLayout加入布局文件中即可,无其他使用限制。从代码中可知,当布局变化时并不需要知道KeyboardLayout的高度来计算输入法高度,KeyboardLayout只是充当一个布局监听器的作用。

<?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.KeyboardLayoutandroid: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() {@Overridepublic void onKeyboardStateChanged(boolean isActive, int keyboardHeight) {if (isActive) { // 输入法打开//do something}else {}}});

输入法与页面布局无缝切换

输入法和表情栏切换时,如果只是简单的在切换到输入法时隐藏表情栏,或者切换到表情栏时隐藏输入法,这样过度过程造成布局闪烁一下,如下所示:

这样的效果简直会逼死像我这样有强迫症的人,因此我们需要解决它!造成这种问题的原因是,在显示表情栏时,输入法还没消失,因此表情栏会出现在输入法上面,当输入法消失时,表情栏的位置又被重新调整到底部,因此会造成布局闪烁,同理可以解释切换到输入法时造成闪烁的原因。解决问题的关键主要靠如下两句代码:

// 设置输入法弹起时自动调整布局,使之在输入法之上
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@Overrideprotected 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() {@Overridepublic void onClick(View v) {mKeyboardLayout.postDelayed(new Runnable() {@Overridepublic void run() { // 输入法弹出之后,重新调整mEmojiBtn.setSelected(false);mEmojiView.setVisibility(View.GONE);getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);}}, 250); // 延迟一段时间,等待输入法完全弹出}});mEmojiBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic 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() {@Overridepublic 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);}
}

实践中会不断的改进的代码,请大家关注最新完整的代码: https://github.com/1993hzw/Androids

Android获取输入法高度——输入法与页面布局无缝切换相关推荐

  1. Android输入法与页面布局无缝切换

    背景 最近在做一个笔记相关的项目,涉及到输入法与页面布局间的切换,以前最多就是控制输入法显示隐藏,所以在做的过程中遇到一些闪屏的问题,在此记录一下. 如图,在输入法上方悬浮一个tab栏,在点击切换字体 ...

  2. javascript 获取滚动条高度+常用js页面宽度与高度

    javascript 获取滚动条高度+常用js页面宽度与高度/ ********************* 取窗口滚动条高度****************** / function getScrol ...

  3. android 获取webView高度,设置webView高度

    1.注入获取webView高度的js方法 webView.setWebViewClient(new WebViewClient() {@Overridepublic boolean shouldOve ...

  4. android 获取navigationbar 高度,Android判断NavigationBar是否显示的方法(获取屏幕真实的高度)...

    有些时候,我们需要知道当前手机上是否显示了NavigationBar,也就是屏幕底部的虚拟按键. 比如截屏的时候,要获取屏幕的高度,必须包括NavigationBar的高度. 试过网上的多种方法,但是 ...

  5. android获得键盘高度,Android 获取键盘高度,显示键盘和隐藏键盘

    运行效果图 项目中一般会有"评论"的功能,为了更好的进行UI交互操作,就需要监听键盘弹出并获取键盘高度.所以就写一个键盘工具类. 监听键盘弹出思路:首先获取DecorView.he ...

  6. Android开发自定义View实现数字与图片无缝切换的2048

    本博客地址:http://blog.csdn.net/talentclass_ctt/article/details/51952378 最近在学自定义View,无意中看到鸿洋大神以前写过的2048(附 ...

  7. android获取键盘高度

    方案一:监听布局高度变化,使用屏幕高度减去可用高度,剩余高度即键盘高度 private ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutL ...

  8. android获取自适应高度,Android中oncreate中获得控件高度或宽度的实现方法

    Android中oncreate中获得控件高度或宽度的实现方法 onCreate函数只是提供了数据初始化的机会,此时还没有正式绘制图形.在图形尚未渲染的情况下,getWidth()或getHeight ...

  9. android 获取刘海高度,Android各厂商的刘海屏高度获取工具类

    public final classNotchScreenUtil {/*** 华为start*/ //判断是否是华为刘海屏 public static booleanhasNotchInScreen ...

最新文章

  1. 线性表ArrayList和LinkedList源码详解。
  2. 【转】Virtualbox虚拟机配置安装CentOS 6.5图文教程
  3. 高性能Cordova App开发学习笔记
  4. [不定期更新]需要记住的结论
  5. Python配置pip国内镜像源
  6. System.IO 的三个抽象类
  7. 如何在vue-router的beforeEach钩子里做页面访问权限验证
  8. ai无法启动产品_启动AI启动的三个关键教训
  9. CMU机器学习学院院长Tom Mitchell:计算机模拟人脑才刚刚起步
  10. 11.11开启10分钟 达达承接的京东小时购首单已签收
  11. 三星 android 截屏,三星S7怎么截屏 3种三星S7截图方法
  12. 使用Postman对Restful接口进行测试
  13. 为ASP.NET AJAX 1.0 Beta补充trace和dump功能
  14. java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException
  15. windowsXP sp3 升级包
  16. python处理pdf实例_Python使用PDFMiner解析PDF代码实例
  17. SG-UAP电网项目开发简介
  18. 磨砺数年,高效PERC技术终迎好时光
  19. ios 编译时报 Could not build module xxx 的解决方法尝试
  20. 戴尔服务器盘柜盘故障分析

热门文章

  1. c语言inline有什么作用,C语言inline的用法
  2. mssql远程ip连接mysql_远程MSSql数据库连接教程
  3. 最简单的asp.net 操作 access 数据库方法
  4. 域名注册和域名解析详解
  5. 今天实在忍不住,把极速星空的密码给破了
  6. Spring Condition条件使用示例
  7. ASP.NET 2.0使用FileUpload控件上传文件示例
  8. Centos移动/home目录空间到/根目录下
  9. oracle 修改数据库名和实例名
  10. 小程序如何通过绑定事件传参和触发事件如何获取参数,注意data-name中的name里面有大写或者烤串命名方式会被转换为小写