在开发Android App时,常常会遇到各种协定,并且有些文字是灰色的,有些蓝色的,能够点击跳转,对于这种状况,其实咱们是能够对它进行一些封装的,因为这些性能都是通用的,成果如下。

能够看到,协定内容除了各种协定外,还蕴含很多的形容文案。对于这种需要,咱们能够通过SpannableStringBuilder来实现。首先,新建一个TextUtils工具类,它基于SpannableStringBuilder实现,代码如下。

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(),故请务必在调用该办法前保障strBuilder不为空,即调用了后面的办法        public Builder checkBox(Context context, TextView tv, OnImageClickListener listener){            setImageSpan(context, 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();                    } 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());//设置可点击状态            tv.setHighlightColor(Color.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);//居中对齐        builder.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);    }     public static class MyImageSpan extends ImageSpan{        //因为这里文字存在换行,零碎的ImageSpan图标无奈进行居中,所以咱们自定义一个ImageSpan,重写draw办法,解决了该问题        public MyImageSpan(@NonNull Context context, 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) {//自定义居中对齐                //与文字的中间线对齐(这种形式不管是否设置行间距都能保障文字的中间线和图片的中间线是对齐的)                // y+ascent失去文字内容的顶部坐标,y+descent失去文字的底部坐标,(顶部坐标+底部坐标)/2=文字内容中间线坐标                transY = ((y + fm.descent) + (y + fm.ascent)) / 2 - drawable.getBounds().bottom / 2;            }            canvas.translate(x, transY);            drawable.draw(canvas);            canvas.restore();        }    }}

而后,在须要应用的中央引入即可,如下所示。

 //\u3000实现占位缩进<string name="company_partner_protocol">\u3000\u3000我已认真浏览《委托付款协定》的全部内容,批准并承受《隐衷政策》全副条款。嘉联账户和单干账户余额提现时,将扣除x%%的服务费;</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:                                //跳转链接                                WebviewActivity.newInstance(CompanyPartner2Activity.this, Config.WITHDRAW_AGREEMENT, "");                                break;                            case 1:                                WebviewActivity.newInstance(CompanyPartner2Activity.this, Config.PRIVACY, "隐衷政策");                                break;                        }                    }                }, "《委托付款协定》", "《隐衷政策》").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);

其中,tv_protocol就是咱们的TextView组件。