マイペースなプログラミング日記

DTMやプログラミングにお熱なd-kamiがマイペースに書くブログ

簡易テキストエディタ作成 その6

今回は横スクロールを追加した。横スクロールを追加するにあたって、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;
}

次回は、スクロールの仕方を変更してみるか