微博的文本编辑和显示(emoji表情,@某人、链接高亮点击)

您所在的位置:网站首页 微博的emoji表情 微博的文本编辑和显示(emoji表情,@某人、链接高亮点击)

微博的文本编辑和显示(emoji表情,@某人、链接高亮点击)

2023-08-21 16:17| 来源: 网络整理| 查看: 265

日常开发的过程中我们经常会需要实现类似微博的文本输入框,可以自定义的emoji、@某人高亮显示、快捷删除、文本显示表情、@人和链接点解等效果。本人躺尸过各种坑后来一波,废话不说,先看效果:

大家好,我是废话[]( ̄▽ ̄)*

效果 动图效果 maxLength) { return; } int resId = SmileUtils.getRedId(name); Drawable drawable = editText.getResources().getDrawable(resId); if (drawable == null) return; drawable.setBounds(0, 0, size, size);//这里设置图片的大小 ImageSpan imageSpan = new ImageSpan(drawable); SpannableString spannableString = new SpannableString(name); spannableString.setSpan(imageSpan, 0, spannableString.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); int index = Math.max(editText.getSelectionStart(), 0); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(editText.getText()); spannableStringBuilder.insert(index, spannableString); editText.setText(spannableStringBuilder); editText.setSelection(index + spannableString.length()); }

3、表情显示框的删除表情

右下角那个

一般在表情选择框中,最后面都会有一个返回按键,这个返回的图片资源这里给它取了一个特殊的名字delete_expression,在每一页的最后一个加上它,同时对于这个按键的点击做特殊的处理:

这里判断如果是返回按键图片的话,每次逐个删除[xxx]这样的块

String filename = smileImageExpressionAdapter.getItem(position); try { if (filename != "delete_expression") { // 不是删除键,显示表情 /**插入表情*/ SmileUtils.insertIcon(editTextEmoji, 2000, ScreenUtils.dip2px(getContext(), 20), filename); } else { // 删除文字或者表情 if (!TextUtils.isEmpty(editTextEmoji.getText())) { int selectionStart = editTextEmoji.getSelectionStart();// 获取光标的位置 if (selectionStart > 0) { String body = editTextEmoji.getText().toString(); String tempStr = body.substring(0, selectionStart); int i = tempStr.lastIndexOf("[");// 获取最后一个表情的位置 if (i != -1) { CharSequence cs = tempStr.substring(i, selectionStart); if (SmileUtils.containsKey(cs.toString())) editTextEmoji.getEditableText().delete(i, selectionStart); else editTextEmoji.getEditableText().delete(selectionStart - 1, selectionStart); } else { editTextEmoji.getEditableText().delete(selectionStart - 1, selectionStart); } } } } } catch (Exception e) { e.printStackTrace(); }

4、批量处理显示文本,适合插入文本到EditText和TextView中

对于文本我们最后都处理为Spannable 返回,显示的时候只需要setText即可。

这里使用的是通过CharSequence 生成一个新的Spannable ,对这个Spananle进行key的正则匹配一个一个替换需要显示为表情的文本。

/** * replace existing spannable with smiles * * @param context 上下文 * @param spannable 显示的span * @return 是否添加 */ public static boolean addSmiles(Context context, Spannable spannable) { boolean hasChanges = false; for (Map.Entry entry : emoticons.entrySet()) { Matcher matcher = entry.getKey().matcher(spannable); while (matcher.find()) { boolean set = true; for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) if (spannable.getSpanStart(span) >= matcher.start() && spannable.getSpanEnd(span) 0) { SpannableStringBuilder style = new SpannableStringBuilder(charSequence); style.clearSpans();// should clear old spans for (URLSpan url : urls) { String urlString = url.getURL(); if (isNumeric(urlString.replace("tel:", ""))) { style.setSpan(new StyleSpan(Typeface.NORMAL), sp.getSpanStart(url), sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); } else if (isTopURL(urlString.toLowerCase())) { LinkSpan linkSpan = new LinkSpan(context, url.getURL(), color, spanUrlCallBack); style.setSpan(linkSpan, sp.getSpanStart(url), sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); } else { style.setSpan(new StyleSpan(Typeface.NORMAL), sp.getSpanStart(url), sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); } } for (ClickAtUserSpan atUserSpan : atSpan) { style.setSpan(atUserSpan, sp.getSpanStart(atUserSpan), sp.getSpanEnd(atUserSpan), Spanned.SPAN_MARK_POINT); } SmileUtils.addSmiles(context, style); textView.setAutoLinkMask(0); return style; } else { return spannable; } } else { return spannable; } }

2、TextView的@某人显示效果

如同上面处理的逻辑,@某人使用的也是一种自定的Span,继承了ClickableSpan,所以上面在清除样式后要恢复到原来的状态。所以@某人和url的显示有着一个正宫和二奶的关系,这里是如果@某人和url冲突,优先显示@人的效果。

目前@某人的判断逻辑和微博的还不大一样(其实我也想一样的 ̄へ ̄),微博是拿用户的昵称直接作为id可以把带@直接用正则判断显示高亮,而这里用的是用户昵称和用户id绑定后判断文本里是否有需要高亮显示,用的是@xxx (@xxx加一个空格)或者@xxx\b这样的固定格式。

这里需要注意的逻辑是

@人的在文本中出现的顺序和返回的List顺序不一定一致 @同一个人的名字可能出现多次

所以找女朋友还是以这里以返回的人list为主,一个一个到文本中去配对吧。

具体逻辑是

首先通过String的indexOf来判断文本中是否有该名字的存在(index),首先从0的偏移开始。

如果识别到了,那么就将这个位置用 Map map记录下来这个位置用于后面判断。

判断这个位置下的名字前面是否有@、后面是否有空格或者\b。(这里注意有时候服务端可能把最后一个空格且截取了)

如果符合条件即可用span替换显示。

记录下来这个index为startIndex,下一个循环从这个startIndex开始indexOf的获取。

如果从这个indexOf开始到结束一直没有,那么@名字可能在startIndex前面,所以从0开始重新取index。

如果拿到了index,还需要判断这个index是不是map里已经处理过的,如果是就往后移startIndex再去取一次判断。

最后设置下方效果来达到点击跳转。

textView.setMovementMethod(LinkMovementMethod.getInstance());

整个处理代码(╮(╯_╰)╭我已经写的无力了):

/** * AT某人的跳转 * * @param context 上下文 * @param listUser 需要显示的AT某人 * @param content 需要处理的文本 * @param textView 需要显示的view * @param clickable AT某人是否可以点击 * @param color 需要显示的颜色 * @param spanAtUserCallBack AT某人点击的返回 * @return 返回显示的spananle */ public static Spannable getAtText(Context context, List listUser, String content, TextView textView, boolean clickable, int color, SpanAtUserCallBack spanAtUserCallBack) { if (listUser == null || listUser.size() 0) { index = content.indexOf(listUser.get(i).getUser_name()); if (map.containsKey("" + index)) { int tmpIndexStart = (indexStart < lenght) ? Integer.parseInt(map.get("" + index)) : lenght - 1; if (tmpIndexStart != indexStart) { indexStart = tmpIndexStart; i--; continue; } } } if (index > 0) { map.put(index + "", index + ""); int mathStart = index - 1; int indexEnd = index + listUser.get(i).getUser_name().length(); boolean hadAt = "@".equals(content.substring(mathStart, index)); int matchEnd = indexEnd + 1; if (hadAt && (matchEnd indexStart) { indexStart = indexEnd; } hadHighLine = true; spannableString.setSpan(new ClickAtUserSpan(context, listUser.get(i), color, spanAtUserCallBack), mathStart, (indexEnd == lenght) ? lenght : matchEnd, Spanned.SPAN_MARK_POINT); } } } } SmileUtils.addSmiles(context, spannableString); if (!(textView instanceof EditText) && clickable && hadHighLine) textView.setMovementMethod(LinkMovementMethod.getInstance()); return spannableString; } EditTextAtUtils 处理@某人的逻辑

这里需要实现的在编辑文本框中需要实现的@某人显示,类似微博Android端的效果需要注意这几个: ((ノಠ益ಠ)ノ彡┻━┻哪来那么多问题)

1)、回退的时候直接删除整个@块。

2)、光标不能落入到@块中,防止在@块中又插入多一次。

3)、删除的时候对应删除list里面的id和name。

