Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
372 views
in Technique[技术] by (71.8m points)

android - Changing the underline color of TextView

I am trying to change the color of the underline indicator in a TextView.

I know I can underline text like this

Spanned htmlString = Html.fromHtml("<u> <font color="#FF0000"> some text </font> </u>")
someTextView.setText(htmlString);

And like this

SpannableString content = new SpannableString(test);
content.setSpan(new UnderlineSpan(), 0, test.length(), 0);
tvTest.setText(content);

But both solutions use the same color for the text and the underline indicator. I want to know if I can change the color of the underline indicator in a sane way without any background XML and reflection hacks as seen here

I want to underline only the text, not the paddings, margins. My text can span over multiple lines too, I want every line to be underlined. (This is where the XML solution fails).

Result of my code

enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

It feels weird answering my own question, but for the sake of anyone having the same problem, I will. 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 lineBoundsRect;
    private Paint underlinePaint;

    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 mColor = typedArray.getColor(R.styleable.UnderlinedTextView_underlineColor, 0xFFFF0000);
        float mStrokeWidth = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineWidth, density * 2);
        typedArray.recycle();

        lineBoundsRect = new Rect();
        underlinePaint = new Paint();
        underlinePaint.setStyle(Paint.Style.STROKE);
        underlinePaint.setColor(mColor); //color of the underline
        underlinePaint.setStrokeWidth(mStrokeWidth);
    }

    @ColorInt
    public int getUnderLineColor() {
        return underlinePaint.getColor();
    }

    public void setUnderLineColor(@ColorInt int mColor) {
        underlinePaint.setColor(mColor);
        invalidate();
    }

    public float getUnderlineWidth() {
       underlinePaint.getStrokeWidth()
    }

    public void setUnderlineWidth(float mStrokeWidth) {
        underlinePaint.setStrokeWidth(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, lineBoundsRect);
            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, underlinePaint);
        }

        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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

56.8k users

...