返回介绍

2.1 SpannableString

发布于 2024-12-23 22:16:00 字数 4007 浏览 0 评论 0 收藏 0

SpannableString 是由一个内部 SpannableStringInternal 实现的,所以我们就直接看 SpannableStringInternal 源码:

/* package */ 
void setSpan(Object what, int start, int end, int flags) {
  int nstart = start;
  int nend = end;

  // 检查 start 和 end 是否合法
  checkRange("setSpan", start, end);

  // 对 SPAN_PARAGRAPH 做格式检查
  if ((flags & Spannable.SPAN_PARAGRAPH) == Spannable.SPAN_PARAGRAPH) {
    if (start != 0 && start != length()) {
      char c = charAt(start - 1);
      if (c != '\n')
        throw new RuntimeException(
            "PARAGRAPH span must start at paragraph boundary" +
            " (" + start + " follows " + c + ")");
    }
    if (end != 0 && end != length()) {
      char c = charAt(end - 1);
      if (c != '\n')
        throw new RuntimeException(
            "PARAGRAPH span must end at paragraph boundary" +
            " (" + end + " follows " + c + ")");
    }
  }
  // 检查是否已经存在 span
  int count = mSpanCount;
  Object[] spans = mSpans;
  int[] data = mSpanData;
  for (int i = 0; i < count; i++) {
    if (spans[i] == what) {
      int ostart = data[i * COLUMNS + START];
      int oend = data[i * COLUMNS + END];
      data[i * COLUMNS + START] = start;
      data[i * COLUMNS + END] = end;
      data[i * COLUMNS + FLAGS] = flags;
      sendSpanChanged(what, ostart, oend, nstart, nend);
      return;
    }
  }
  // 检查数组大小
  if (mSpanCount + 1 >= mSpans.length) {
    Object[] newtags = ArrayUtils.newUnpaddedObjectArray(
        GrowingArrayUtils.growSize(mSpanCount));
    int[] newdata = new int[newtags.length * 3];
    System.arraycopy(mSpans, 0, newtags, 0, mSpanCount);
    System.arraycopy(mSpanData, 0, newdata, 0, mSpanCount * 3);
    mSpans = newtags;
    mSpanData = newdata;
  }
  mSpans[mSpanCount] = what;
  mSpanData[mSpanCount * COLUMNS + START] = start;
  mSpanData[mSpanCount * COLUMNS + END] = end;
  mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
  mSpanCount++;
  if (this instanceof Spannable)
    sendSpanAdded(what, nstart, nend);
}

首先是通过 checkRange 方法检查 start 和 end 是否合法:

private void checkRange(final String operation, int start, int end) {
  if (end < start) {
    throw new IndexOutOfBoundsException(operation + " " +
                      region(start, end) +
                      " has end before start");
  }
  int len = length();
  if (start > len || end > len) {
    throw new IndexOutOfBoundsException(operation + " " +
                      region(start, end) +
                      " ends beyond length " + len);
  }
  if (start < 0 || end < 0) {
    throw new IndexOutOfBoundsException(operation + " " +
                      region(start, end) +
                      " starts before 0");
  }
}

接下来检查是否是 SPAN_PARAGRAPH, 当 flag 是 SPAN_PARAGRAPH 时, start 和 end 必须要是字符串的开头和结尾,例如:

SpannableString ss = new SpannableString("abcd");
ss.setSpan(new UnderlineSpan(), 0,4, Spanned.SPAN_PARAGRAPH);

然后会检查是否已经存在了相同的 span 对象,如果已经存在相同的 span, 那么就会用新的覆盖旧的,所以在使用的时候要注意不要使用同一个对象:

SpannableString ss = new SpannableString("abcd");
UnderlineSpan underlineSpan = new UnderlineSpan();

ss.setSpan(underlineSpan, 1,2, Spanned.SPAN_INCLUSIVE_INCLUSIVE);  // 这个被覆盖了
ss.setSpan(underlineSpan, 3,4, Spanned.SPAN_INCLUSIVE_INCLUSIVE);

然后检查数组大小,不够大时扩容,最终将值保存起来,保存的方式也很容易理解,如下代码所示:

private static final int START = 0;
private static final int END = 1;
private static final int FLAGS = 2;
private static final int COLUMNS = 3;

mSpans[mSpanCount] = what;
mSpanData[mSpanCount * COLUMNS + START] = start;
mSpanData[mSpanCount * COLUMNS + END] = end;
mSpanData[mSpanCount * COLUMNS + FLAGS] = flags;
mSpanCount++;

到这里为止,我们的 span 就被保存起来了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文