How to underline text in TextView with some differ

2019-01-24 06:25发布

I know how to underline text in a textview. But how to underline text with some different color ? Underline can be done with:

TextView t = (TextView) findViewById(R.id.textview);
t.setPaintFlags(t.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
t.setText("Underline Text");

Let say my textcolor is black and I want to underline the same with blue color, how to do it ? Thanks in advance.

6条回答
Evening l夕情丶
2楼-- · 2019-01-24 06:41

If you are an XML's fan. Take a look at my solution:

Create a selector selector_edittext_white.xml in drawable folder

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:bottom="-15dp">
    <rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:fromDegrees="0"
        android:pivotX="0.5"
        android:pivotY="0.5"
        android:toDegrees="0">
        <shape android:shape="line">
            <stroke
                android:width="0.5dp"
                android:color="@android:color/white" />
        </shape>
    </rotate>
  </item>
</layer-list>

Then, set your EditText

android:background="@drawable/selector_edittext_white"

In the setting above, the underline's color is white, and you can move it by changing android:bottom above is "-15dp" . In case it's disappear, try to set your EditText bottom margin like this

android:layout_marginBottom="5dp"
查看更多
我想做一个坏孩纸
3楼-- · 2019-01-24 06:46

You can try out as below:

  String styledText = "<u><font color='red'>Underline Text</font></u>.";
  textView.setText(Html.fromHtml(styledText), TextView.BufferType.SPANNABLE);
查看更多
Explosion°爆炸
4楼-- · 2019-01-24 06:47
    Paint p = new Paint();
    p.setColor(Color.RED);

    TextView t = (TextView) findViewById(R.id.textview);
    t.setPaintFlags(p.getColor());
    t.setPaintFlags(Paint.UNDERLINE_TEXT_FLAG);
    t.setText("Underline Text");    

make a new paint color. and assign the paint to the textview.

查看更多
爷、活的狠高调
5楼-- · 2019-01-24 06:49

I had the same problem and I have stumbled upon Layout class when reading some other posts for doing this on EditText. It provides everything you need to make this happen by manually drawing underline with canvas.

First I defined custom attributes for an easy customization in XML layout files

<declare-styleable name="UnderlinedTextView" >
    <attr name="underlineWidth" format="dimension" />
    <attr name="underlineColor" format="color" />
</declare-styleable>

And a custom TextView class

public class UnderlinedTextView extends AppCompatTextView {

    private Rect mRect;
    private Paint mPaint;
    private float mStrokeWidth;

    public UnderlinedTextView(Context context) {
        this(context, null, 0);
    }

    public UnderlinedTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public UnderlinedTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attributeSet, int defStyle) {

        float density = context.getResources().getDisplayMetrics().density;

        TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.UnderlinedTextView, defStyle, 0);
        int underlineColor = typedArray.getColor(R.styleable.UnderlinedTextView_underlineColor, 0xFFFF0000);
        mStrokeWidth = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineWidth, density * 2);
        typedArray.recycle();

        mRect = new Rect();
        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(underlineColor);
        mPaint.setStrokeWidth(mStrokeWidth);
    }

    public int getUnderLineColor() {
        return mPaint.getColor();
    }

    public void setUnderLineColor(int mColor) {
        mPaint.setColor(mColor);
        invalidate();
    }

    public float getUnderlineWidth() {
        return mStrokeWidth;
    }

    public void setUnderlineWidth(float mStrokeWidth) {
        this.mStrokeWidth = mStrokeWidth;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        int count = getLineCount();

        final Layout layout = getLayout();
        float x_start, x_stop, x_diff;
        int firstCharInLine, lastCharInLine;

        for (int i = 0; i < count; i++) {
            int baseline = getLineBounds(i, mRect);
            firstCharInLine = layout.getLineStart(i);
            lastCharInLine = layout.getLineEnd(i);

            x_start = layout.getPrimaryHorizontal(firstCharInLine);
            x_diff = layout.getPrimaryHorizontal(firstCharInLine + 1) - x_start;
            x_stop = layout.getPrimaryHorizontal(lastCharInLine - 1) + x_diff;

            canvas.drawLine(x_start, baseline + mStrokeWidth, x_stop, baseline + mStrokeWidth, mPaint);
        }

        super.onDraw(canvas);
    }
}

Then it's usage is simple

<some.package.UnderlinedTextView
    android:id="@+id/tvTest"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="10dp"
    android:layout_marginLeft="20dp"
    android:layout_marginRight="20dp"
    android:gravity="center"
    android:text="This is a demo text"
    android:textSize="16sp"
    app:underlineColor="#ffc112ef"
    app:underlineWidth="3dp"/>

Final result

  • Multi line

    enter image description here
  • Single line

    enter image description here
查看更多
劫难
6楼-- · 2019-01-24 06:53

Another solution, this time without extending TextView (based on a question I wrote a long time ago, here) :

Have a drawable to be shown as the underline, and have a span for the text itself :

text_underline.xml

<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">
    <padding android:bottom="10dp"/>
    <stroke
        android:width="1dp"
        android:color="#3792e5"/>
</shape>

DrawableSpan.kt

class DrawableSpan(private val drawable: Drawable) : ReplacementSpan() {
    private val padding: Rect = Rect()

    init {
        drawable.getPadding(padding)
    }

    override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
        val rect = RectF(x, top.toFloat(), x + measureText(paint, text, start, end), bottom.toFloat())
        drawable.setBounds(rect.left.toInt() - padding.left, rect.top.toInt() - padding.top, rect.right.toInt() + padding.right, rect.bottom.toInt() + padding.bottom)
        canvas.drawText(text, start, end, x, y.toFloat(), paint)
        drawable.draw(canvas)
    }

    override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int = Math.round(paint.measureText(text, start, end))

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

}

usage:

    val text = getString(R.string.large_text)
    val spannable = SpannableString(text)
    spannable.setSpan(DrawableSpan(resources.getDrawable(R.drawable.text_underline)), 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
    textView.setText(spannable, TextView.BufferType.SPANNABLE)

And the result:

enter image description here

查看更多
唯我独甜
7楼-- · 2019-01-24 06:54

I can't add a comment yet, so I'm posting as an answer instead.

I just want to say that Bojan Kseneman's answer (https://stackoverflow.com/a/30717100/2771087) is fantastic. There's one issue with it that I'd like to correct, though.

Rather than finding the end position of the last character in a line, it's grabbing the end of second last character and then adding the width of the first character in the line. These two lines here:

x_diff = layout.getPrimaryHorizontal(firstCharInLine + 1) - x_start;
x_stop = layout.getPrimaryHorizontal(lastCharInLine - 1) + x_diff;

Instead of this, getSecondaryHorizontal() can be used to grab the opposite side of the character, as in:

x_stop = layout.getSecondaryHorizontal(lastCharInLine);

However, this will also underline the space at the end of each line for multi-line text areas. So to fix that issue, use code like the following to skip past it before calculating x_stop:

while (lastCharInLine != firstCharInLine &&
       Character.isWhitespace(getText().charAt(lastCharInLine - 1))) {
    lastCharInLine--;
}
查看更多
登录 后发表回答