Question

I am trying to subclass EditText to create a new textbox with horizonal lines (as found on lined paper). Here is my basic code:

public class LinedEditText extends EditText {

    // the paint we will use to draw the lines
    private Paint dashedLinePaint;

    // a reusable rect object
    private Rect reuseableRect;

    public LinedEditText(Context context) {
        super(context);
        init();
    }

    public LinedEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LinedEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * Initialize the paint object.
     */
    private void init() {

        // instantiate the rect
        reuseableRect = new Rect();

        // instantiate the paint
        dashedLinePaint = new Paint();
        dashedLinePaint.setARGB(200, 0, 0, 0);
        dashedLinePaint.setStyle(Style.STROKE);
        dashedLinePaint.setPathEffect(new DashPathEffect(new float[]{4, 6}, 0));
    }

    @Override
    protected void onDraw(Canvas canvas) {

        Log.e("LINE_HEIGHT", "Line height for typeface is " + getLineHeight());

        // get the height of the view
        int height = getHeight();

        // get the height of each line
        int lineHeight = getLineHeight();

        // the number of lines equals the height divided by the line height
        int numberOfLines = height / lineHeight;

        // if there are more lines than what appear on screen
        if (getLineCount() > numberOfLines) {

            // set the number of lines to the line count
            numberOfLines = getLineCount();
        }

        // get the rectangle instance
        Rect r = reuseableRect;

        // get the baseline for the first line
        int baseline = getLineBounds(0, r);

        // for each line
        for (int i = 0; i < numberOfLines; i++) {

            // draw the line
            canvas.drawLine(
                    r.left,                 // left
                    baseline,               // top
                    r.right,                // right
                    baseline,               // bottom
                    dashedLinePaint);       // paint instance

            // get the baseline for the next line
            baseline += getLineHeight();
        }

        super.onDraw(canvas);
    }
}

...this works perfectly when I use the default system font:

01-20 15:31:08.172 3966-3966/com... E/LINE_HEIGHT: Line height for typeface is 63

with system font

But as soon as I call linedEditTextBox.setTypeface(typeface), it is drawn this way:

01-20 15:33:58.938 4064-4064/com... E/LINE_HEIGHT: Line height for typeface is 83

with custom font

Does anyone know what is happening here? Should I be overriding setTypeface in my subclass, and doing some type of calculation to determine the new line height? I am stumped, any input is appreciated.

Here are some other StackOverflow questions I've looked at that seemed to be related: EditText draw lines wrong Drawing multiple lines in edittext e.g. notepad

Was it helpful?

Solution

Figured it out. For some reason I had to subtract one from the line height returned from getLineHeight(), no idea why. It works now, here is the complete code for the class:

public class LinedEditText extends EditText {

    // the vertical offset scaling factor (10% of the height of the text)
    private static final float VERTICAL_OFFSET_SCALING_FACTOR = 0.1f;

    // the dashed line scale factors
    private static final float DASHED_LINE_ON_SCALE_FACTOR = 0.008f;
    private static final float DASHED_LINE_OFF_SCALE_FACTOR = 0.0125f;

    // the paint we will use to draw the lines
    private Paint dashedLinePaint;

    // a reusable rect object
    private Rect reuseableRect;

    public LinedEditText(Context context) {
        super(context);
        init();
    }

    public LinedEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public LinedEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {

        // instantiate the rect
        reuseableRect = new Rect();

        // instantiate the paint
        dashedLinePaint = new Paint();
        dashedLinePaint.setARGB(200, 0, 0, 0);
        dashedLinePaint.setStyle(Style.STROKE);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        // set the path effect based on the view width
        dashedLinePaint.setPathEffect(
                new DashPathEffect(
                        new float[]{
                                getWidth() * DASHED_LINE_ON_SCALE_FACTOR,
                                getWidth() * DASHED_LINE_OFF_SCALE_FACTOR},
                        0));

        // get the height of the view
        int height = getHeight();

        // get the height of each line (not subtracting one from the line height makes lines uneven)
        int lineHeight = getLineHeight() - 1;

        // set the vertical offset basef off of the view width
        int verticalOffset = (int) (lineHeight * VERTICAL_OFFSET_SCALING_FACTOR);

        // the number of lines equals the height divided by the line height
        int numberOfLines = height / lineHeight;

        // if there are more lines than what appear on screen
        if (getLineCount() > numberOfLines) {

            // set the number of lines to the line count
            numberOfLines = getLineCount();
        }

        // get the baseline for the first line
        int baseline = getLineBounds(0, reuseableRect);

        // for each line
        for (int i = 0; i < numberOfLines; i++) {

            // draw the line
            canvas.drawLine(
                    reuseableRect.left,             // left
                    baseline + verticalOffset,      // top
                    reuseableRect.right,            // right
                    baseline + verticalOffset,      // bottom
                    dashedLinePaint);               // paint instance

            // get the baseline for the next line
            baseline += lineHeight;
        }

        super.onDraw(canvas);
    }
}

Looking correct now:

correct with new font

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top