Help us understand the problem. What is going on with this article?

テキストエディタに行数を表示するEditTextカスタム

More than 1 year has passed since last update.

AdventCalendar Android その2 の12日目の記事です


テキストエディタを作るには

Androidでテキスト入力を実装するにはEditTextを使うことになるかと思います。
テキストエディタを作るにはEditTextをマルチラインにし、場合によってはSpannableStringBuilderを駆使してリッチな機能を盛り込んでいきます。

今回は機能面ではなく、このようにエディタの行数をどうやって表示するかを解説します。
Screenshot_1513043110.png

実現方法

onDrawをoverrideします

CustomEdiText.java
package jp.advent2017.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.v7.widget.AppCompatEditText;
import android.text.Layout;
import android.util.AttributeSet;

/**
 * Created by yuta.ida on 2017/09/01.
 */

public class CustomEdiText extends AppCompatEditText {
    private static final String LF = "\n";

    public CustomEdiText(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onDraw(Canvas canvas) {
        String text = this.getText().toString();
        int displayLineCount = this.getLineCount();
        int lineNo = 1;
        int start = 0;
        Layout layout = this.getLayout();
        for (int i = 0; i < displayLineCount; i++) {

            if (isNewLine(text, start)) {
                drawLineNumber(canvas, i, lineNo);
                lineNo++;
            }

            int end = layout.getLineEnd(i);
            if (hasLineBreak(text, start, end)) {
                String lineString = text.substring(start, end);
                drawLineBreak(canvas, i, lineString);
            }
            start = end;
        }
        super.onDraw(canvas);
    }

    // ①その行の直前に改行が含まれているか
    private boolean isNewLine(String text, int charStart) {
        if (charStart == 0) {
            return true;
        }
        String lineString = text.substring(charStart - 1, charStart);
        return lineString.indexOf(LF) >= 0;
    }

    private void drawLineNumber(Canvas canvas, Integer number, Integer lineNo) {
        Layout layout = this.getLayout();
        Paint textPaint = this.getPaint();

        // ②行のY座標を取得
        int top = this.getPaddingTop() + layout.getLineBottom(number);

        // ③行の先頭にテキストを描画
        canvas.drawText(lineNo.toString(), 0, top, textPaint);
    }

}

詳説

getLineCount

getLineCountはビューの見た目上の行数を取得します。
折り返しによる行数も含まれるので注意が必要です。

行範囲を取得

layout.getLineEnd(layout.getLineStartでも)で行範囲を取得し、直前に改行コードが含まれている場合は新しい行として検知します。

描画する

layout.getLineBottom(lineNumber)で行の位置を取得し、canvas.drawTextを利用し行番号を描画します。

応用

このように行末位置を取得することで改行文字を表示することも可能です。

    @Override
    protected void onDraw(Canvas canvas) {
        ...
        for (int i = 0; i < displayLineCount; i++) {
            ...
            int end = layout.getLineEnd(i);
            if (hasLineBreak(text, start, end)) {
                String lineString = text.substring(start, end);
                drawLineBreak(canvas, i, lineString);
            }
        }
        ...
    }
    private boolean hasLineBreak(String text, int charStart, int charEnd) {
        String lineString = text.substring(charStart, charEnd);
        return lineString.indexOf(LF) >= 0;
    }
    private void drawLineBreak(Canvas canvas, Integer lineNumber, String lineString) {
        Layout layout = this.getLayout();
        Paint textPaint = this.getPaint();
        float measure = textPaint.measureText(lineString, 0, lineString.length());
        measure += this.getPaddingLeft();
        int top = this.getPaddingTop() + layout.getLineBottom(lineNumber);
        canvas.drawText("⏎", measure, top, textPaint);
    }

Screenshot_1513043190.png

daponta
dena_coltd
    Delight and Impact the World
https://dena.com/jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away