4)、不能直接使用Span来改变颜色,不然某些机器中会导致@块后面的字体效果直接变为@一样的样式(目前不知道什么原因)。

5)、监听输入@符号。

未能实现的是复制的时候微博可以整个复制,不能复制其中文字,如果有知道实现的大神留言指导下~ (臣妾不知道如何入♀手啊.....((/- -)/) 好了,开始说实现方法吧:

1、输入文本中的文本格式为@名字\b这个的格式,那么监听EditText文本变化,判断如果被删除的是\b,那么就把\b到@的文本直接删除。

2、同样是在文本框中监听如果输入的文本是增加的,而且@符号,那么就通知跳转到用户选择页面。

@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { beforeCount = s.toString().length(); if (count == 1) { String deleteSb = s.toString().substring(start, start + 1); if ("\b".equals(deleteSb)) { delIndex = s.toString().lastIndexOf("@", start); length = start - delIndex; } } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { String setMsg = s.toString(); if (delIndex != -1) { resolveDeleteName(); int position = delIndex; delIndex = -1; editText.getText().replace(position, position + length, ""); editText.setSelection(position); } else { if (setMsg.length() >= beforeCount && editText.getSelectionEnd() > 0 && setMsg.charAt(editText.getSelectionEnd() - 1) == '@') { if (editTextAtUtilJumpListener != null) { editTextAtUtilJumpListener.notifyAt(); } } } }

3、光标处理

EditText在点击的时候我们可以获取到光标落下的位置,这时候我们通过该位置去已有@的list列表里判断每个名字所在位置,比对光标位置是不是落在了@块内,如果是就强行将光标落到@块的旁边(= =光标不能插进来)。

if (TextUtils.isEmpty(editText.getText())) return; int selectionStart = editText.getSelectionStart(); if (selectionStart > 0) { int lastPos = 0; for (int i = 0; i < contactNameList.size(); i++) { if ((lastPos = editText.getText().toString().indexOf( contactNameList.get(i), lastPos)) != -1) { if (selectionStart >= lastPos && selectionStart


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3