今回は横スクロールを追加した。横スクロールを追加するにあたって、Ruler、RowRuler、EditorViewを変更した。RowRulerを変更したのは、Canvasのtranslateで描画の位置をづらす方法だと、文字列を描画する位置がマイナスにできないのか、描画の切れ目が不自然になってしまったためだ。まずRulerインタフェースを変更。getWidthとsetFontSizeを追加。描画時にどれくらい幅が必要になるのか取得するためのgetWidthと、幅を求めるためのフォントサイズを設定するsetFontSizeを追加した。
package text.edit.view; import android.graphics.Canvas; import android.text.TextPaint; import text.edit.info.ViewInfo; public interface Ruler { int getWidth(); void setFontSize(int fontSize); void drawRuler(Canvas canvas, ViewInfo viewInfo, TextPaint paint); }
次にRowRuler。フォントサイズとルーラの幅を保持する変数を追加した。あと、Rulerに追加したメソッドの実装と、translateメソッドを取り除いた。
package text.edit.view; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Canvas; import android.text.TextPaint; import text.edit.info.ConstName; import text.edit.info.ViewInfo; public class RowRuler implements Ruler{ private static final int DEFAULT_PLACE = 5; private int place; private int fontSize; private int width; public RowRuler(){ this(RowRuler.DEFAULT_PLACE); } public RowRuler(int place){ this.place = place; } public void setFontSize(int fontSize){ this.fontSize = fontSize; width = (fontSize * (place + 1)) / 2; } public int getWidth(){ return width; } public void drawRuler(Canvas canvas, ViewInfo viewInfo, TextPaint paint){ int height = viewInfo.getInt(ConstName.EDITOR_HEIGHT); int srcColor = paint.getColor(); Paint.Style srcStyle = paint.getStyle(); drawBackground(canvas, width, height, paint); drawLineNumber(canvas, width, height, viewInfo, paint); paint.setColor(srcColor); paint.setStyle(srcStyle); } //背景描画 private void drawBackground(Canvas canvas, int width, int height, TextPaint paint){ int srcColor = paint.getColor(); Paint.Style srcStyle = paint.getStyle(); paint.setStyle(Paint.Style.FILL); paint.setColor(0xFFFFCC00); canvas.drawRect(0, 0, width, height, paint); //このメソッドが呼ばれる前のスタイルと色に戻す paint.setStyle(srcStyle); paint.setColor(srcColor); } //行数描画 private void drawLineNumber(Canvas canvas, int width, int height, ViewInfo viewInfo, TextPaint paint){ int srcColor = paint.getColor(); int scrollY = viewInfo.getInt(ConstName.SCROLL_Y); paint.setColor(Color.BLACK); int top = scrollY; int bottom = height + scrollY + fontSize; int row = (top / fontSize) - 1; int drawY = (fontSize - (scrollY % fontSize)) - fontSize; for(int y = top; y <= bottom; y += fontSize){ String rowNumber = String.format("%0" + place + "d", ++row); int x = alignRight(rowNumber, width, paint); canvas.drawText(rowNumber, x, drawY, paint); drawY += fontSize; } //このメソッドが呼ばれる前の色に戻す paint.setColor(srcColor); } //渡された文字列が幅widthの領域の中で右揃えになるようなx座標を返す private int alignRight(String text, int width, TextPaint paint){ return width - (int)paint.measureText(text, 0, text.length()); } }
最後にEditorView。まずonDrawを変更した。drawTextとdrawRulerの順番を変更。これはテキストを描画したあとに、ルーラで上書きして、描画の切れ目の不自然さを消すためである。
@Override protected void onDraw(Canvas canvas){ viewInfo.put(ConstName.EDITOR_WIDTH, getWidth()); viewInfo.put(ConstName.EDITOR_HEIGHT, getHeight()); TextPaint paint = new TextPaint(); paint.setTextSize(fontSize); drawText(canvas, document.getText(), paint); rowRuler.drawRuler(canvas, viewInfo, paint); }
次にdrawTextメソッド。描画を始めるX座標を保持するようにした
//テキストを描画する private void drawText(Canvas canvas, String text, TextPaint paint){ String token; TextTokenizer tokenizer = new JavaTokenizer(text.toCharArray()); //描画を始めるX座標を設定。改行の度に元の位置に戻るためstartXに保持する int startX = rowRuler.getWidth() - viewInfo.getInt(ConstName.SCROLL_X); int drawX = startX; //最初のY座標は垂直スクロール位置を引いた場所 int drawY = fontSize - viewInfo.getInt(ConstName.SCROLL_Y); while((token = tokenizer.getNextToken()) != null){ if(token.equals("\n")){ drawX = startX; drawY += fontSize; }else{ drawX = drawString(canvas, token, drawX, drawY, paint); } } }
次にdrawString。前回までは文字列を描画するかはここで決めていたが、描画するかどうかを文字単位に変更したため、その部分を消している
//文字列を描画して次の文字列を描画する位置を返す private int drawString(Canvas canvas, String text, int drawX, int drawY, TextPaint paint){ int srcColor = paint.getColor(); int height = getHeight(); //渡された文字列がキーワードに登録されてるなら //関連付けられた色で描画する if(colorMap.containsKey(text)){ paint.setColor(colorMap.get(text)); }else{ paint.setColor(Color.BLACK); } for(char c : text.toCharArray()){ drawX = drawCharacter(canvas, c, drawX, drawY, paint); } paint.setColor(srcColor); return drawX; }
次にdrawCharacter。drawStringでやっていた、描画するかどうかの処理をこっちに持ってきた。描画するかどうかの判定はisDrawCharacterに任せている。
//一文字描画して次の文字の描画位置を返す private int drawCharacter(Canvas canvas, char c, int drawX, int drawY, TextPaint paint){ String drawChar = Character.toString(c); int charWidth = (int)paint.measureText(drawChar, 0, 1); //テキストを描画する場合、この文字を描画する if(isDrawCharacter(drawX, drawY)){ canvas.drawText(drawChar, 0, 1, drawX, drawY, paint); } drawX += charWidth; return drawX; }
最後にisDrawCharacter。文字を描画するかどうかを返す。結構単純なメソッド
public boolean isDrawCharacter(int drawX, int drawY){ int right = getWidth(); int bottom = getHeight(); if(0 <= drawY && drawY <= bottom){ if(0 <= drawX && drawX <= right){ return true; } } return false; }
次回は、スクロールの仕方を変更してみるか