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 (HtmlTextView.DEBUG) { Log.d(HtmlTextView.TAG, "Could not fix the Spanned by adding spaces around spans"); } return FixingResult.notFixed(); }
private void resetLinkSpan(SpannableStringBuilder ssb, RichTextConfig config, URLSpan urlSpan) { int start = ssb.getSpanStart(urlSpan); int end = ssb.getSpanEnd(urlSpan); ssb.removeSpan(urlSpan); LinkHolder linkHolder = new LinkHolder(urlSpan.getURL()); if (config.linkFixCallback != null) { config.linkFixCallback.fix(linkHolder); } LongClickableURLSpan longClickableURLSpan = new LongClickableURLSpan( linkHolder, config.onUrlClickListener, config.onUrlLongClickListener); ssb.setSpan(longClickableURLSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); }
/** * Set the necessary spans for each spoiler. * * <p>The algorithm works in the same way as <code>setCodeFont</code>. * * @param sequence * @return */ private CharSequence setSpoilerStyle(SpannableStringBuilder sequence) { int start = 0; int end = 0; for (int i = 0; i < sequence.length(); i++) { if (sequence.charAt(i) == '[' && i < sequence.length() - 3) { if (sequence.charAt(i + 1) == '[' && sequence.charAt(i + 2) == 's' && sequence.charAt(i + 3) == '[') { start = i; } } else if (sequence.charAt(i) == ']' && i < sequence.length() - 3) { if (sequence.charAt(i + 1) == 's' && sequence.charAt(i + 2) == ']' && sequence.charAt(i + 3) == ']') { end = i; } } if (end > start) { sequence.delete(end, end + 4); sequence.delete(start, start + 4); BackgroundColorSpan backgroundColorSpan = new BackgroundColorSpan(Color.BLACK); ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan(Color.BLACK); URLSpan urlSpan = sequence.getSpans(start, start, URLSpan.class)[0]; sequence.setSpan( urlSpan, sequence.getSpanStart(urlSpan), start - 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); // spoiler text has a space at the front sequence.setSpan( backgroundColorSpan, start + 1, end - 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); sequence.setSpan(foregroundColorSpan, start, end - 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); storedSpoilerSpans.add(foregroundColorSpan); storedSpoilerSpans.add(backgroundColorSpan); // Shift 1 to account for remove of beginning "<" storedSpoilerStarts.add(start - 1); storedSpoilerStarts.add(start - 1); storedSpoilerEnds.add(end - 5); storedSpoilerEnds.add(end - 5); sequence.delete(start - 2, start - 1); // remove the trailing < start = 0; end = 0; i = i - 5; // move back to compensate for removal of [[s[ } } return sequence; }
/** * @desc * <pre>表情解析,转成unicode字符</pre> * * @author Weiliang Hu * @date 2013-12-17 * @param cs * @param mContext * @return */ public static String convertToMsg(CharSequence cs, Context mContext) { SpannableStringBuilder ssb = new SpannableStringBuilder(cs); ImageSpan[] spans = ssb.getSpans(0, cs.length(), ImageSpan.class); for (int i = 0; i < spans.length; i++) { ImageSpan span = spans[i]; String c = span.getSource(); int a = ssb.getSpanStart(span); int b = ssb.getSpanEnd(span); if (c.contains("[")) { ssb.replace(a, b, convertUnicode(c)); } } ssb.clearSpans(); return ssb.toString(); }
protected void makeLinkClickable( SpannableStringBuilder strBuilder, final URLSpan span, final Activity c) { int start = strBuilder.getSpanStart(span); int end = strBuilder.getSpanEnd(span); int flags = strBuilder.getSpanFlags(span); final ClickableSpan clickable = new ClickableSpan() { public void onClick(View view) { // TODO make clickable ContentOpen.openingText(url, true, c); } }; strBuilder.setSpan(clickable, start, end, flags); strBuilder.removeSpan(span); }
public static void setTextWithIcon(Context context, TextView textView, String text) { Spanned html = Html.fromHtml(text); SpannableStringBuilder builder = new SpannableStringBuilder(html); ImageSpan[] images = builder.getSpans(0, builder.length(), ImageSpan.class); if (images != null && images.length > 0) { ImageSpan imagePlaceholder = images[0]; int start = builder.getSpanStart(imagePlaceholder); int end = builder.getSpanEnd(imagePlaceholder); int flags = builder.getSpanFlags(imagePlaceholder); ImageSpan imageSpan = new ImageSpan(context, R.drawable.lock_icon, ImageSpan.ALIGN_BASELINE); builder.setSpan(imageSpan, start, end, flags); builder.removeSpan(imagePlaceholder); } textView.setText(builder); }
@SuppressLint("WrongCall") private void removeUnneededSpaces( int widthMeasureSpec, int heightMeasureSpec, SpannableStringBuilder builder, FixingResult result) { for (Object span : result.spansWithSpacesAfter) { int spanEnd = builder.getSpanEnd(span); builder.delete(spanEnd, spanEnd + 1); try { setTextAndMeasure(builder, widthMeasureSpec, heightMeasureSpec); } catch (IndexOutOfBoundsException ignored) { builder.insert(spanEnd, " "); } } boolean needReset = true; for (Object span : result.spansWithSpacesBefore) { int spanStart = builder.getSpanStart(span); builder.delete(spanStart - 1, spanStart); try { setTextAndMeasure(builder, widthMeasureSpec, heightMeasureSpec); needReset = false; } catch (IndexOutOfBoundsException ignored) { needReset = true; int newSpanStart = spanStart - 1; builder.insert(newSpanStart, " "); } } if (needReset) { setText(builder); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }
private void icUpdateGecko(boolean force) { // Skip if receiving a repeated request, or // if suppressing compositions during text selection. if ((!force && mIcUpdateSeqno == mLastIcUpdateSeqno) || mSuppressCompositions) { if (DEBUG) { Log.d(LOGTAG, "icUpdateGecko() skipped"); } return; } mLastIcUpdateSeqno = mIcUpdateSeqno; mActionQueue.syncWithGecko(); if (DEBUG) { Log.d(LOGTAG, "icUpdateGecko()"); } final int selStart = mText.getSpanStart(Selection.SELECTION_START); final int selEnd = mText.getSpanEnd(Selection.SELECTION_END); int composingStart = mText.length(); int composingEnd = 0; Object[] spans = mText.getSpans(0, composingStart, Object.class); for (Object span : spans) { if ((mText.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) { composingStart = Math.min(composingStart, mText.getSpanStart(span)); composingEnd = Math.max(composingEnd, mText.getSpanEnd(span)); } } if (DEBUG) { Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd); Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd); } if (composingStart >= composingEnd) { if (selStart >= 0 && selEnd >= 0) { onImeSetSelection(selStart, selEnd); } else { onImeRemoveComposition(); } return; } if (selEnd >= composingStart && selEnd <= composingEnd) { onImeAddCompositionRange( selEnd - composingStart, selEnd - composingStart, IME_RANGE_CARETPOSITION, 0, 0, false, 0, 0, 0); } int rangeStart = composingStart; TextPaint tp = new TextPaint(); TextPaint emptyTp = new TextPaint(); // set initial foreground color to 0, because we check for tp.getColor() == 0 // below to decide whether to pass a foreground color to Gecko emptyTp.setColor(0); do { int rangeType, rangeStyles = 0, rangeLineStyle = IME_RANGE_LINE_NONE; boolean rangeBoldLine = false; int rangeForeColor = 0, rangeBackColor = 0, rangeLineColor = 0; int rangeEnd = mText.nextSpanTransition(rangeStart, composingEnd, Object.class); if (selStart > rangeStart && selStart < rangeEnd) { rangeEnd = selStart; } else if (selEnd > rangeStart && selEnd < rangeEnd) { rangeEnd = selEnd; } CharacterStyle[] styleSpans = mText.getSpans(rangeStart, rangeEnd, CharacterStyle.class); if (DEBUG) { Log.d(LOGTAG, " found " + styleSpans.length + " spans @ " + rangeStart + "-" + rangeEnd); } if (styleSpans.length == 0) { rangeType = (selStart == rangeStart && selEnd == rangeEnd) ? IME_RANGE_SELECTEDRAWTEXT : IME_RANGE_RAWINPUT; } else { rangeType = (selStart == rangeStart && selEnd == rangeEnd) ? IME_RANGE_SELECTEDCONVERTEDTEXT : IME_RANGE_CONVERTEDTEXT; tp.set(emptyTp); for (CharacterStyle span : styleSpans) { span.updateDrawState(tp); } int tpUnderlineColor = 0; float tpUnderlineThickness = 0.0f; // These TextPaint fields only exist on Android ICS+ and are not in the SDK. if (Versions.feature14Plus) { tpUnderlineColor = (Integer) getField(tp, "underlineColor", 0); tpUnderlineThickness = (Float) getField(tp, "underlineThickness", 0.0f); } if (tpUnderlineColor != 0) { rangeStyles |= IME_RANGE_UNDERLINE | IME_RANGE_LINECOLOR; rangeLineColor = tpUnderlineColor; // Approximately translate underline thickness to what Gecko understands if (tpUnderlineThickness <= 0.5f) { rangeLineStyle = IME_RANGE_LINE_DOTTED; } else { rangeLineStyle = IME_RANGE_LINE_SOLID; if (tpUnderlineThickness >= 2.0f) { rangeBoldLine = true; } } } else if (tp.isUnderlineText()) { rangeStyles |= IME_RANGE_UNDERLINE; rangeLineStyle = IME_RANGE_LINE_SOLID; } if (tp.getColor() != 0) { rangeStyles |= IME_RANGE_FORECOLOR; rangeForeColor = tp.getColor(); } if (tp.bgColor != 0) { rangeStyles |= IME_RANGE_BACKCOLOR; rangeBackColor = tp.bgColor; } } onImeAddCompositionRange( rangeStart - composingStart, rangeEnd - composingStart, rangeType, rangeStyles, rangeLineStyle, rangeBoldLine, rangeForeColor, rangeBackColor, rangeLineColor); rangeStart = rangeEnd; if (DEBUG) { Log.d( LOGTAG, " added " + rangeType + " : " + Integer.toHexString(rangeStyles) + " : " + Integer.toHexString(rangeForeColor) + " : " + Integer.toHexString(rangeBackColor)); } } while (rangeStart < composingEnd); onImeUpdateComposition(composingStart, composingEnd); }
private int handleImage( SpannableStringBuilder ssb, ImageGetterWrapper imageGetterWrapper, RichTextConfig config, boolean cached) { if (cached) { ClickableImageSpan[] cis = ssb.getSpans(0, ssb.length(), ClickableImageSpan.class); if (cis != null && cis.length > 0) { for (ClickableImageSpan ci : cis) { int start = ssb.getSpanStart(ci); int end = ssb.getSpanEnd(ci); ssb.removeSpan(ci); OnImageClickListener onImageClickListener = null; OnImageLongClickListener onImageLongClickListener = null; if (config.clickable > 0) { onImageClickListener = config.onImageClickListener; onImageLongClickListener = config.onImageLongClickListener; } Drawable drawable = imageGetterWrapper.getDrawable(ci.getSource()); if (drawable == null) { drawable = new ColorDrawable(Color.TRANSPARENT); } ClickableImageSpan nci = new ClickableImageSpan(drawable, ci, onImageClickListener, onImageLongClickListener); ssb.setSpan(nci, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return cis.length; } } else if (!config.noImage) { ImageSpan[] iss = ssb.getSpans(0, ssb.length(), ImageSpan.class); if (iss != null && iss.length > 0) { ArrayList<String> imageUrls = new ArrayList<>(iss.length); for (int i = 0; i < iss.length; i++) { ImageSpan imageSpan = iss[i]; String imageUrl = imageSpan.getSource(); imageUrls.add(imageUrl); int start = ssb.getSpanStart(imageSpan); int end = ssb.getSpanEnd(imageSpan); ClickableSpan[] clickableSpans = ssb.getSpans(start, end, ClickableSpan.class); if (clickableSpans != null && clickableSpans.length != 0) { for (ClickableSpan cs : clickableSpans) { ssb.removeSpan(cs); } } OnImageClickListener onImageClickListener = null; OnImageLongClickListener onImageLongClickListener = null; if (config.clickable > 0) { onImageClickListener = config.onImageClickListener; onImageLongClickListener = config.onImageLongClickListener; } Drawable drawable = imageGetterWrapper.getDrawable(imageUrl); if (drawable == null) { drawable = new ColorDrawable(Color.TRANSPARENT); } ClickableImageSpan cacheImageSpan = new ClickableImageSpan( drawable, imageUrls, i, onImageClickListener, onImageLongClickListener); ssb.removeSpan(imageSpan); ssb.setSpan(cacheImageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return iss.length; } } return 0; }