最近开发一个类IM应用,碰到一个平时认为是想当然的功能,但实际做的时候却稍有卡壳。
需求:当键盘弹出,点击聊天页面空白处的时候,键盘隐藏
我们的本篇的重点是解决点击聊天页面空白处的问题,而非键盘的show/hiden,所以若有键盘相关需求的童鞋,可以先google下,有很多。
聊天页面的空白处,说明得排除可点击的区域,比如:
①、文字(点击后可能会有URL、电话号码的操作等)
②、头像(可能头像点击后会跳转个人信息)
③、图片(点击后有放大图片的操作)
④、视频(点击后播放视频)
⑤、卡片(比如分享的链接、红包等)
。。。。。
思考过程:
初来拿到问题,想当然的认为监听RecyclerView的点击事件好了。
recyclerView.setOnClickListener()
⬇️
但是稍微一想,肯定不对啊,我们点击的虽然是空白区域,但其实点击的是每一条recycler view item。
⬇️
那么当消息不满一屏的时候,也就是recycler view item还没有覆盖一整屏的时候,空白区域是用recyclerView.setOnClickListener()
来监听吗?
经验证,也不是。
⬇️
于是想到肯定要处理view的touch事件了。
点击屏幕,判断当前点击区域是否为可点击控件(文字、头像、图片、视频、卡片等情况),如果不是,则父控件拦截事件、隐藏键盘;
当点击到上述可点击区域时,父控件下发事件,子控件(也就是上述可点击控件)拦截并消耗事件;
选择解决方案:
那么监听哪个view的touch事件呢?
1、Recycler item view?
的确,我们点击的就是item,那么理应处理item view的点击事件,对吧。
如果该item是文字类型,那么如果点击的是空白区域,父控件拦截并隐藏键盘;若我们点击的文字textview和头像imageview的时候,父控件就下发事件,交给文字或头像控件处理
但是,我们前面提过,还有当消息不满一屏的时候,即屏幕没有完全被消息item覆盖的时候,我们还得另外处理,头疼。。。
2、Activity?
网上看到的解决方案,重写Activity的onInterceptTouchEvent,把可点击的view,添加到一个list里面,然后处理onInterceptTouchEvent事件的时候,如果没有点击到该view,则拦截并隐藏键盘;若是,下发到子控件处理相关操作
大哥,要知道整个Activity里面包含的内容可并不只是有聊天内容啊,还有比如Toolbar、input操作区域、一些选择图片、emoji、发送按钮等功能区域,你要把这些也一个个添加到list里面,皇上还是赐我一个痛快的吧。。。
3、RecyclerView
这也是我最终选择的方案,我们直接来看代码吧
//给recyclerView添加touch listener
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
//获得action事件
int action = e.getActionMasked();
//当action是ACTION_DOWN的时候,处理事件
if (action == MotionEvent.ACTION_DOWN) {
//根据你点击的点的横纵坐标,得到你点击的view
//这是Recyclerview自带的方法
View touchView = rv.findChildViewUnder(e.getX(), e.getY());
//Touch到了recyclerview没有item覆盖的区域
if (touchView == null) {
//隐藏键盘
Utils.forceHideKeyboard(ActivityChatNew.this, mInputView);
return false;
}
//需要单独处理event的view
View textView = touchView.findViewById(R.id.normal_message);
View imageView = touchView.findViewById(R.id.image_message);
View cardView = touchView.findViewById(R.id.card_root_view);
View emojiView = touchView.findViewById(R.id.chat_emoji_view);
View resendView = touchView.findViewById(R.id.chat_send_failed);
if (textView == null && imageView == null
&& cardView == null && emojiView == null
&& resendView == null) {
Utils.forceHideKeyboard(ActivityChatNew.this, mInputView);
return false;
}
//判断点击的地方是否为可点击的控件,如果不是,隐藏键盘
if (!ConversationUtils.isTouchInView(textView, e)
&& !ConversationUtils.isTouchInView(imageView, e)
&& !ConversationUtils.isTouchInView(cardView, e)
&& !ConversationUtils.isTouchInView(emojiView, e)
&& !ConversationUtils.isTouchInView(resendView, e)) {
Utils.forceHideKeyboard(ActivityChatNew.this, mInputView);
}
}
//处理可点击控件的事件
return false;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
});
看看isTouchInView的代码,根据坐标判断点击的区域是否为可点击控件:
public static boolean isTouchInView(View view, MotionEvent event) {
if (view == null) {
return false;
}
int[] location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
return x < event.getRawX() && event.getRawX() < x + view.getWidth()
&& y < event.getRawY() && event.getRawY() < y + view.getHeight();
}
OK,介绍完毕,是否清楚思路了?
请关注我的公众号~
![](http://upload-images.jianshu.io/upload_images/1857762-209f15b082cc1b04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)