@Override public void clearSpans() { /* XXX this clears the selection spans too, but there is no way to clear the corresponding selection in Gecko */ Log.w(LOGTAG, "selection cleared with clearSpans()"); mText.clearSpans(); }
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tv = new TextView(this); ctx = this; String htmlLinkText = "我是超链接" + "<a href='lianjie'><font color='red'>超链接点击事件</font></a>"; // 文字的样式(style)被覆盖,不能改变…… tv.setText(Html.fromHtml(htmlLinkText)); tv.setMovementMethod(LinkMovementMethod.getInstance()); CharSequence text = tv.getText(); if (text instanceof Spannable) { int end = text.length(); Spannable sp = (Spannable) tv.getText(); URLSpan[] urls = sp.getSpans(0, end, URLSpan.class); SpannableStringBuilder style = new SpannableStringBuilder(text); style.clearSpans(); // should clear old spans // 循环把链接发过去 for (URLSpan url : urls) { MyURLSpan myURLSpan = new MyURLSpan(url.getURL()); style.setSpan( myURLSpan, sp.getSpanStart(url), sp.getSpanEnd(url), Spannable.SPAN_EXCLUSIVE_INCLUSIVE); } tv.setText(style); } setContentView(tv); }
/** * @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(); }
private void updateErrorText() { mErrorText.clear(); mErrorText.clearSpans(); final int length = mTextInputLayout.getEditText().length(); if (length > 0) { mErrorText.append(String.valueOf(length)); mErrorText.append(" / "); mErrorText.append(String.valueOf(mMaxLen)); mErrorText.setSpan( mAlignmentSpan, 0, mErrorText.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); if (hasValidLength()) { mErrorText.setSpan( mNormalTextAppearance, 0, mErrorText.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } mTextInputLayout.setError(mErrorText); }
@WrapForJNI @Override public void onTextChange( final CharSequence text, final int start, final int unboundedOldEnd, final int unboundedNewEnd) { if (DEBUG) { // GeckoEditableListener methods should all be called from the Gecko thread ThreadUtils.assertOnGeckoThread(); StringBuilder sb = new StringBuilder("onTextChange("); debugAppend(sb, text); sb.append(", ") .append(start) .append(", ") .append(unboundedOldEnd) .append(", ") .append(unboundedNewEnd) .append(")"); Log.d(LOGTAG, sb.toString()); } if (start < 0 || start > unboundedOldEnd) { Log.e(LOGTAG, "invalid text notification range: " + start + " to " + unboundedOldEnd); throw new IllegalArgumentException("invalid text notification range"); } /* For the "end" parameters, Gecko can pass in a large number to denote "end of the text". Fix that here */ final int oldEnd = unboundedOldEnd > mText.length() ? mText.length() : unboundedOldEnd; // new end should always match text if (start != 0 && unboundedNewEnd != (start + text.length())) { Log.e( LOGTAG, "newEnd does not match text: " + unboundedNewEnd + " vs " + (start + text.length())); throw new IllegalArgumentException("newEnd does not match text"); } final int newEnd = start + text.length(); final Action action = mActionQueue.peek(); /* Text changes affect the selection as well, and we may not receive another selection update as a result of selection notification masking on the Gecko side; therefore, in order to prevent previous stale selection notifications from occurring, we need to increment the seqno here as well */ ++mGeckoUpdateSeqno; if (action != null && action.mType == Action.TYPE_ACKNOWLEDGE_FOCUS) { // Simply replace the text for newly-focused editors. mText.replace(0, mText.length(), text); } else { mChangedText.clearSpans(); mChangedText.replace(0, mChangedText.length(), text); // Preserve as many spans as possible TextUtils.copySpansFrom( mText, start, Math.min(oldEnd, newEnd), Object.class, mChangedText, 0); if (action != null && (action.mType == Action.TYPE_REPLACE_TEXT || action.mType == Action.TYPE_COMPOSE_TEXT) && start <= action.mStart && action.mStart + action.mSequence.length() <= newEnd) { // actionNewEnd is the new end of the original replacement action final int actionNewEnd = action.mStart + action.mSequence.length(); int selStart = Selection.getSelectionStart(mText); int selEnd = Selection.getSelectionEnd(mText); // Replace old spans with new spans mChangedText.replace(action.mStart - start, actionNewEnd - start, action.mSequence); geckoReplaceText(start, oldEnd, mChangedText); // delete/insert above might have moved our selection to somewhere else // this happens when the Gecko text change covers a larger range than // the original replacement action. Fix selection here if (selStart >= start && selStart <= oldEnd) { selStart = selStart < action.mStart ? selStart : selStart < action.mEnd ? actionNewEnd : selStart + actionNewEnd - action.mEnd; mText.setSpan(Selection.SELECTION_START, selStart, selStart, Spanned.SPAN_POINT_POINT); } if (selEnd >= start && selEnd <= oldEnd) { selEnd = selEnd < action.mStart ? selEnd : selEnd < action.mEnd ? actionNewEnd : selEnd + actionNewEnd - action.mEnd; mText.setSpan(Selection.SELECTION_END, selEnd, selEnd, Spanned.SPAN_POINT_POINT); } } else { // Gecko side initiated the text change. if (isSameText(start, oldEnd, mChangedText)) { // Nothing to do because the text is the same. // This could happen when the composition is updated for example. return; } geckoReplaceText(start, oldEnd, mChangedText); } } geckoPostToIc( new Runnable() { @Override public void run() { mListener.onTextChange(text, start, oldEnd, newEnd); } }); }