AdventCalendar Android その2 の12日目の記事です
テキストエディタを作るには
Androidでテキスト入力を実装するにはEditText
を使うことになるかと思います。
テキストエディタを作るにはEditText
をマルチラインにし、場合によってはSpannableStringBuilder
を駆使してリッチな機能を盛り込んでいきます。
今回は機能面ではなく、このようにエディタの行数をどうやって表示するかを解説します。
実現方法
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);
}