即时通讯聊天页面,点击空白处隐藏键盘

最近开发一个类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)
    原文作者:UP7CR
    原文地址: https://www.jianshu.com/p/2d7c8591ef9d
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