最近開發(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)擊后播放視頻)
⑤、卡片(比如分享的鏈接、紅包等)
。。。。。

思考過程:
初來拿到問題,想當(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)聽嗎?

經(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)注我的公眾號~
