【问题标题】:TextView with background color and line spacing具有背景颜色和行距的 TextView
【发布时间】:2014-05-21 07:35:04
【问题描述】:

我想显示如下文本...

我的编码如下:

SpannableString sText = new SpannableString(text);
sText.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, sText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

holder.txtText.setLineSpacing(0, 1.5f);
textView.setText(sText);

【问题讨论】:

  • 我也面临同样的问题。你找到解决办法了吗?

标签: android background textview html


【解决方案1】:

只是面临同样的问题,我发现这个答案对我来说很有效,应该也能解决你的问题。 在 xml 中的 TextView 上配置它:

android:lineSpacingMultiplier="2"

链接:How to add line spacing after every “\n” in a string in android?

已经 11 个月了,所以我希望你已经完成了它! 祝你好运!

【讨论】:

  • 他在谈论自动换行
【解决方案2】:

基于@evren-ozturk 答案,没有 textView 链接和更好的排版支持

class RoundedBackgroundSpan(
    private val textColor: Int,
    private val backgroundColor: Int
) : ReplacementSpan() {

    private val additionalPadding = 4.toPx().toFloat()
    private val cornerRadius = 4.toPx().toFloat()

    override fun draw(
        canvas: Canvas,
        text: CharSequence,
        start: Int,
        end: Int,
        x: Float,
        top: Int,
        y: Int,
        bottom: Int,
        paint: Paint
    ) {
        val newTop = y + paint.fontMetrics.ascent
        val newBottom = y + paint.fontMetrics.descent
        val rect = RectF(x, newTop, x + measureText(paint, text, start, end) + 2 * additionalPadding, newBottom)
        paint.color = backgroundColor

        canvas.drawRoundRect(rect, cornerRadius, cornerRadius, paint)
        paint.color = textColor
        canvas.drawText(text, start, end, x + additionalPadding, y.toFloat(), paint)
    }

    override fun getSize(paint: Paint, text: CharSequence?, start: Int, end: Int, fm: FontMetricsInt?): Int {
        return (paint.measureText(text, start, end) + 2 * additionalPadding).roundToInt()
    }

    private fun measureText(paint: Paint, text: CharSequence, start: Int, end: Int): Float {
        return paint.measureText(text, start, end)
    }

    private fun Int.toPx(): Int {
        val resources = Resources.getSystem()
        val metrics = resources.displayMetrics
        return Math.round(this * (metrics.densityDpi / 160.0f))
    }
}

