即時(shí)通訊聊天頁面,點(diǎn)擊空白處隱藏鍵盤

最近開發(fā)一個(gè)類IM應(yīng)用,碰到一個(gè)平時(shí)認(rèn)為是想當(dāng)然的功能,但實(shí)際做的時(shí)候卻稍有卡殼。

需求:當(dāng)鍵盤彈出,點(diǎn)擊聊天頁面空白處的時(shí)候,鍵盤隱藏

我們的本篇的重點(diǎn)是解決點(diǎn)擊聊天頁面空白處的問題,而非鍵盤的show/hiden,所以若有鍵盤相關(guān)需求的童鞋,可以先google下,有很多。

聊天頁面的空白處,說明得排除可點(diǎn)擊的區(qū)域,比如:
①、文字(點(diǎn)擊后可能會有URL、電話號碼的操作等)
②、頭像(可能頭像點(diǎn)擊后會跳轉(zhuǎn)個(gè)人信息)
③、圖片(點(diǎn)擊后有放大圖片的操作)
④、視頻(點(diǎn)擊后播放視頻)
⑤、卡片(比如分享的鏈接、紅包等)
。。。。。

圖是不是有點(diǎn)大。。。

思考過程:

初來拿到問題,想當(dāng)然的認(rèn)為監(jiān)聽RecyclerView的點(diǎn)擊事件好了。
recyclerView.setOnClickListener()
??
但是稍微一想,肯定不對啊,我們點(diǎn)擊的雖然是空白區(qū)域,但其實(shí)點(diǎn)擊的是每一條recycler view item。
??
那么當(dāng)消息不滿一屏的時(shí)候,也就是recycler view item還沒有覆蓋一整屏的時(shí)候,空白區(qū)域是用recyclerView.setOnClickListener()來監(jiān)聽嗎?

未鋪滿一屏的空白區(qū)域

經(jīng)驗(yàn)證,也不是。
??
于是想到肯定要處理view的touch事件了。
點(diǎn)擊屏幕,判斷當(dāng)前點(diǎn)擊區(qū)域是否為可點(diǎn)擊控件(文字、頭像、圖片、視頻、卡片等情況),如果不是,則父控件攔截事件、隱藏鍵盤;
當(dāng)點(diǎn)擊到上述可點(diǎn)擊區(qū)域時(shí),父控件下發(fā)事件,子控件(也就是上述可點(diǎn)擊控件)攔截并消耗事件;

選擇解決方案:

那么監(jiān)聽哪個(gè)view的touch事件呢?
1、Recycler item view?
的確,我們點(diǎn)擊的就是item,那么理應(yīng)處理item view的點(diǎn)擊事件,對吧。
如果該item是文字類型,那么如果點(diǎn)擊的是空白區(qū)域,父控件攔截并隱藏鍵盤;若我們點(diǎn)擊的文字textview和頭像imageview的時(shí)候,父控件就下發(fā)事件,交給文字或頭像控件處理
但是,我們前面提過,還有當(dāng)消息不滿一屏的時(shí)候,即屏幕沒有完全被消息item覆蓋的時(shí)候,我們還得另外處理,頭疼。。。

2、Activity?
網(wǎng)上看到的解決方案,重寫Activity的onInterceptTouchEvent,把可點(diǎn)擊的view,添加到一個(gè)list里面,然后處理onInterceptTouchEvent事件的時(shí)候,如果沒有點(diǎn)擊到該view,則攔截并隱藏鍵盤;若是,下發(fā)到子控件處理相關(guān)操作
大哥,要知道整個(gè)Activity里面包含的內(nèi)容可并不只是有聊天內(nèi)容啊,還有比如Toolbar、input操作區(qū)域、一些選擇圖片、emoji、發(fā)送按鈕等功能區(qū)域,你要把這些也一個(gè)個(gè)添加到list里面,皇上還是賜我一個(gè)痛快的吧。。。

3、RecyclerView
這也是我最終選擇的方案,我們直接來看代碼吧

//給recyclerView添加touch listener
recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                //獲得action事件
                int action = e.getActionMasked();

                //當(dāng)action是ACTION_DOWN的時(shí)候,處理事件
                if (action == MotionEvent.ACTION_DOWN) {
                    //根據(jù)你點(diǎn)擊的點(diǎn)的橫縱坐標(biāo),得到你點(diǎn)擊的view
                    //這是Recyclerview自帶的方法
                    View touchView = rv.findChildViewUnder(e.getX(), e.getY());
                    //Touch到了recyclerview沒有item覆蓋的區(qū)域
                    if (touchView == null) {
                        //隱藏鍵盤
                        Utils.forceHideKeyboard(ActivityChatNew.this, mInputView);
                        return false;
                    }
                    //需要單獨(dú)處理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;
                    }

                    //判斷點(diǎn)擊的地方是否為可點(diǎn)擊的控件,如果不是,隱藏鍵盤
                    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);
                    }
                }
                //處理可點(diǎn)擊控件的事件
                return false;
            }
            @Override
            public void onTouchEvent(RecyclerView rv, MotionEvent e) {
            }
            @Override
            public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
            }
        });

看看isTouchInView的代碼,根據(jù)坐標(biāo)判斷點(diǎn)擊的區(qū)域是否為可點(diǎn)擊控件:

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,介紹完畢,是否清楚思路了?


請關(guān)注我的公眾號~

![](http://upload-images.jianshu.io/upload_images/1857762-209f15b082cc1b04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,781評論 25 709
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 47,134評論 22 665
  • 郁悶,我可真黑,跟碳似的。尤其對比了十天前我(?.????????????????????????????????...
    菁菁zhen閱讀 163評論 0 0
  • 菊花——不畏懼刺骨寒風(fēng),傲霜而立,在我國已有3000多年的栽培歷史。從古至今,不管是文人墨客,還是尋常百姓,對菊...
    SoulCasualness閱讀 664評論 0 1
  • 出瓜洲站不久,前往敦煌的路上,戈壁荒灘上突然冒出一片向日葵,金燦燦的向陽花,我差點(diǎn)認(rèn)成油菜花,想來油菜花種在這里早...
    彩蛋旅行閱讀 614評論 2 48

友情鏈接更多精彩內(nèi)容