天天看点

Android图文混排

在开发中通常通常会遇到图文混编的情况,例如文字中插入置顶,热门等图片标签,在很多时候换行的时候TextView文字换行后都无法占满首位字符,要求TextView中要求某些文字设置其他颜色,字体,样式等等,这篇文章就是解决这种问题,如下:

Android图文混排
Android图文混排

一、SpannableString

SpannableString是富文本显示效果一个重要的类,是CharSequence的一种,和String一样,都是一种字符串类型,SpannableString可以直接作为TextView的显示文本,不同的是SpannableString可以通过使用其方法setSpan方法实现字符串各种形式风格的显示,重要的是可以指定设置的区间,也就是为字符串指定下标区间内的子字符串设置格式。在开发中,TextView可以通过setText(CharSequence)传入SpannableString作为参数,来达到显示不同样式文字的效果。一般通过以下方式进行设置:

spannableString.setSpan(Object what, int start, int end, int flags);

  1.     what:对SpannableString进行处理的各种Span;
  2.     int:需要处理文字段开始的下标;
  3.     end:需要处理文字段结束的下标;
  4.     flags:决定开始和结束下标是否包含的标志位,有四个参数可选
  •     SPAN_INCLUSIVE_EXCLUSIVE:包括开始下标,但不包括结束下标
  •     SPAN_EXCLUSIVE_INCLUSIVE:不包括开始下标,但包括结束下标
  •     SPAN_INCLUSIVE_INCLUSIVE:既包括开始下标,又包括结束下标
  •     SPAN_EXCLUSIVE_EXCLUSIVE:不包括开始下标,也不包括结束下标

除了SpannableString之外还有SpannableStringBuilder和SpannableString相似,类似于String和SpannableStringBuilder的关系, 在实际开发中发现SpannableStringBuilder字符占位会出现问题,所有没有用。

 二、setSpan

在这里主要介绍两类Span:ClickableSpan和ImageSpan,在开发中基本够用了。

1、ClickableSpan 继承自CharacterStyle,用来设置指定区间文字的点击事件和文字样式设置。

class TopicSpan extends ClickableSpan {

            @Override
            public void onClick(@NonNull View widget) {
                Toast.makeText(context,"点击了处理的文字",Toast.LENGTH_LONG).show();
            }

            @Override
            public void updateDrawState(@NonNull TextPaint ds) {
                super.updateDrawState(ds);
                //设置颜色、字体大小等等
                ds.setColor(Color.parseColor("#ff000"));
                //设置取消下划线
                ds.setUnderlineText(false);
            }
        }
        //在这里可以处理表情库添加到SpannableString构造方法里面作为参数
        SpannableString spannable = new SpannableString(text);
        spannable.setSpan(new TopicSpan(), 0, spannable.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textview.setText(spannable);
        //如果处理的本文需要点击跳转,则需要加上setMovementMethod响应点击事件,否则点击无响应
        textview.setMovementMethod(LinkMovementMethod.getInstance());
           

2、ImageSpan:继承自DynamicDrawableSpan,用在在指定的文本中插入图片。

class LogoSpan extends ImageSpan {

        public LogoSpan(@NonNull Context context, @NonNull Bitmap b) {
            super(context, b);
        }

        @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 b = getDrawable();
            Paint.FontMetricsInt fm = paint.getFontMetricsInt();
            int transY = (y + fm.descent + y + fm.ascent) / 2 - b.getBounds().bottom / 2;//计算y方向的位移
            canvas.save();
            canvas.translate(x, transY);//绘制图片位移一段距离
            b.draw(canvas);
            canvas.restore();
        }
    }
           

//"  "表示图片插入的文本

SpannableString imageSpannable = new SpannableString("  ");

LogoSpan imageSpan = new LogoSpan(context, bitmap);

//0, 1表示插入的文本位置

imageSpannable.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

textview.setText(imageSpannable);

    三、文本超过几行显示省略号

设置SpannableString后的TextView设置android:ellipsize="end"是无法生效的,但是android:maxLines="4"是可以生效的,意味着超过4行是无法显示省略号,直接就被截断了,需要重写TextVie的onDraw方法:

private static final String YX_THREE_DOTS = "...";
    private static final int YX_THREE_DOTS_LENGTH = YX_THREE_DOTS.length();
    private SpannableStringBuilder mSpannableStringBuilder;

    @Override
    protected void onDraw(Canvas canvas) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            final Layout layout = getLayout();

            if (layout.getLineCount() >= getMaxLines()) {
                CharSequence charSequence = getText();
                int lastCharDown = layout.getLineVisibleEnd(getMaxLines() - 1);

                if (lastCharDown >= YX_THREE_DOTS_LENGTH && charSequence.length() > lastCharDown) {
                    if (mSpannableStringBuilder == null) {
                        mSpannableStringBuilder = new SpannableStringBuilder();
                    } else {
                        mSpannableStringBuilder.clear();
                    }
                    mSpannableStringBuilder.append(charSequence.subSequence(0, lastCharDown - 1))
                            .append(YX_THREE_DOTS);
                    setText(mSpannableStringBuilder);
                }
            }
        }
        super.onDraw(canvas);
    }