In the development of Android APP, we often encounter a variety of protocols, and some text is gray, some blue, you can click jump, for this situation, in fact, we can carry out some encapsulation of it, because these functions are universal, the effect is as follows.



As you can see, in addition to the various agreements, the agreement content also contains a lot of description. For this requirement, we can implement it with SpannableStringBuilder. First, create a new Textutils utility class, based on the SpannableStringBuilder implementation, as follows.

public class TextUtils { public static Builder getBuilder() { return new Builder(); } public static class Builder { private SpannableStringBuilder strBuilder; private Builder() { strBuilder = new SpannableStringBuilder(); } public Builder append(CharSequence text) { strBuilder.append(text); return this; } public Builder append(CharSequence text, int color) { int start = strBuilder.length(); strBuilder.append(text); int end = strBuilder.length(); strBuilder.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return this; } public Builder replace(CharSequence text, int color, String... replaces) { strBuilder.append(text); for (int i = 0; i < replaces.length; i++) { String replace = replaces[i]; int start = text.toString().indexOf(replace); if (start >= 0) { int end = start + replace.length(); strBuilder.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } return this; } public Builder click(CharSequence text, final int color, final OnClickListener onClickListener,String... clickTexts) { strBuilder.append(text); for (int i = 0; i < clickTexts.length; i++) { String clickText = clickTexts[i]; final int postion=i; int start = text.toString().indexOf(clickText); if (start >= 0) { int end = start + clickText.length(); strBuilder.setSpan(new ClickableSpan() { @Override public void onClick(View view) { if (onClickListener ! = null) { onClickListener.onClick(postion); } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(color); ds.setUnderlineText(false); } }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } return this; } private boolean isChecked = false; StrBuilder.append () is not called in this method, so make sure that strBuilder is not empty before calling this method. Public Builder CheckBox (Context Context, TextView TV, OnImageClickListener Listener){setAgespan (Context, TextView, OnImageClickListener){setAgespan (Context, TextView, OnImageClickListener); strBuilder, R.drawable.xzhhr_icon_circle2x); strBuilder.setSpan(new ClickableSpan() { @Override public void onClick(@NonNull View view) { isChecked = ! isChecked; if (isChecked){ setImageSpan(context, strBuilder, R.drawable.xzhhr_icon_tick2x); tv.setText(strBuilder); Listener.onChecked (); listener.onChecked(); } else { setImageSpan(context, strBuilder, R.drawable.xzhhr_icon_circle2x); tv.setText(strBuilder); listener.onUnChecked(); } } @Override public void updateDrawState(TextPaint ds) { super.updateDrawState(ds); ds.setColor(Color.WHITE); ds.setUnderlineText(false); } }, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return this; } public Builder clickInto(TextView tv) { tv.setMovementMethod(LinkMovementMethod.getInstance()); // Set the clickable state tv.seHighlightColor (Color. Transparent); // Set the color to transparent TV.setText (strBuilder); return this; } public Builder into(TextView tv) { tv.setText(strBuilder); return this; } } public interface OnClickListener { void onClick(int position); } public interface OnImageClickListener{ void onChecked(); void onUnChecked(); } private static void setImageSpan(Context context, SpannableStringBuilder builder, int resourceId){ MyImageSpan imageSpan = new MyImageSpan(context, resourceId, 2); // Align Builder. SetSpan (ImageSpan, 0, 1, spanned.span_exclusive_exclusive); } public static class MyImageSpan extends ImageSpan{public static class MyImageSpan extends ImageSpan{public static class MyImageSpan extends ImageSpan{public static class MyImageSpan extends ImageSpan{public static class MyImageSpan extends ImageSpan{ Public myImageSpan (@nonnull Context Context, int ResourceId, int ResourceId, int ResourceId, int verticalAlignment) { super(context, resourceId, verticalAlignment); } @Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { Drawable drawable = getDrawable(); canvas.save(); Paint.FontMetricsInt FM = Paint. GetFontMetricsInt (); int transY = bottom - drawable.getBounds().bottom; if (mVerticalAlignment == ALIGN_BASELINE) { transY -= fm.descent; } else if (mVerticalAlignment == ALIGN_CENTER) {// Align with the text midline (this way ensures that the text midline is aligned with the image midline with or without line spacing) // Y + Ascent = top, y+ Descent = bottom /2 - Drawable.getBounds ().bottom /2; Drawable.getBounds ().bottom /2; } canvas.translate(x, transY); drawable.draw(canvas); canvas.restore(); }}}

Then, introduce it where you need to use it, as shown below.

//\u3000 Implement placeholder indentation <string name="company_partner_protocol">\u3000\u3000 I have carefully read the entire contents of the Entrust Payment Agreement and agree and accept all the terms and conditions of the Privacy Policy. X %% service charge will be deducted when the balance of Jialian account and cooperative account is withdrawn; </string> TextUtils.getBuilder().click(getResources().getString(R.string.company_partner_protocol), getResources().getColor(R.color.blue), new TextUtils.OnClickListener() { @Override public void onClick(int position) { switch (position){ case 0: / / jump links WebviewActivity. NewInstance (CompanyPartner2Activity. This Config. WITHDRAW_AGREEMENT, ""); break; Case 1: WebviewActivity. NewInstance (CompanyPartner2Activity. This Config. The PRIVACY, "PRIVACY policy"); break; }}, "Payment by Entrusted Agreement "," Privacy Policy ").checkbox (this, tv_protocol, new TextUtils.OnImageClickListener() { @Override public void onChecked() { btn_commit.setEnabled(true); // ToastUtils.showToast(CompanyPartner2Activity.this, "checked"); } @Override public void onUnChecked() { btn_commit.setEnabled(false); // ToastUtils.showToast(CompanyPartner2Activity.this, "unChecked"); } }).clickInto(tv_protocol);

Where, the TV_PROTOCOL is our TextView component.