下面列出了android.text.SpannableStringBuilder#getSpans ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
/**
* Constructor.
* @param charSequence the text which will be input to TextService. Attached spans that
* implement {@link ParcelableSpan} will also be marshaled alongside with the text.
* @param start the beginning of the range of text (inclusive).
* @param end the end of the range of text (exclusive).
* @param cookie the cookie for this TextInfo
* @param sequenceNumber the sequence number for this TextInfo
*/
public TextInfo(CharSequence charSequence, int start, int end, int cookie, int sequenceNumber) {
if (TextUtils.isEmpty(charSequence)) {
throw new IllegalArgumentException("charSequence is empty");
}
// Create a snapshot of the text including spans in case they are updated outside later.
final SpannableStringBuilder spannableString =
new SpannableStringBuilder(charSequence, start, end);
// SpellCheckSpan is for internal use. We do not want to marshal this for TextService.
final SpellCheckSpan[] spans = spannableString.getSpans(0, spannableString.length(),
SpellCheckSpan.class);
for (int i = 0; i < spans.length; ++i) {
spannableString.removeSpan(spans[i]);
}
mCharSequence = spannableString;
mCookie = cookie;
mSequenceNumber = sequenceNumber;
}
@Test
public void testLinkifySymbols_linkClickListener() {
final String symbol = "TWTR";
final String fullText = BASE_TEXT + " $" + symbol;
final LinkClickListener mockClickListener = mock(LinkClickListener.class);
final SymbolEntity symbolEntity = EntityFactory.newSymbolEntity(fullText, symbol);
final FormattedTweetText formattedText = new FormattedTweetText();
formattedText.text = fullText;
formattedText.symbolEntities.add(FormattedUrlEntity.createFormattedUrlEntity(
symbolEntity));
final SpannableStringBuilder linkifiedText = (SpannableStringBuilder)
TweetTextLinkifier.linkifyUrls(formattedText, mockClickListener, 0, 0, true,
true);
final ClickableSpan[] clickables =
linkifiedText.getSpans(symbolEntity.getStart(), symbolEntity.getEnd(),
ClickableSpan.class);
assertEquals(1, clickables.length);
}
/**
* @param spanText
* @return true if have url
*/
public static final boolean hackURLSpanHasResult(SpannableStringBuilder spanText) {
boolean result = false;
URLSpan[] spans = spanText.getSpans(0, spanText.length(), URLSpan.class);
// TODO URLSpan need change to ClickableSpan (GoURLSpan) , otherwise URLSpan can not click, not display underline.WHY?
for (URLSpan span : spans) {
int start = spanText.getSpanStart(span);
int end = spanText.getSpanEnd(span);
String url = span.getURL();
if (url != null) {
result = true;
spanText.removeSpan(span);
ClickableSpan span1 = new GoURLSpan(span.getURL());
spanText.setSpan(span1, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return result;
}
public SpanGetter(final CharSequence inputText,
final Class<? extends CharacterStyle> spanType) {
mInputText = (SpannableStringBuilder)inputText;
final CharacterStyle[] spans =
mInputText.getSpans(0, mInputText.length(), spanType);
if (0 == spans.length) {
mSpan = null;
mStart = -1;
mEnd = -1;
} else if (1 == spans.length) {
mSpan = spans[0];
mStart = mInputText.getSpanStart(mSpan);
mEnd = mInputText.getSpanEnd(mSpan);
} else {
throw new RuntimeException("Expected one span, found " + spans.length);
}
}
@Test
public void testLinkifyMentions_linkClickListener() {
final String mention = "TwitterDev";
final String fullText = BASE_TEXT + " @" + mention;
final LinkClickListener mockClickListener = mock(LinkClickListener.class);
final MentionEntity mentionEntity = EntityFactory.newMentionEntity(fullText, mention);
final FormattedTweetText formattedText = new FormattedTweetText();
formattedText.text = fullText;
formattedText.mentionEntities.add(FormattedUrlEntity.createFormattedUrlEntity(
mentionEntity));
final SpannableStringBuilder linkifiedText = (SpannableStringBuilder)
TweetTextLinkifier.linkifyUrls(formattedText, mockClickListener, 0, 0, true,
true);
final ClickableSpan[] clickables =
linkifiedText.getSpans(mentionEntity.getStart(), mentionEntity.getEnd(),
ClickableSpan.class);
assertEquals(1, clickables.length);
}
private void setTextViewHTML(TextView textView, String string) {
// trim two trailing blank lines
CharSequence sequence =
Html.fromHtml(string.replace("<p>", "<br /><br />").replace("\n", "<br />"), null, mHTMLCodeTagHandler);
// use Chrome custom tab if it is available
if (mContext instanceof CommentsActivity
&& ((CommentsActivity) mContext).getChromeCustomTabsHelper() != null) {
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for (URLSpan span : urls) {
makeLinkClickable(strBuilder, span);
}
textView.setText(strBuilder);
} else {
textView.setText(sequence);
}
}
public static void setTextViewHTML(@NonNull TextView text, @NonNull String html, LinkClickableSpan listener)
{
CharSequence sequence = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for(URLSpan span : urls) {
makeLinkClickable(strBuilder, span, listener);
}
text.setText(strBuilder);
text.setMovementMethod(LinkMovementMethod.getInstance());
}
private void setTextViewHTML(TextView text, String html) {
CharSequence sequence = Html.fromHtml(html.replaceAll("\n", ""));
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for (URLSpan span : urls) {
makeLinkClickable(strBuilder, span);
}
text.setText(strBuilder);
text.setMovementMethod(LinkMovementMethod.getInstance());
}
private static SpannableStringBuilder addBullets(Spanned htmlSpanned) {
SpannableStringBuilder spannableBuilder = new SpannableStringBuilder(htmlSpanned);
BulletSpan[] spans = spannableBuilder.getSpans(0, spannableBuilder.length(), BulletSpan.class);
if (spans != null) {
for (BulletSpan span : spans) {
int start = spannableBuilder.getSpanStart(span);
int end = spannableBuilder.getSpanEnd(span);
spannableBuilder.removeSpan(span);
spannableBuilder.setSpan(new ImprovedBulletSpan(dip(2), dip(8), 0), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
return spannableBuilder;
}
private static boolean hasAnimatedSpans(SpannableStringBuilder spannableBuilder) {
CustomTypefaceSpan[] spans = spannableBuilder.getSpans(0, spannableBuilder.length(), CustomTypefaceSpan.class);
for (CustomTypefaceSpan span : spans) {
if (span.isAnimated())
return true;
}
return false;
}
private boolean checkInCode(SpannableStringBuilder builder, int start, int end) {
CodeSpan[] css = builder.getSpans(0, builder.length(), CodeSpan.class);
for (CodeSpan cs : css) {
int c_start = builder.getSpanStart(cs);
int c_end = builder.getSpanEnd(cs);
if (!(c_start >= end || c_end <= start)) {
return true;
}
}
return false;
}
/**
* Parses String containing HTML to Android's Spannable format and displays it in this TextView.
*
* @param html String containing HTML, for example: "<b>Hello world!</b>"
*/
public void setHtmlFromString(String html) {
SpannableStringBuilder spanText = (SpannableStringBuilder) Html.fromHtml(html, new UrlImageGetter(this, getContext(), mImageLoader), new HtmlTagHandler());
URLSpan[] spans = spanText.getSpans(0, spanText.length(), URLSpan.class);
// TODO URLSpan 必需用自定义的 ClickableSpan (DiscourseURLSpan) 替换, 否则 URLSpan不可点击, 显示也没有下划线。 WHY?
for (URLSpan span : spans) {
int start = spanText.getSpanStart(span);
int end = spanText.getSpanEnd(span);
String url = span.getURL();
if (url != null) {
spanText.removeSpan(span);
ClickableSpan span1 = new DiscourseURLSpan(span.getURL());
spanText.setSpan(span1, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
// ImageSpan[] images = spanText.getSpans(0, spanText.length(), ImageSpan.class);
// for (ImageSpan image : images) {
// int start = spanText.getSpanStart(image);
// int end = spanText.getSpanEnd(image);
// L.i("%d-%d :%s", start, end, image.getSource());
// spanText.removeSpan(image);
// DynamicDrawableSpan is = new DiscourseImageSpan(getContext(), image.getDrawable());
// spanText.setSpan(is, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//
// }
setText(spanText);
// make links work
setMovementMethod(LinkMovementMethod.getInstance());
// no flickering when clicking textview for Android < 4
// text.setTextColor(getResources().getColor(android.R.color.secondary_text_dark_nodisable));
}
private FixingResult addSpacesAroundSpansUntilFixed(SpannableStringBuilder builder, int widthMeasureSpec, int heightMeasureSpec) {
Object[] spans = builder.getSpans(0, builder.length(), Object.class);
List<Object> spansWithSpacesBefore = new ArrayList<Object>(spans.length);
List<Object> spansWithSpacesAfter = new ArrayList<Object>(spans.length);
for (Object span : spans) {
int spanStart = builder.getSpanStart(span);
if (isNotSpace(builder, spanStart - 1)) {
builder.insert(spanStart, " ");
spansWithSpacesBefore.add(span);
}
int spanEnd = builder.getSpanEnd(span);
if (isNotSpace(builder, spanEnd)) {
builder.insert(spanEnd, " ");
spansWithSpacesAfter.add(span);
}
try {
setTextAndMeasure(builder, widthMeasureSpec, heightMeasureSpec);
return FixingResult.fixed(spansWithSpacesBefore, spansWithSpacesAfter);
} catch (IndexOutOfBoundsException notFixed) {
}
}
if (BuildConfig.DEBUG) {
Log.d(HtmlTextView.TAG, "Could not fix the Spanned by adding spaces around spans");
}
return FixingResult.notFixed();
}
@Test
public void append_spanned_normal() {
// #append is called with regular Spanned content -> spans should be added in reverse
final SpannableStringBuilder ssb = new SpannableStringBuilder();
for (int i = 0; i < 3; i++) {
ssb.append(String.valueOf(i));
ssb.setSpan(i, i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
assertTrue(builder.getSpans(0, builder.length()).isEmpty());
builder.append(ssb);
assertEquals("012", builder.toString());
// this one would return normal order as spans are reversed here
// final List<SpannableBuilder.Span> spans = builder.getSpans(0, builder.length());
final SpannableStringBuilder spannableStringBuilder = builder.spannableStringBuilder();
final Object[] spans = spannableStringBuilder.getSpans(0, builder.length(), Object.class);
assertEquals(3, spans.length);
for (int i = 0, length = spans.length; i < length; i++) {
assertEquals(length - 1 - i, spans[i]);
}
}
private String getRealText(boolean saved) {
if (saved && mMode == MODE_NONE) {
return null;
}
Editable editable = getText();
if (editable == null || editable.length() == 0) {
return "";
}
SpannableStringBuilder value = new SpannableStringBuilder(editable);
IPlaceholderSpan[] spans;
if (mMode == MODE_NONE) {
spans = EMPTY_SPANS;
} else if (mMode < MODE_MASK) {
spans =
value.getSpans(
0,
Math.min(value.length(), mHolders[mHolders.length - 1].index),
IPlaceholderSpan.class);
} else {
spans =
value.getSpans(
0,
Math.min(value.length(), mFormatStyle.length()),
IPlaceholderSpan.class);
if (spans.length == mFormatStyle.length()) {
return "";
}
}
if (spans.length == 0) {
if (saved) {
value.clear();
return null;
}
} else {
clearNonEmptySpans(value, spans, false);
}
final String realText = value.toString();
value.clear();
return realText;
}
/**
* An extension to {@link HtmlUtils#parseHtml(String, ColorStateList, int)} which adds Dribbble
* specific behaviour.
*/
public static Spanned parseDribbbleHtml(
String input,
ColorStateList linkTextColor,
@ColorInt int linkHighlightColor) {
SpannableStringBuilder ssb = HtmlUtils.parseHtml(input, linkTextColor, linkHighlightColor);
TouchableUrlSpan[] urlSpans = ssb.getSpans(0, ssb.length(), TouchableUrlSpan.class);
for (TouchableUrlSpan urlSpan : urlSpans) {
int start = ssb.getSpanStart(urlSpan);
if (ssb.subSequence(start, start + 1).toString().equals("@")) {
int end = ssb.getSpanEnd(urlSpan);
ssb.removeSpan(urlSpan);
HttpUrl url = HttpUrl.parse(urlSpan.getURL());
long playerId = -1l;
String playerUsername = null;
try {
playerId = Long.parseLong(url.pathSegments().get(0));
} catch (NumberFormatException nfe) {
playerUsername = url.pathSegments().get(0);
}
ssb.setSpan(new PlayerSpan(urlSpan.getURL(),
ssb.subSequence(start + 1, end).toString(),
playerId,
playerUsername,
linkTextColor,
linkHighlightColor),
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return ssb;
}
/**
* Formats {@code node} and its descendants, appending the result
* to {@code sb}.
*/
private void formatSubtree(AccessibilityNodeInfoCompat node,
Editable result) {
if (!node.isVisibleToUser()) {
return;
}
BrailleRule rule = mRuleRepository.find(node);
SpannableStringBuilder subtreeResult = new SpannableStringBuilder();
rule.format(subtreeResult, mContext, node);
if (rule.includeChildren(node, mContext)) {
int childCount = node.getChildCount();
for (int i = 0; i < childCount; ++i) {
AccessibilityNodeInfoCompat child = node.getChild(i);
if (child == null) {
continue;
}
formatSubtree(child, subtreeResult);
child.recycle();
}
}
if (!TextUtils.isEmpty(subtreeResult)) {
// If the node is accessibility focused, add the focus span
// here to cover the node and its formatted children.
// This is a fallback in case the formatting rule hasn't set
// focus by itself.
if (node.isAccessibilityFocused()
&& subtreeResult.getSpans(0, subtreeResult.length(),
DisplaySpans.FocusSpan.class).length == 0) {
DisplaySpans.addFocus(subtreeResult, 0,
subtreeResult.length());
}
addNodeSpanForUncovered(node, subtreeResult);
StringUtils.appendWithSpaces(result, subtreeResult);
}
}
private CharSequence getClickableHtml(Spanned spannedHtml) {
SpannableStringBuilder clickableHtmlBuilder = new SpannableStringBuilder(spannedHtml);
URLSpan[] urls = clickableHtmlBuilder.getSpans(0, spannedHtml.length(), URLSpan.class);
for (final URLSpan span : urls) {
setLinkClickable(clickableHtmlBuilder, span);
clickableHtmlBuilder.removeSpan(span);
}
return clickableHtmlBuilder;
}
@Override
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
if (charSequence instanceof SpannableStringBuilder) {
SpannableStringBuilder span = (SpannableStringBuilder) charSequence;
Object[] spans = span.getSpans(0, span.length(), CharacterStyle.class);
Iterator iterator = null;
if (mData.get(position).getSpanList() != null) {
iterator = mData.get(position).getSpanList().iterator();
}
boolean needAdd = false;
for (int i = 0; i < spans.length; i++) {
SpanModel model;
if (iterator != null && iterator.hasNext()) {
//复用原本的Model,防止重复New
model = (SpanModel) iterator.next();
if (model.mSpans != null) {
model.mSpans.clear();
} else {
model.mSpans = new ArrayList<>();
}
} else {
RichLog.log("NEW-------------");
needAdd = true;
model = new SpanModel();
}
model.mSpans.add(spans[i]);
model.end = span.getSpanEnd(spans[i]);
model.start = span.getSpanStart(spans[i]);
for (++i; i < spans.length; i++) {
if (span.getSpanEnd(spans[i]) == model.end && span.getSpanStart(spans[i]) == model.start) {
model.mSpans.add(spans[i]);
} else {
i--;
break;
}
}
if (needAdd) {
mData.get(position).getSpanList().add(model);
}
}
while (!needAdd && iterator != null && iterator.hasNext()) {
iterator.next();
iterator.remove();
}
for (SpanModel item : mData.get(position).getSpanList()) {
Log.i(BASE_LOG, item.mSpans + "start:" + item.start + "end:" + item.end);
}
}
mData.get(position).setSource(charSequence.toString());
}
private boolean isCached(SpannableStringBuilder ssb) {
Cached[] cs = ssb.getSpans(0, ssb.length(), Cached.class);
return cs != null && cs.length > 0;
}