Search code examples
androidandroid-canvastextviewandroid-custom-viewandroid-custom-attributes

give space between text and underline with customview android


I want an underline below OPTING

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.


Solution

  • 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>