give space between text and underline with customv

2019-07-21 16:02发布

问题:

I want an underline below OPTING like below, but when i create this will the help of customview, underline appears just below opting but i want some space between text and line like image

I had created a Custom view, in which a word will searched in a string if that is found, then that corresponding text will be underlined but the only thing i want is to give some space between underline and text,

My class is as follows,

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.AppCompatTextView;
import android.text.Layout;
import android.util.AttributeSet;
import android.view.Display;
import android.view.WindowManager;
import android.widget.TextView;


public class UnderLine  extends AppCompatTextView {

private Rect mRect;
private Paint mPaint;
private int mColor;
private float density;
private float mStrokeWidth;
private String stringSeach;

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

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

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

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

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

    TypedArray typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.UnderLine, defStyle, 0);
    mColor = typedArray.getColor(R.styleable.UnderLine_underlineColorr, 0xFFFF0000);
    stringSeach = typedArray.getString(R.styleable.UnderLine_underlineTextt);
    mStrokeWidth = typedArray.getDimension(R.styleable.UnderLine_underlineWidthh, density * 2);
    typedArray.recycle();

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

public int getUnderLineColor() {
    return mColor;
}

public void setUnderLineColor(int mColor) {
    this.mColor = mColor;
    invalidate();
}

public float getUnderlineWidth() {
    return mStrokeWidth;
}

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

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension( getMeasuredWidth(), getMeasuredHeight()+110);
}

@Override
protected void onDraw(Canvas canvas) {
    TextView parentTextView = this;
    Rect parentTextViewRect = new Rect();

    String targetWord = stringSeach.toLowerCase();
    int startOffsetOfClickedText = this.getText().toString().toLowerCase().indexOf(targetWord);
    int endOffsetOfClickedText = startOffsetOfClickedText + targetWord.length();

    // Initialize values for the computing of clickedText position
    Layout textViewLayout = parentTextView.getLayout();

    double startXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)startOffsetOfClickedText);
    double endXCoordinatesOfClickedText = textViewLayout.getPrimaryHorizontal((int)endOffsetOfClickedText);

    // Get the rectangle of the clicked text
    int currentLineStartOffset = textViewLayout.getLineForOffset((int)startOffsetOfClickedText);
    int currentLineEndOffset = textViewLayout.getLineForOffset((int)endOffsetOfClickedText);
    boolean keywordIsInMultiLine = currentLineStartOffset != currentLineEndOffset;
    textViewLayout.getLineBounds(currentLineStartOffset, parentTextViewRect);

    // Update the rectangle position to his real position on screen
    int[] parentTextViewLocation = {0,0};
    parentTextView.getLocationOnScreen(parentTextViewLocation);

    double parentTextViewTopAndBottomOffset = (
            //parentTextViewLocation[1] -
            parentTextView.getScrollY() +
                    parentTextView.getCompoundPaddingTop()
    );

    parentTextViewRect.top += parentTextViewTopAndBottomOffset;
    parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;

    // In the case of multi line text, we have to choose what rectangle take
    if (keywordIsInMultiLine){

        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        Display display = wm.getDefaultDisplay();

        int screenHeight = display.getHeight();
        int dyTop = parentTextViewRect.top;
        int dyBottom = screenHeight - parentTextViewRect.bottom;
        boolean onTop = dyTop > dyBottom;

        if (onTop){
            endXCoordinatesOfClickedText = textViewLayout.getLineRight(currentLineStartOffset);
        }
        else{
            parentTextViewRect = new Rect();
            textViewLayout.getLineBounds(currentLineEndOffset, parentTextViewRect);
            parentTextViewRect.top += parentTextViewTopAndBottomOffset;
            parentTextViewRect.bottom += parentTextViewTopAndBottomOffset;
            startXCoordinatesOfClickedText = textViewLayout.getLineLeft(currentLineEndOffset);
        }

    }

    parentTextViewRect.left += (
            parentTextViewLocation[0] +
                    startXCoordinatesOfClickedText +
                    parentTextView.getCompoundPaddingLeft() -
                    parentTextView.getScrollX()
    );
    parentTextViewRect.right = (int) (
            parentTextViewRect.left +
                    endXCoordinatesOfClickedText -
                    startXCoordinatesOfClickedText
    );


   canvas.drawLine(parentTextViewRect.left,parentTextViewRect.bottom+mStrokeWidth, parentTextViewRect.right,
            parentTextViewRect.bottom+mStrokeWidth, mPaint);
    super.onDraw(canvas);
}

}