【讨论】:

    【解决方案3】:

    对于那些有同样问题的人,你可以创建一个新的类扩展 ReplacemntSpan 类并重写它的 draw 方法,如下所示:

    
    public class RoundedBackgroundSpan extends ReplacementSpan {
    
        private int backgroundColor = 0;
        private int textColor = 0;
    
        public RoundBackgroundSpan(Context context) {
            super();
            backgroundColor = ContextCompat.getColor(context , R.color.amber_600);
            textColor = ContextCompat.getColor(context , R.color.white);
        }
    
        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
            float newBottom = bottom - 2;
            float newTop = top + 2;
            RectF rect = new RectF(x , newTop , x + measureText(paint, text, start, end), newBottom );
            paint.setColor(backgroundColor);
            int CORNER_RADIUS = 8;
            canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, paint);
            paint.setColor(textColor);
            canvas.drawText(text, start, end, x , y, paint);
        }
    
        @Override
        public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            return Math.round(paint.measureText(text, start, end));
        }
    
        private float measureText(Paint paint, CharSequence text, int start, int end) {
            return paint.measureText(text, start, end);
        }
    }
    

    并在您的 Spannable 中使用它..

    【讨论】:

      【解决方案4】:

      基于this 的一点修改解决方案,带有设置颜色的选项,在 kotlin 中:

      //https://stackoverflow.com/a/30096905/878126
      class BgColorTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatTextView(context, attrs, defStyleAttr) {
          @Suppress("MemberVisibilityCanBePrivate")
          var backgroundTextColor = -0x145bfb
              set(value) {
                  field = value
                  invalidate()
              }
      
          override fun draw(canvas: Canvas) {
              val currentLayout = layout
              if (currentLayout == null) {
                  super.draw(canvas)
                  return
              }
              val lineCount = currentLayout.lineCount
              val rect = Rect()
              val paint = Paint()
              paint.color = backgroundTextColor
              for (i in 0 until lineCount) {
                  rect.top = currentLayout.getLineTop(i)
                  rect.left = currentLayout.getLineLeft(i).toInt()
                  rect.right = currentLayout.getLineRight(i).toInt()
                  rect.bottom = currentLayout.getLineBottom(i) - (if (i + 1 == lineCount) 0 else currentLayout.spacingAdd.toInt())
                  canvas.drawRect(rect, paint)
              }
              super.draw(canvas)
          }
      }
      

      【讨论】:

        【解决方案5】:

        已经很久了,但我将静态方法放在下面。希望它会帮助另一个人。

        public static void setTextWithSpan(final TextView textView, int backgroundColor, String text, float lineSpacingMultiplier) {
            class BackgroundColorSpanWithPaddingAndLineSpacing implements LineBackgroundSpan {
                private float roundedCornerSize;
                private int backgroundColor;
                private int paddingSize;
                private RectF rect;
        
                private BackgroundColorSpanWithPaddingAndLineSpacing(int backgroundColor, int paddingSize, float roundedCornerSize) {
                    super();
                    this.backgroundColor = backgroundColor;
                    this.paddingSize = paddingSize;
                    this.roundedCornerSize = roundedCornerSize;
                    this.rect = new RectF();
                }
        
                @Override
                public void drawBackground(Canvas c, Paint p, int left, int right, int top, int baseline, int bottom, CharSequence text, int start, int end, int currentLineNumber) {
                    final int textWidth = Math.round(p.measureText(text, start, end));
                    final int paintColor = p.getColor();
        
                    rect.set(left - paddingSize / 2, top - paddingSize / 4, left + textWidth + paddingSize / 2, top + textView.getTextSize() + paddingSize / 2);
                    p.setColor(backgroundColor);
                    c.drawRoundRect(rect, roundedCornerSize, roundedCornerSize, p);
                    p.setColor(paintColor);
                }
            }
        
            int padding = textView.getPaddingLeft();
            int radius = padding / 2;
        
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            BackgroundColorSpanWithPaddingAndLineSpacing backgroundSpan = new BackgroundColorSpanWithPaddingAndLineSpacing(backgroundColor, padding, radius);
            builder.setSpan(backgroundSpan, 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        
            textView.setShadowLayer(padding, 0, 0, 0);
            textView.setLineSpacing(0, lineSpacingMultiplier);
        
            textView.setText(builder, TextView.BufferType.SPANNABLE);
        }
        

        用法:

        SpanUtils.setTextWithSpan(titleTv, android.graphics.Color.BLUE, textStr, 1.4f);
        

        我相信你会设法根据你的需要进行修改。

        【讨论】:

        • 文字背景好像在某些字母的底部被剪掉了,比如“y”的底部。
        【解决方案6】:

        试试这个。 创建自定义 TextView 并覆盖方法 draw(Canvas canvas)。

        public class BgColorTextView extends TextView {
            public BgColorTextView(Context context) {
                super(context);
            }
        
            public BgColorTextView(Context context, AttributeSet attrs) {
                super(context, attrs);
            }
        
            public BgColorTextView(Context context, AttributeSet attrs, int defStyleAttr) {
                super(context, attrs, defStyleAttr);
            }
        
            public BgColorTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
                super(context, attrs, defStyleAttr, defStyleRes);
            }
        
            @Override
            public void draw(Canvas canvas) {
                int lineCount = getLayout().getLineCount();
                Rect rect = new Rect();
                Paint paint = new Paint();
                paint.setColor(getResources().getColor(R.color.YOUR_CUSTOM_COLOR));
                for (int i = 0; i < lineCount; i++) {
                    rect.top = (getLayout().getLineTop(i));
                    rect.left = (int) getLayout().getLineLeft(i);
                    rect.right = (int) getLayout().getLineRight(i);
                    rect.bottom = (int) (getLayout().getLineBottom(i) - ((i + 1 == lineCount) ? 0 : getLayout().getSpacingAdd()));
        
                    canvas.drawRect(rect, paint);
                }
                super.draw(canvas);
            }
        }
        

        【讨论】:

        • 这可行,但“getLayout()”可能会返回 null 并崩溃。这种情况应该怎么办?
        • 布局为空时可以跳过绘制迭代。如果没有布局,什么会绘制android? =)
        • 是的。最后,这就是我所做的:stackoverflow.com/a/54667013/878126。但我认为这里有一个不同的问题,即使有空行也会绘制背景......
        【解决方案7】:

        您可以在 XML 文件中使用 lineSpacingExtralineSpacingMultiplier。你可以在这里找到它:http://developer.android.com/reference/android/widget/TextView.html#attr_android%3alineSpacingExtra

        对于背景颜色,您可以使用:

            <TextView
                  android:background="#FFFF00" 
           />
        

        【讨论】:

        • 我还是一样的。