1、SpannableString和SpannableStringBuilder
1、SpannableString和SpannableStringBuilder的区别相当于String和StringBuilder的区别,在Android中可以通过SpannableString或SpannableStringBuilder给存储的字符串添加样式,实现简单的富文本效果。
2、示例,类似朋友圈效果
-
先看效果图:
-
首先这是一段文字,小明和小红是蓝色,直接看下面代码:
String content = "小明回复小红:一点状态都没有"; mSpannableString = new SpannableString(content); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),0,2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),4,6, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mTextMessage.setText(mSpannableString);
-
上面代码就实现了颜色效果,下面一步一步学习下
首先,setSpan(Object what, int start, int end, int flags):参数一:表示样式,上面的ForegroundColorSpan就是一种样式,常用的样式如下:
ForegroundColorSpan:为文本设置前景色;
BackgroundColorSpan:为文本设置文本背景色;
RelativeSizeSpan:为文本设置相对大小,在TextView原有的文字大小的基础上,相对设置文字大小;
StrikethroughSpan:为文本设置中划线,也就是常说的删除线;
UnderlineSpan:为文本设置下划线;
SuperscriptSpan:为文本设置上标,可以配合RelativeSizeSpan做数学公式中的上标;
SubscriptSpan:为文本设置下标;
StyleSpan:为文本设置(粗体、斜体)风格;
ImageSpan:将文本替换为图片;
ClickableSpan:为文本设置点击事件;
URLSpan:为文本设置超链接。参数二和三,start表示需要设置格式的子字符串的起始下标,end表示终止下标。
参数四,flag表示子字符串是否包含开始或结束端点:
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE --- 不包含两端start和end所在的端点 --- (a,b);
Spanned.SPAN_EXCLUSIVE_INCLUSIVE --- 不包含端start,但包含end所在的端点 --- (a,b];
Spanned.SPAN_INCLUSIVE_EXCLUSIVE --- 包含两端start,但不包含end所在的端点 --- [a,b);
Spanned.SPAN_INCLUSIVE_INCLUSIVE --- 包含两端start和end所在的端点 --- [a,b]。
注意,flag只是在SpannableStringBuilder才有意义,如下:mSpannableStringBuilder.setSpan(new ForegroundColorSpan(Color.BLUE),0,2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpannableStringBuilder.setSpan(new ForegroundColorSpan(Color.BLUE),4,6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSpannableStringBuilder.insert(2,"a"); //加入的a会根据flag来判断有没效果。
-
到这里还有一步没有完成,就是小明和小红应该有点击效果,利用ClickableSpan:
mSpannableString = new SpannableString(content); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),0,2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),4,6, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mSpannableString.setSpan(span,0,2,Spanned.SPAN_INCLUSIVE_INCLUSIVE); mTextMessage.setMovementMethod(LinkMovementMethod.getInstance()); ClickableSpan span = new ClickableSpan() { @Override public void onClick(View widget) { String text = ((TextView) widget).getText().toString().substring(0,2); Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show(); } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setUnderlineText(false); //去除下划线 } };
-
上面虽然实现了点击效果,但是需求是点击的时候背景为灰色,抬起又是原有的背景颜色,自己试了多个方法没成功,最后找到这个:
public class LinkTouchMovementMethod extends LinkMovementMethod { private TouchableSpan mPressedSpan; @Override public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { mPressedSpan = getPressedSpan(textView, spannable, event); if (mPressedSpan != null) { mPressedSpan.setPressed(true); Selection.setSelection(spannable, spannable.getSpanStart(mPressedSpan), spannable.getSpanEnd(mPressedSpan)); } } else if (event.getAction() == MotionEvent.ACTION_MOVE) { TouchableSpan touchedSpan = getPressedSpan(textView, spannable, event); if (mPressedSpan != null && touchedSpan != mPressedSpan) { mPressedSpan.setPressed(false); mPressedSpan = null; Selection.removeSelection(spannable); } } else { if (mPressedSpan != null) { mPressedSpan.setPressed(false); super.onTouchEvent(textView, spannable, event); } mPressedSpan = null; Selection.removeSelection(spannable); } return true; } private TouchableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); x -= textView.getTotalPaddingLeft(); y -= textView.getTotalPaddingTop(); x += textView.getScrollX(); y += textView.getScrollY(); Layout layout = textView.getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); TouchableSpan[] link = spannable.getSpans(off, off, TouchableSpan.class); TouchableSpan touchedSpan = null; if (link.length > 0) { touchedSpan = link[0]; } return touchedSpan; } } public abstract class TouchableSpan extends ClickableSpan { private boolean mIsPressed; private int mPressedBackgroundColor; private int mNormalTextColor; private int mPressedTextColor; public TouchableSpan(int normalTextColor, int pressedTextColor, int pressedBackgroundColor) { mNormalTextColor = normalTextColor; mPressedTextColor = pressedTextColor; mPressedBackgroundColor = pressedBackgroundColor; } public void setPressed(boolean isSelected) { mIsPressed = isSelected; } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(mIsPressed ? mPressedTextColor : mNormalTextColor); ds.bgColor = mIsPressed ? mPressedBackgroundColor : Color.TRANSPARENT; ds.setUnderlineText(false); } }
具体用法:
mSpannableString = new SpannableString(content); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),0,2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mSpannableString.setSpan(new ForegroundColorSpan(Color.BLUE),4,6, Spanned.SPAN_INCLUSIVE_INCLUSIVE); mSpannableString.setSpan(span,0,2,Spanned.SPAN_INCLUSIVE_INCLUSIVE); mTextMessage.setMovementMethod(new LinkTouchMovementMethod()); TouchableSpan span = new TouchableSpan(Color.BLUE,Color.BLUE,Color.GRAY) { @Override public void onClick(View widget) { String text = ((TextView) widget).getText().toString().substring(0,2); Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show(); } };