Android键盘操作总结(1)

4.1 问题描述

在IM聊天页面,通常下面会做成类似于微信的样式(点击后可切换表情面板和键盘)。点击表情按钮,会弹出表情面板,且表情按钮变成键盘模式;再次点击键盘模式,或者点击输入框,会弹出输入框,并收起表情面板。以下篇幅均称表情面板为面板。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

4.2 常规思路

通常这样的页面布局是一个RecyclerView+输入区域。输入区域在RecyclerView下面,所以整个布局可以使用垂直的LinearLayout。键盘模式我们选择adjustResize

常规的逻辑如下

  • 输入区域包含输入框和下面的表情面板,默认表情面板的visibilityGONE
  • 点击表情按钮时,面板的可见性为VISIBLE;收起输入法键盘;按钮图片变为键盘模式。
  • 再次点击键盘模式按钮,面板的可见性为GONE;展开输入法键盘;按钮推盘变成表情模式。

我们按照上述思路写下关键代码:

private void initView(View root) {
mInputEt = root.findViewById(R.id.et_input);
mFaceBtn = root.findViewById(R.id.btn_face);
mPanel = root.findViewById(R.id.panel);

mFaceBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mPanel.getVisibility() == View.VISIBLE) {
mPanel.setVisibility(View.GONE);
mFaceBtn.setImageResource(R.drawable.emoji_download_icon);
openKeyBoard(mInputEt);
} else {
mPanel.setVisibility(View.VISIBLE);
mFaceBtn.setImageResource(R.drawable.zz_chat_reply_keyboard);
closeKeyBoard(mInputEt);
}
}
});
}

运行看看结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

出现了奇怪的一帧:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

结论:

当屏幕中已显示键盘时,点击表情按钮弹出面板前,需要隐藏键盘,但是隐藏键盘我们只是调用的一个远程服务(Context.INPUT_METHOD_SERVICE),它是何时执行我们无法控制(一般来说,涉及跨进程通信,所以执行顺序肯定是在面板显示之后),所以我们无论我们先调用隐藏键盘api再显示表情面板,还是先显示表情面板在调用隐藏键盘的api都会出现这一帧现象,给人的感觉就是闪烁了一下。

4.3 解决方案

因此,我们隐藏和显示表情面板的时机不是点击表情按钮时就立刻执行,而是需要等到输入法面板完全显示或完全隐藏后再进行。

这里可能涉及到一些监听键盘弹起/隐藏操作和获取键盘高度的知识。可以参见上一节小结如何获取键盘高度。我们获取的键盘高度每次更新后直接存储在SharedPreferences,某些应用需要重新将弹起的面板高度重新设置为与键盘高度相同,如微信就需要记录键盘的高度。但也不是每个应用都需要面板与键盘高度一致,如果你的应用不需要可以不用看如何获取键盘高度。

如果我们在OnGlobalLayoutListener中监听键盘的弹出或收起,并根据相应状态设置面板的隐藏或显示时会出现一些闪烁的问题(代码可以看Demo中的半解决切换键盘冲突)。因为闪烁的时间很短,所以录制gif的时候无法看到,有兴趣的可以运行Demo中半解决切换键盘冲突方案。

以键盘弹起为例,我们的流程是这样的:

触发键盘弹起 --> OnGlobalLayoutListener接收到布局变化 --> 此时键盘已经完全弹起 --> X --> 隐藏表情面板

这个流程中的X指的是bug出现的时候,键盘完全弹起时,表情键盘并没有立即隐藏,而是随后隐藏的,这就导致了半解决冲突的微弱闪烁的现象。

  • 为什么会出现这样的微弱的闪烁?

我们来看看ViewGroup的测量过程。ViewGroup测量时,会先去遍历测量所有的子View的尺寸,然后结合ViewGroup的测量模式计算出合适的尺寸。在我们这个案例里,当表情面板已经展开时,如果切换到键盘,首页键盘会挤压整个布局,也就是我们说的ViewGroup的布局,但是此时执行ViewGroup的onMeasure时,里面的表情面板仍然是可见的。然后我们在OnGlobalLayoutListener的回调里将表情面板的可见性设为GONE, 但此时已经和键盘刚展开时已经不是同一帧了,所以看到了微弱的闪烁效果。

根据上面的分析,我们需要在键盘收起时的那一帧中,测量ViewGroup尺寸时,直接重新测量的面板控件的尺寸就可以了。我们把表情区域放进一个自定义的布局控件KBPanelConflictLayout,整个页面的根布局设为自定义控件KBRootConflictLayout(代码可以看Demo中的解决切换键盘冲突)。

KBRootConflictLayoutonMeasure方法中,根据布局高度变化是否超过某个阈值来判断是否键盘弹起。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
preNotifyChild(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

private void preNotifyChild(int width, int height) {
if (mOldHeight < 0) {
mOldHeight = height;
return ;
}
int deltaY = height - mOldHeight;
mOldHeight = height;
int minKeyboardHeight = 180;
if (Math.abs(deltaY) >= minKeyboardHeight) {
if (deltaY < 0) {
// 键盘弹起
if (mKBPanelConflictLayout != null) {
// 隐藏面板
mKBPanelConflictLayout.setHide();
}
} else {
// 键盘收起
if (mKBPanelConflictLayout != null) {
// 显示面板
mKBPanelConflictLayout.setShow();
}
}
}
}

KBPanelConflictLayoutonMeasure方法中,我们根据是否隐藏状态来判断是否需要把键盘的高度变为0.

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mHide) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

public void setHide() {
this.mHide = true;
setVisibility(View.GONE);
}
public void setShow() {
this.mHide = false;
setVisibility(View.VISIBLE);
}
这样在测试下,看看没有闪烁冲突的效果图吧。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如果你把上述代码整理优化下,加上对RelativeLayout和FrameLayout的支持,对设置是否调整面板高度与键盘一致的支持,对多面板的切换的支持,提供一些工具类给用户直接调用,那就是2000+star的github.com/Jacksgong/J…项目了。
附上本文Demo地址,欢迎点心:
github.com/hust2010107…

设计模式学习笔记

设计模式系列学习视频

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值