在发布微博或者写空间说说时,经常会用到@某人的输入格式:
猜测在新浪微博中的使用,是用SpannableStringBuilder来进行实现,同时在点击“@xxx”时光标不能停在用户名内部,即不可编辑用户。
在qq空间中,@之后的用户名没有样式也可点击编辑,但是这两个的共同点是删除时会删除这个人,而不是删除其中一个字母。
下面我实现的是新浪微博的样式,因为更加严谨一些:
在上一篇中完成到对特定的用户有了样式显示(蓝色高亮),也有点击事件。
更简单的高亮方式:
后来又想到一个更加简单的高亮方式,但是只是高亮没有实用价值,或者说功能实现起来麻烦一些,就是使用html,在editText中可以现实网页源码内容,那么可以显示之前在用户名加上超链接标签,但是同样需要记录用户的开始索引和结束索引,因为当@多人时,第二次会丢失超链接标签,需要反复设置。
正题:
首先声明一个用来保存内容中所有的“@xxx”的位置的变量:
private ArrayList<WhereKey> spans_position;
class WhereKey {
public int start;
public int end;
}
只需要记录首尾位置。
接下来要做的是更新这些位置的值,因为在编辑时可能会删除也可能会插入内容,那么这个操作的就放在afterTextChanged方法中:
spans_position.clear();
// Log.i(TAG, "spans_position.size()--before=" +
// spans_position.size());
ForegroundColorSpan[] spans_pos = s.getSpans(0, s.length(),
ForegroundColorSpan.class);
for (int i = 0; i < spans_pos.length; i++) {
int start = s.getSpanStart(spans_pos[i]);
int end = s.getSpanEnd(spans_pos[i]);
WhereKey whereKey = new WhereKey();
whereKey.start = start;
whereKey.end = end;
spans_position.add(whereKey);
}
// Log.i(TAG, "spans_position.size()--after=" +
// spans_position.size());
同时上一篇中的adapter部分也有了变化:
@Override
public View getView(final int position, View convertView,
ViewGroup parent) {
PopHolder holder;
if (convertView == null) {
holder = new PopHolder();
convertView = LayoutInflater.from(CreateNewWeibo.this).inflate(
R.layout.pop_for_at_item, null);
holder.nick_name = (TextView) convertView
.findViewById(R.id.pop_for_at_tv);
convertView.setTag(holder);
} else {
holder = (PopHolder) convertView.getTag();
}
holder.nick_name.setText(mListItems.get(position).nickname);
convertView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
b = false;
String temp = editText.getText().toString();
String body = "@" + mListItems.get(position).nickname;
at_users.add(body);
// Log.i(TAG, "at_users.size()="+ at_users.size());
String text = temp.substring(0, curr) + body;
SpannableStringBuilder style = new SpannableStringBuilder(
text); // 将str字符串载入SpannableStringBuilder对象中
ArrayList<WhereKey> keys = new ArrayList<CreateNewWeibo.WhereKey>();
for (int i = 0; i < at_users.size(); i++) {
WhereKey one_key = new WhereKey();
one_key.start = text.indexOf(at_users.get(i));
one_key.end = at_users.get(i).length() + one_key.start;
keys.add(one_key);
}
for (int i = 0; i < keys.size(); i++) {
style.setSpan(new ForegroundColorSpan(Color.BLUE),
keys.get(i).start, keys.get(i).end,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
editText.setText(style);
// editText.setText(Html.fromHtml(text));
// Toast.makeText(CreateNewWeibo.this,
// editText.getText().toString(),
// Toast.LENGTH_SHORT).show();
mPop.dismiss();
curr = 0;
editText.setSelection(editText.getText().length());
}
});
return convertView;
}
}
增加了多个用户时的用户收集变量:at_users,它的更新同样是放在afterTextChanged中:
if (delete_before.length() > s.length()) {
ForegroundColorSpan[] spans = s.getSpans(0, s.length(),
ForegroundColorSpan.class);
for (int i = 0; i < spans.length; i++) {
int start = s.getSpanStart(spans[i]);
int end = s.getSpanEnd(spans[i]);
String last = s.toString().substring(start, end);
for (int j = 0; j < at_users.size(); j++) {
String first = at_users.get(j).substring(0,
at_users.get(j).length() - 1);
if (last.equals(first)) {
s.delete(start, end).toString();
at_users.remove(j);
}
}
}
}
delete_before这个变量记录的是text改变之前的值,上面代码的作用主要是判断是否在进行删除用户操作,当删除用户其中的一个字母时就会在text中移除这个用户的全部信息,并把他从at_users中移除。
接下来要做的就是实现touch用户名时光标不会停在用户信息中间,而会跳到它的前面(至于跳到前面还是后面可以控制,不一定),首先给editText添加OnTouchListener监听,实现onTouch方法,在方法中要弄明白的事情是事件的执行顺序,首先会执行的是onDown,然后是onUp,但是如果只是这样来看,还是没办法获取到你touch之后光标的位置,得到的全是光标之前的位置,解决的办法是先调用一次父类的onTouchenvent,然后在onUp事件中捕获到光标的位置,再借助最前面提到的spans_position来跳转光标:
editText.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
int start_now = editText.getSelectionStart();
Log.i(TAG, "start_now=" + start_now);
Log.i(TAG, "spans_position.size()=" + spans_position.size());
for (int i = 0; i < spans_position.size(); i++) {
// Log.i(TAG, "spans_position.get(i).start="
// + spans_position.get(i).start);
// Log.i(TAG, "spans_position.get(i).end="
// + spans_position.get(i).end);
if (start_now > spans_position.get(i).start
&& start_now < spans_position.get(i).end) {
// Log.i(TAG, "into");
editText.setSelection(spans_position.get(i).start);
return true;
}
}
}
return false;
}
});
这样功能就和微博的完全一样了,同时在输入@进行联想是使用的是popwindow,不用去跳转到另外的活动。
未验证和解决的问题:对于一条信息中多次@同一个人有隐患,未解决。