记得两周前写过关于@功能的简单实现, 那次的实现思路是监听删除字符串, 如果删除一个字符并且该字符是unicode编码为 8197 的字符, 就查找@符号并删除整个@字符串. 刚好上周做到了@的需求, 完全推翻了上篇博客的思路
首先说下要实现的功能
- 输入 @ 字符, 弹出一界面选择好友(监听回调)
- 选择好友后更新 @ 的内容
- 删除 @ 内容将一次删除 @+好友名 所有字符
- @好友名要高亮
- 要可以点击外部按钮输入 @ 并高亮, 并能区分是点击按钮还是键盘输入 @
- @好友 之间插入字符 @ 将失效
- @好友 之间插入 @ , 前一个 @ 将失效
- 记录 @好友 开始位置和结束位置以及跟用户ID对应, 获取在发送文本时发送至服务器, 以供其他界面展示 @内容
- 同一个用户id不能多次@
废话不多说, 先上效果图:
实现思路
- 继承 TextWatcher , 重写其中方法, 监听字符变化
- 插入@符号时记录userId以及@长度(length)和开始位置(index), 定义集合, 记录所有@
- 增删字符时通过监听 EditText 字符变化修改, 修改@对应索引, 当字符变化完毕后更新 spannable 使文字高亮
- 删除字符增加字符时判断如果变化在@+好友中间, 删除对应@对象
- 暴露给外部获取@字符信息(userId index 和length)
使用方法:
private ImpeccableAtTextWatcher atwatcher = null;
private void initEditText(EditText editText){
ImpeccableAtTextWatcher.AtListener listener = new ImpeccableAtTextWatcher.AtListener() {
@Override
public void triggerAt() {
//在此处跳转好友列表界面
//... 从好友界面返回将选择的好友添加至EditText
//键盘输入@使用该方法添加
atwatcher.insertTextForAt("张三", 23);
//外部点击按钮跳转好友列表返回添加使用方法 insertTextForAtIndex()
// atwatcher.insertTextForAtIndex("张三", 23);
}
};
atwatcher = new ImpeccableAtTextWatcher(editText, Color.RED, listener);
editText.addTextChangedListener(atwatcher);
}
代码实现
其实主要就是通过 TextWatcher 的3个监听方法来完成的, 没什么难点, 下边是完整代码:
public class ImpeccableAtTextWatcher implements TextWatcher {
public static final String TAG = "AtTextWatcher";
private int color;
private char atEndFlag = (char) 8197;
private AtListener mListener;
private int atIndex = -1;
private int endFlagIndex = -1;
private EditText et;
private List<AtUserBean> callUsers;
private List<Object> cacheSpans = new ArrayList<>();
private boolean needNotifyAtColor;
private boolean closeListener;
public ImpeccableAtTextWatcher(EditText et, int highLightColor, AtListener listener) {
this.et = et;
this.color = highLightColor;
this.mListener = listener;
}
public List<AtUserBean> getCallUsers() {
return callUsers;
}
/**
* 点击 @ 按钮使用此方法插入带 @ 的高亮字符
*/
public void insertTextForAtIndex(CharSequence nickName, long userId) {
if (containsUser(userId))//去重逻辑
return;
int selectionStart = et.getSelectionStart();
int selectionEnd = et.getSelectionEnd();
if (selectionEnd > selectionStart) {
selectionStart = selectionEnd;
}
int atIndex =