群成员@提醒实现过程

项目中IM即时消息有个需求要做群成员@提醒功能,参考了QQ群聊的类似功能,实现过程大致分为:首先在编辑框输入@字符,跳转到选择群成员界面,选择完毕填充到编辑框内,用spannable着色;对选中的群成员点击,让光标落在尾处,保证群成员作为个人整体删除;如果有超过一页消息且有未读@消息,右上角有悬浮按钮提醒,点击后定位到首个未读的@消息。现在重新整理成一个demo,与大家分享下。效果图如下:


首先是监听编辑框输入的文本内容。如果最后一位是“@”字符,并且此时用户没有进行删除操作,则跳转到选择群成员界面。实现代码是实现TextChangedListener的监听,在onTextChanged方法进行判断:

public void onTextChanged(CharSequence s, int start, int before, int count) {
                // str.endsWith("@")
                // 输入@跳转到选择要@的人界面
                // 自定义表情是/:smile@ 类似于这种
                String str = s.toString();
                if (!isDelete) {
                    if (str.endsWith("@")) {
                        int index = str.lastIndexOf("@");
                        if (index >= 1 && checkCharSequence(str.substring(index - 1, str.length() - 1))) {
                            return;
                        }
                        Intent groupIntent = new Intent(MainActivity.this, SelectGroupMemberActivity.class);
                        groupIntent.putExtra(SelectGroupMemberActivity.GROUP_MEMBER, (Serializable)groupMemberList);
                        startActivityForResult(groupIntent, SelectGroupMemberActivity.REQUEST_CODE);
                    }
                }
            }

选择群成员界面包括:多选、全选、根据群成员手机号或者姓名搜索选择。选择完毕点击确定后,返回聊天页面,编辑框自动填充选中的群成员,并且用spannable进行着色。

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (data != null && data.getExtras() != null) {

            boolean isAllSelected = data.getBooleanExtra(SelectGroupMemberActivity.SELECTED_ALL,false);
            List<GroupMember> tempList=(ArrayList<GroupMember>)data.getSerializableExtra(SelectGroupMemberActivity.SELECTED_MEMBER);
            selectedList.addAll(tempList);

            isDelete = true;//锁住Text监听
            Editable text = mEditText.getText();
            int index = text.toString().length();
            text = text.delete(index - 1, index);//删掉'@'

            if(isAllSelected){//如果是全选
                SpannableString ss = new SpannableString("@全体成员 ");//要@的人
                ss.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.blue)), 0, ss.length(), Spanned.SPAN_EXCLUSIVE_EX                CLUSIVE);
                text = text.append(ss);
            }else {
                for(GroupMember member:tempList){
                    SpannableString ss = new SpannableString("@" + member.getName() + " ");//要@的人
                    ss.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.blue)), 0, ss.length(), Spanned.SPAN_EXCLUSIV                    E_EXCLUSIVE);
                    text = text.append(ss);
                }
            }
            mEditText.setText(text);
            mEditText.setSelection(text.toString().length());
            isDelete = false;//解开Text监听
        }
    }

当用户点击选中的群成员时,如果点击焦点落在spannable的start与end之间区间,那么设置光标落在end处。

//监听EditText点击事件,删除时如果点击位置处于span的start与end区间,让光标落在end后面
        mEditText.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Editable editable = mEditText.getEditableText();
                ForegroundColorSpan[] spans = editable.getSpans(0, editable.length(), ForegroundColorSpan.class);
                if(spans.length == 0)
                    return;
                int selectionStart = mEditText.getSelectionStart();
                for (ForegroundColorSpan span : spans) {
                    int start = editable.getSpanStart(span);
                    int end = editable.getSpanEnd(span);
                    if(selectionStart >= start && selectionStart <= end){
                        mEditText.setSelection(end);
                        return;
                    }
                }
            }
        });

当用户按下删除键时,遍历整个编辑框的所有spannable数组。如果删除位置与某个spannable数组元素吻合,则执行删除操作。特殊情况是,“ @全体成员”,如果执行删除则所有选中状态被清空。

public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN) {
            Editable editable = mEditText.getText();
            ForegroundColorSpan[] spans = editable.getSpans(0, editable.length(), ForegroundColorSpan.class);
            String str = editable.toString();
            for (int i = 0; i < spans.length; i++) {
                int spanStart = editable.getSpanStart(spans[i]);
                int spanEnd = editable.getSpanEnd(spans[i]);

                //@全体成员
                if (str.substring(spanStart, spanEnd).equals("@全体成员")) {
                    editable = editable.delete(spanStart, spanEnd);
//						selectedList.clear();
                    isDelete = true;
                    mEditText.setText(editable);
                    return true;
                }
                if (str.substring(spanStart, spanEnd).equals("@" + selectedList.get(i).getName())) {//@单独成员
                    if (mEditText.getSelectionStart() == spanEnd) {
                        editable = editable.delete(spanStart, spanEnd);
                        selectedList.remove(i);
                        isDelete = true;
                        mEditText.setText(editable);
                        mEditText.setSelection(spanStart);
                        break;
                    }
                }
            }

        }
        return super.onKeyDown(keyCode, event);
    }
接下来是实现未读@消息的定位。这里,有个前提条件:有未读@消息,并且该消息不在当前页面。我是在CursorAdapter里面的getItem方法里,遍历未读消息,判断是否有未读@消息(实际应该是从服务器数据库获取对应字段来进行判断),如果有则返回true同时记录它的position。
public Boolean getItem(int position) {
        Cursor cursor = getCursor();
        cursor.moveToPosition(position);
        String content = cursor.getString(cursor.getColumnIndex(Provider.MessageColumns._MESSAGE_CONTENT));
        boolean hasAtMessage = false;
        if(content.contains("@") && content.contains(" ")){//判断是否含有@群成员
            String[] mArray = content.split("@");
            for(int i=1; i<mArray.length; i++){
                if(mArray[i] != null) {
                    if(mArray[i].contains(" "))
                        mArray[i] = mArray[i].substring(0,mArray[i].indexOf(" "));//@群成员后面可能有其他文字
                    if (CURRENT_ACCOUNT.equals(mArray[i].trim())//这里假如当前用户是"徐福记6"
                            || ALL_MEMBERS.equals(mArray[i].trim())) {//或者是全体成员
                        hasAtMessage = true;//标记有@消息
                        mAtPosition = position;//@消息在list的position
                        break;
                    }
                }
            }
        }
        return hasAtMessage;
    }

判断出未读@消息position位置后,需要在listView实现OnScrollListener监听,在OnScroll方法判断未读@消息是否在当前页面。如果不在当前页面,则显示未读@消息的悬浮按钮进行提醒。点击该按钮可定位到首个未读@消息,并且隐藏提醒。

     @Override
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if((totalItemCount > visibleItemCount) && !hasOtherPage){//item总数大于可见item数,说明listView的item超过一页
           hasOtherPage = true;
           if(hasAt && (mAtPosition < firstVisibleItem)){//有未读@消息且不在当前页面
           rl_at_notify.setVisibility(View.VISIBLE);
           }
        }
     }


好了,群成员@提醒实现过程已经分析完毕,希望对大家有所帮助。至于消息列表的“ 有人@我”实现比较简单,这里就不做分析。如果大家感兴趣可以动手写代码实践下,或者到我的博客下载demo可作参看。



  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐福记456

您的鼓励和肯定是我创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值