EditText实现@功能
如标题所说, 今天要实现 @ 的效果, 明确要实现的效果边界:
- 输入 @ 字符, 弹出一界面选择好友(监听回调)
- 选择好友后更新 @ 的内容
- 删除 @ 内容将一次删除 @+好友名 所有字符
废话不多说, 先上效果图:
效果图中我打开好友列表以延时500ms随机取名字代替:
@Override
public void triggerAt() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
atTextWatcher.insertTextForAt(et, names[random.nextInt(names.length)]);
}
}, 500);
}
下面说下具体的实现方法:
首先使用 TextWatcher 是毋庸置疑的, 通过分析微信 @ 功能, 发现 @张三 分为三部分:
- 第一部分为@功能的开始符号 @ 字符
- 第二部分为好友名字
- 第三部分为@功能的结束符号 ’ ’
- 注意: 通过分析微信中结束符号, 发现它并不是空格, 而是 unicode 编码为 8197 的字符, 该字符跟空格符长得一样但其实是两个东西
分析完思路就有了:
-
- 通过TextWatcher监听EditText
-
- 监听到内容增加一字符并且该字符为 @ 符号执行监听, 弹出选择好友界面, 并记录 @ 符号位置
-
- 当监听到删除一字符并且该字符为@功能结束符, 编码为 8197 的字符, 根据结束符位置往前找 @ 符号, 并移除之间的内容
实现:
- 我定义了 AtTextWatcher 实现 TextWatcher, 来处理 @ 效果, 在
onTextChanged
中判断新增字符是否为 @ , 如果是执行回调AtListener.triggerAt()
方法(Activity 中实现此方法打开好友列表选择好友), 并记录 @ 字符的位置. - 外部选择好友完成后, 调用方法
AtTextWatcher.insertTextForAt("选择的好友名字")
, 将好友名字和结束符号插入到记录的**@**符号后面 - 监听删除 @字符串 的逻辑必须写在监听方法
beforeTextChanged
中, 因为执行该方法时删除的字符并未从EditText中移除, 可以拿到字符并判断是否是 @功能结束符, 并记录结束符位置 - 然后在方法
afterTextChanged
中执行删除逻辑(之所以不在beforeTextChanged
中删除是因为结束符未删除完全就删除其他字符可能会引发一些不必要的问题)
完整代码如下, AtTextWatcher 的使用方法:
final EditText et = mEt;
final AtTextWatcher atTextWatcher;
AtTextWatcher.AtListener listener = new AtTextWatcher.AtListener() {
@Override
public void triggerAt() {
// 此处跳转好友列表
// 选择完好友后执行下面的方法
atTextWatcher.insertTextForAt(et, "张三")
}
};
atTextWatcher = new AtTextWatcher(listener);
et.addTextChangedListener(atTextWatcher);
下面是 AtTextWatcher 的完整代码:
/**
* Created by Eshel on 2019/4/8.
*/
public class AtTextWatcher implements TextWatcher {
char atEndFlag = (char) 8197;
AtListener mListener;
private int atIndex = -1;
private int endFlagIndex = -1;
public AtTextWatcher(AtListener listener) {
this.mListener = listener;
}
public void insertTextForAt(EditText et, CharSequence text){
if(atIndex == -1)
return;
StringBuilder sb = new StringBuilder(text);
sb.append(atEndFlag);
text = sb.toString();
Editable text1 = et.getText();
text1.insert(atIndex+1, text);
// et.invalidate();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if(count == 1){//删除一个字符
char c = s.charAt(start);
if(c == atEndFlag){
endFlagIndex = start;
}
}
}
/**
* @param s 新文本内容,即文本改变之后的内容
* @param start 被修改文本的起始偏移量
* @param before 被替换旧文本长度
* @param count 替换的新文本长度
*/
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if(count == 1){//新增(输入)一个字符
char c = s.charAt(start);
if(c == '@'){
atIndex = start;
if(mListener != null){
mListener.triggerAt();
}
}
}
}
@Override
public void afterTextChanged(Editable s) {
Log.i(TAG, "afterTextChanged() called with: s = [" + s + "]");
if(endFlagIndex != -1){
int index = endFlagIndex;
while ((index -= 1) != -1){
char c = s.charAt(index);
if(c == '@'){
break;
}
}
int endFlagIndex = this.endFlagIndex;
this.endFlagIndex = -1;
if(index != -1)
s.delete(index, endFlagIndex);
}
}
/**
* 输入 @ 监听
*/
public interface AtListener{
void triggerAt();
}
}