attrs.xml is as follows,

  <declare-styleable name="UnderLine" >
    <attr name="underlineWidthh" format="dimension" />
    <attr name="underlineColorr" format="color" />
    <attr name="underlineTextt" format="string" />
</declare-styleable>

Sample layout as follows,

    <UnderLine
    style="@style/textView"
    android:gravity="top|center"
    app:underlineColorr="@color/signup_bottom_darkWhite"
    app:underlineWidthh="2dp"
    app:underlineTextt="OPTING"
    android:text="ON FREE PARKING + DISCOUNTED RATES \n BY OPTING IN"/>

I am a beginner with customview, and i had created this with the help of some answers on stackoverflow. Please don't suggest any other way to do this.

Anyhelp will be greatly appreciated.

回答1:

I had developed my own view, as follows,

package com.example.reprator.underlinetextview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.AppCompatTextView;
import android.text.Layout;
import android.util.AttributeSet;

public class UnderlinedTextView extends AppCompatTextView {

private Rect mRect;
private Paint mPaint;
private int mColor;
private float mStrokeWidth;
private float mMarginTop;
private boolean isAllSelected;
private int lineNumber;
private int selectTextEachLine;

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);
    mColor = typedArray.getColor(R.styleable.UnderlinedTextView_underlineColor, 0xFFFF0000);
    mStrokeWidth = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineWidth, density * 2);
    mMarginTop = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineMarginTop, density * 2);
    isAllSelected = typedArray.getBoolean(R.styleable.UnderlinedTextView_underlineIsAll, false);
    lineNumber = typedArray.getInteger(R.styleable.UnderlinedTextView_underlineNoLine, 1);
    selectTextEachLine = typedArray.getInteger(R.styleable.UnderlinedTextView_underlineTextEachLine, 3);
    typedArray.recycle();

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

public int getUnderLineColor() {
    return mColor;
}

public void setUnderLineColor(int mColor) {
    this.mColor = mColor;
    invalidate();
}

public float getUnderlineWidth() {
    return mStrokeWidth;
}

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

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int h = (int) (getMeasuredHeight() + mMarginTop);
    setMeasuredDimension(widthMeasureSpec, h);
}

@Override
protected void onDraw(Canvas canvas) {

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

    int limit = isAllSelected ? getLineCount() : lineNumber;
    for (int i = 0; i < limit; i++) {
        int baseline = getLineBounds(i, mRect);
        firstCharInLine = layout.getLineStart(i);
        lastCharInLine = layout.getLineEnd(i);

        int textHighlight = isAllSelected ? lastCharInLine - 1 : (firstCharInLine + selectTextEachLine);
        x_start = layout.getPrimaryHorizontal(firstCharInLine);
        x_stop = layout.getPrimaryHorizontal(textHighlight);

        float y = baseline + mStrokeWidth + mMarginTop;
        canvas.drawLine(x_start, y, x_stop, y, mPaint);
    }

    super.onDraw(canvas);
}

}

and my attars.xml are as follows,

 <declare-styleable name="UnderlinedTextView">
    <attr name="underlineWidth" format="dimension" />
    <attr name="underlineMarginTop" format="dimension" />
    <attr name="underlineColor" format="color" />
    <attr name="underlineText" format="string" />
    <attr name="underlineIsAll" format="boolean" />
    <attr name="underlineNoLine" format="integer" />
    <attr name="underlineTextEachLine" format="integer" />
</declare-styleable>