Java Drawing DrawTop

Language

JP  US  UK

 

行の折り返し

 H. Jyounishi, Tokyo Japan
 

Frame (Index), No frame                 version:0.3(latest)  

要旨: 属性つき文字列を扱うテキストエディタにはテキストボックス内のフォント属性を設定・操作する機能、 属性付き文字列をテキストボックスの境界で折り返して行に割り当てる機能が必要である。

使用する主なJava SEのクラス:java.awt.event.MouseListener, MouseMotionListener, MouseEvent

このページで>説明するクラス: LineBreaker, Line, StringTokenizerEx, CaretPosition


1. LineBreaker 戻る=>page top
1.1 概要

java.awt.font.LineBreakMeasurerを使って、属性つき文字列を表示領域の幅に収まるよう複数行に割り付ける。 LineBreakMeasurerは"\n" (改行)があるとうまく処理できないので、"\n" (改行)処理専用のメソッドを用意している。
行のテキスト表現はjava.awt.font.TextLayout。複数行になるのでTextLayoutの配列で表す。 また各行が、画面のどこに表示するかを示す位置情報をPoint2D[]の配列に格納する。



: テキストインデックスとLineBreaker実行後のインデックス
つぎの変数で表す。
◦ textIndex - テキストインデックス。
◦ lineindex - LineBreaker実行後に決まる行インデックス。
◦ columnIndex - LineBreaker実行後に決まる行内のテキストインデックス。
: 改行の扱い

1行目の最後は改行が入っている。すなわち、textLayout[0]内のテキストは、 ”MILITARY Jokes\n”。ここでもう一回改行を押すと、textLayout[1]には”\n”だけが入ることになる。

図(a) TextLayoutオブジェクト

図(b) TextLayoutのパラメータ>

図 1.1 LineBreaker


1.2 API
public class LineBreaker
フィールド

説明

textIterator

private AttributedCharacterIterator textIterator

表示テキスト

textBox private TextBox textBox
このオブジェクトを使うTextBoxオブジェクト

textArea

private Rectangle2D textArea

テキスト表示範囲

textAlign

private int textAlign

テキストの水平方向配置。左寄り/中央/右寄りで整数値は0/1/2。

lineSpace

private int lineSpace

行間隔

lineList

private ArrayList lineList

Lineオブジェクトを格納するリスト。

Lineオブジェクトに各行のテキスト情報、TextLayout情報、TextLayoutの画面位置情報などテキストの割付に関する全ての情報を格納する。

widthLimit

private int widthLimit

textAreaの幅が0のとき、文字の折り返し幅をこの値にする。


メソッド

説明

コンストラクタ public LineBreaker(){}

setData

public void setData(AttributedCharacterIterator textIterator, TextBox textBox)

・textBoxをフィールド変数に設定する。

また textBoxのデータを参照して、フィールド変数textArea, textAlign, lineSpaceに値を設定する。

・textIterator がnullでなければ、createMultipleLinesメソッドでLineオブジェクトを作成しArrayListに出力する。

createMultipleLines

private void createMultipleLines()

createLineBreakPositions メソッドを呼ぶ。

 行の分割位置を出力。分割位置は先頭からの文字数で表す。

・各行のTextLayoutを格納するtextLayouts (TextLayout[])を作成。

AttributedStringUtil.getTextLayoutStringメソッドでAttributedStringを取得し、TextLayout[])を作成する。

: TextLayoutオブジェクトに格納された"\n" (Line feed)
"\n" (Line feed)をTextLayoutに格納してTextLayout.drawメソッドで表示(TextBox.drawText)しても、何も表示されずテキスト選択(TextBox.setCaretPositionOrSelection)もできない。このため"\n" (Line feed)を" " (スペース)または"↲"で置き換えて表示する。
AttributedStringUtil, CommittedTextContainerなど属性つき文字列を処理するクラスでは、"\n" (Line feed)をそのまま扱い表示だけ他の文字で置き換えるようにするため、このメソッドでで処理する。

・各行のStringデータをlayoutStrings[]に格納。

createTextLayoutPositionsメソッドを呼ぶ。

 各TextLayoutの表示位置を配列textLayoutPositionsに出力。

 配列textLayoutPositionsのX座標はTextLayoutの左端、Y座標はTextlayoutのbaseline位置(上図参照)。

Lineオブジェクト作成

breakPositions, textLayoutPositions, textLayouts, layoutStringsから各行のLineオブジェクトを作成してフィールド変数LineListに追加。

createLineBreakPositions

private int[] createLineBreakPositions(AttributedCharacterIterator textIterator, float formatWidth)
行の分割位置を文字数で出力。出力データの配列数は「作成される行数+1」でbreakPositions[0]=0。

・StringTokenizerEx:Line Feed(\n)で文字列を分割。

・Line Feed(\n)で分割された文字列に対しLineBreakMeasurerを適用して複数行に分割。

createTextLayout
Positions

private Point2D[] createTextLayoutPositions(TextLayout[] layout)

・TextAreaの左上隅点から配置する。

・TextLayoutのAscent, Descentを取得してY値を更新

(注)X位置はTextLayoutの左端、Y位置はTextlayoutのbaseline位置。

createTextLayoutBounds

private Rectangle2D[] createTextLayoutBounds(TextLayout[] layout, Point2D[] textLayoutPositions)

TextLayoutを囲むRectangle2Dを作成

getString

public String getString(CharacterIterator iterator, int begin, int end)

フィールド変数textIteratorから[begin, end]区間のStringデータを返す。

getTextLayouts

public TextLayout[] getTextLayouts()

各行に対応するTextLayoutを配列で返す。

TextLayoutの数が0(表示テキストがない)の場合、長さ0の配列またはnullを返す。

getTextLayout

public TextLayout getTextLayout(int lineIndex)

lineIndexで指定された行のTextLayoutを返す。

TextLayoutがない場合、またはindex<0の場合、nullを返す。

getTextLayoutStrings

public String[] getTextLayoutStrings()

各TextLayoutに含まれるテキストをStringの配列で返す。

表示テキストがない場合、長さ0の配列またはnullを返す。

getTextLayoutString

public String getTextLayoutString(int lineIndex)

lineIndex番目のTextLayoutに含まれるテキストをStringで返す。

lineIndex<0の場合表示テキストがない場合はnullを返す。

getPreceedingString

private String getPreceedingString(int lineIndex, int columnIndex)

lineIndexが指すTextLayout内のcolumnIndex番目より手前のテキストを返す。
See (lineIndex, columnIndex) => 図5.1

getPreceedingCharacter

private String getPreceedingCharacter(int lineIndex, int columnIndex)

lineIndexが指すTextLayout内のcolumnIndex番目の手前の1文字を返す。

getTextLayoutPositions

public Point2D[] getTextLayoutPositions()

各行のTextLayoutの画面上での位置を配列で返す。

X位置はTextLayoutの左端、Y位置はTextlayoutのbaseline位置。

表示テキストがない場合、長さ0の配列またはnullを返す。
See =>図5.1 TextLayoutのパラメータ

getBounds

public Rectangle2D[] getBounds()

各行のTextlayoutを囲む矩形を配列で返す。

TextlayoutのgetBoundsメソッドで取り出す矩形のx,yは0であるが、ここで返す矩形はcreateTextLayoutPositionsメソッドで決めた、画面上での位置を反映している。

表示テキストがない場合、長さ0の配列またはnullを返す。

getBounds

public Rectangle2D getBounds(int lineIndex)

lineIndexで指定したTextLayoutを囲む矩形を返す。

lineIndex<0の場合、TextLayoutがない場合はnullを返す。

getCharacterCount

public int getCharacterCount()

フィールド変数textIteratorの文字数を返す。

getCharacterCount

public int getCharacterCount(int lineIndex)

lineIndexで指定したTextLayout内の文字数を返す。

getCharacterCount

public int getCharacterCount(int lineIndex, int columnIndex)

テキストの先頭からlineIndexのTextLayout内のcolumnIndexまでの文字数を返す。

See (lineIndex, columnIndex) => 図5.1

getLineColumnIndices public int[] getLineColumnIndices(int textIndex)
textIndexを変換してlineIndexとcolumnIndexを返す。int配列の戻り値indicesは、indices[0]がlineIndex、indices[1]がcolumnIndexである。
getTextIndex public int getTextIndex(int lineIndex, int columnIndex)
line index (lineIndex) とcolumn index (columnIndex)を変換してテキストインデックス(textIndex)を返す。
See (lineIndex, columnIndex) => 図5.1

updateCaretPosition

public CaretPosition updateCaretPosition(int textIndex)

廃止。CaretPosition.updateCaretPositionを使用する。

引数:
textIndex - テキストインデックス.

戻り値:
CaretPosition オブジェクト.

Processing:

このメソッドは上記のgetLineColumnIndices メソッドを使えば簡単に実現できる。

getCaretPosition

public CaretPosition getCaretPosition(int lineIndex, columnIndex)

廃止。CaretPosition.getCaretPositionを使用する

引数:

lineIndex - 行数で、getTextLayoutsで取り出すTextLayoutの配列の番号。

columnIndex - lineIndexが指すTextLayout内での文字位置。

処理:

lineIndex、columnIndexから、その手前の文字数をカウントして動機の取れたCretPositionを返す。

TextBox. getCaretPositionAtMouseから呼ばれる。

参照=>CaretPositionの役割

(注) columnIndex>0で直前の文字が\n(改行)ならば\nの前のキャレット位置を返す。

getOffsetCaretPosition

public CaretPosition getOffsetCaretPosition(CaretPosition caretPosition, int offset)

廃止。CaretPosition.columnOffsetを使用する

引数:
caretPosition - 現在のテキストカーソル(Caret)に位置を表す CaretPositionオブジェクト。
offset - 文字数でのオフセット量。マイナス値を指定してもよい。

戻り値:
オフセット位置のCaretPositionオブジェクト。

処理:

引数のcaretPositionの位置から、 offsetの長さのテキストを追加した場合に、追加テキストの後の位置を新しいCaretPositionとして返す。下の同名メソッドを使う。

offsetはプラス値、マイナス値のどちらでも良いが、オフセットした位置がLineBreakerが保持するテキストの範囲を超えることはできない。

このメソッドはTextBox.drawCaretにおいてcomposedText(未確定テキスト)がある場合にcaretの位置を求めるのに使われる。

またTextBox.keyPressedメソッドでHome、Endキーを押してキャレットを左右に移動するために使用する。

上下に移動するにはgetLineOffsetCaretPositionメソッドを使う。

getOffsetCaretPosition

public CaretPosition getOffsetCaretPosition(int lineIndex, int columnIndex, int offset)

廃止。CaretPosition.offsetCaretPositionを使用する

引数:
lineIndex - 行インデックス (lineIndex=0,1, 2,....)
columnIndex - カラムインデックス
(columnIndex=1, 2,....)
offset - 文字数でのオフセット量。マイナス値を指定してもよい。

戻り値:
オフセット位置のCaretPositionオブジェクト。

処理:
TextLayout番号、TextLayout内位置を指定しこれに文字数offset(マイナス値も可)を加えたCaretPositionを返す。

updateCaretPositionメソッドで計算する。

getLineOffsetCaret
Position

public CaretPosition getLineOffsetCaretPosition(CaretPosition caretPosition, int lineOffset)

廃止。CaretPosition.lineOffsetを使用する

引数:
caretPosition - 現在のテキストカーソル(Caret)に位置を表す CaretPositionオブジェクト。
lineOffset - 行数でのオフセット量。マイナス値を指定してもよい。

戻り値:
オフセット位置のCaretPositionオブジェクト。

処理:

引数のcaretPositionの位置から、上または下へ一行ずらしたCaretPositionを返す。

lineOffset<0の時は上の行へ、lineOffset>0は下の行へ移す。移った行の文字数が少ない場合、キャレットの位置は、その行の一番後ろになる。但し、最後の文字が\n(改行)の場合は、\nの前の位置とする。

このメソッドはTextBox.keyPressedメソッドでUp、Downキーを押してキャレットを上下に移動するために使用する。

getCaretRectangle

public Rectangle getCaretRectangle(CaretPosition caretPosition, int offset)
引数:
caretPosition - 現在のキャレット(Caret、テキストカーソル)の位置を表す CaretPositionオブジェクト。
offset - 文字数でのオフセット値。マイナス値を指定してもよい。
戻り値:
オフセットを加えた位置に表示するキャレット図形(Rectangle)を返す。
処理:
このメソッドは、TextBoxのgetTextLocationdrawCaretから呼ばれる。 オフセット値は、composedText(未確定テキスト)が存在し、その後ろにキャレット(テキストカーソル)に 表示するときに使われる。

(a)textIteratorがnullのとき、以下の処理を実行。

このケースはTextBoxにテキストが存在しない場合、つまり新規のTextBoxオブジェクトを作成した場合や、 テキストがすべて削除された場合に起きる(下図ケース(a))。 この場合、参考となる属性付き文字列(Java Class AttributedString)がないので、特にキャレット図形の高さが決まらない。 そこで空白の属性付き文字列と、それを格納する一時的なTextLayoutを作成し、 そのTextLayoutからascent、descent(Figure 1.1 (b))を取得してキャレット図形の高さを決める。 結論としてキャレット図形の左上隅点は、textAreaの左上隅点、 幅は0、高さは(ascent+descent)となる。


(b)引数caretPositionがテキストのある行の内側を指している場合、次の処理を行う。

これは未確定テキスト確定テキストの 少なくとも一方が表示されているケースである(ケース(b))。 この場合、キャレット図形をTextLayoutクラス(Java API)のgetCaretShapesメソッドを使い、次のように取得する。
Shape[] carets=textLayout.getCaretShapes(index);
そしてキャレット図形(矩形)を戻り値carets[0]を参照して作成する。


(c)引数caretPositionがテキストのある行の終端を指している場合、次の処理を行う。

この場合、キャレットを次の行の先頭に表示する(ケース(c))。
(b)と同様な方法でキャレット図形を取得し、その位置を次の行の先頭に変更する。 これはascent、descenlineSpace値から計算できる。


ケース(a) ケース(b) ケース(c)
図 キャレットの矩形(赤マーク)

printTextLayoutPositions

public void printTextLayoutPositions()

各行のTextLayoutの位置をプリントする。

printTextLayouts

public void printTextLayouts()

各行のTextLayoutの文字数、テキストなどをプリントする。

printBounds

public void printBounds()

各行のTextLayoutを囲む矩形をプリントする。

rangeCheck private void rangeCheck(int index, int indexMax) throws RangeException
index>indexMaxならばRangeExceptionをスローする。
次のrangeErrorでエラーを検知したときに、e.printStackTrace()を呼び出すのが目的。
textRangeError private boolean textRangeError(int textIndex, String errMessage)
textIndex>(textIteratorの文字数)ならばエラーメッセージとStackTraceを出力する。
lineRangeError private boolean lineRangeError(int lineIndex, String errMessage)
lineIndex>this.lineList.size()ならばエラーメッセージとStackTraceを出力する。
lineColumnRangeError private boolean lineColumnRangeError(int lineIndex, int columnIndex, String errMessage)
lineIndex>this.lineList.size()またはcolumnIndex>columMaxならばエラーメッセージとStackTraceを出力する。
: columnIndexはlineIndexが指す行の文字インデックス。columMaxはその行の文字数。


1.3 RangeException - LineBreakerの内部クラス
コード

class RangeException extends Exception {
RangeException() {}
public String toString() {
return "range Exception";
}
}



2. Lineクラス 戻る=>page top
public class Line
LineBreakerで作成される各行を表すクラス。

フィールド

説明

startCharPosition

int startCharPosition

LineBreakerが保持する表示テキストに部分区間がこの行に割り付けられている。

部分区間の表示テキストでの開始位置。

endCharPosition

int endCharPosition

部分区間の表示テキストでの終了位置。

textLayoutPosition

Point2D textLayoutPosition

TextLayoutの左端ベースライン位置。

textLayout

TextLayout textLayout

この行のTextLayout。

textLayoutString

String textLayoutString

この行のテキスト。


メソッド

説明

コンストラクタ

public Line(int startCharPosition, int endCharPosition, Point2D textLayoutPosition, TextLayout textLayout, String textLayoutString)

引数をフィールド変数に設定したオブジェクトを作成する。

getStartCharPosition

public int getStartCharPosition()

フィールド変数startCharPositionを返す。

getEndCharPosition

public int getEndCharPosition()

フィールド変数endCharPositionを返す。

getTextLayoutPosition

public Point2D getTextLayoutPosition()

フィールド変数textLayoutPositionを返す。

getTextLayout

public TextLayout getTextLayout()

フィールド変数textLayoutを返す。

getTextLayoutString

public String getTextLayoutString()

フィールド変数textLayoutStringを返す。



3. StringTokenizerExクラス 戻る=>page top
public class StringTokenizerEx

java.util.StringTokenizerと同様な機能を果たすクラス。 区切り文字に\n(改行)を指定すると、\nの後ろで文字列を分割する。
LineBreakerで使われる。

フィールド

説明

str

String str:文字列。

delim

String delim:区切り文字。

index

int index:文字列のindex。


メソッド

説明

コンストラクタ

public StringTokenizerEx(String str, String delim)

文字列と区切り文字を指定してオブジェクトを作成する。

hasMoreTokens

public boolean hasMoreTokens()
トークンがまだあるかどうかを返す。

nextToken

public String nextToken()

次のトークンを返す。



4. CaretPositionクラス 戻る=>page top
4.1 CaretPositionの使い方

(1)テキストカーソル(キャレット)の位置

テキストカーソルの位置は次の2通りの形式で表す。 See => 図5.1

(a)表示文字列での位置(一次元的な位置)

1文字目の前のキャレット位置を0、後を1とし文字に番号をふる。この一次元的な番号付けでのキャレット位置をtextIndexと書く。

(b)行番号とその行での位置(二次元的な位置)

行番号をlineIndexで表す(lineIndex=0,1,2...)。行内でのテキスト位置をcolumIndexで表す(columnIndex=0,1,2...)。1文字目の前の位置が0。

(2)textIndexと(lineIndex, columnIndex)の間の変換
(lineIndex, columnIndex)とtextIndexは、TextLayoutの先頭位置と末尾の位置を除いては1:1に対応するので変換できる。 但し、この変換に必要なTextLayoutの配列のデータを保持しているのは LineBreakerオブジェクトなので、 変換は常にLineBreakerを参照して行う。

4.2 API
public class CaretPosition 

フィールド

説明

lineIndex

int lineIndex

TexyLayout配列の要素番号(-1は無効を表す)。

columnIndex

int columIndex

TexyLayoutでのキャレット位置(-1は無効を表す)。

textIndex

int textIndex

文字列でのキャレット位置(-1は無効を表す)。

lineFeedOption boolean lineFeedOption
lineFeedOption=trueならば、\n(改行)の後のテキストカーソル(キャレット)位置は、次の行の先頭に位置ずける。

String str="";
if(lineFeedOption&&columnIndex>0){
str=lineBreaker.getPreceedingCharacter(lineIndex, columnIndex);
if(str.equals("\n")){
lineIndex++;
columnIndex=0;
}
}

: lineFeedOption
通常のテキストカーソル(キャレット)の処理ではlineFeedOption=trueを指定する。これはEnterキーを押すと改行され、次の行の先頭にテキストカーソルが表示される通常のワードプロセッサーと同様である。
またテキストの選択範囲の先頭文字の前と最後文字の後に2個のCaretPositionオブジェクトを使う場合には、lineFeedOption=falseを指定する。

See => TextBox.selStart, TextBox.selEnd


メソッド

説明

コンストラクタ

public CaretPosition(int lineIndex, int columnIndex)

new CaretPosition(-1, -1), new CaretPosition(0, 0)など自明なCaretPositionオブジェクトを作成するのに使う。
(textindex, lineIndex, lineColumnIndex)を整合性をもって設定するためには、staticメソッドCaretPosition.getCaretPositionを使う。

コンストラクタ

public CaretPosition(int lineIndex, int columnIndex, boolean lineFeedOption)

同上

コンストラクタ

public CaretPosition(int lineIndex, int columnIndex, int textIndex )

同上

コンストラクタ

public CaretPosition(int lineIndex, int columnIndex, int textIndex, boolean lineFeedOption)

同上

isValid public boolean isValid()
lineindex>=0, columnIndex>=0, textIndex>=0ならばtrueを返す。

getLineIndex

public int getLineIndex()

lineIndexを返す。

getColumnIndex

public int getColumnIndex()

positionInLineを返す。

getTextIndex

public int getTextIndex()

positionInTextを返す。

resetPosition public void resetPosition()
すべてのフィールド変数に0をセットする。
updateCaretPosition public void updateCaretPosition(int newTextIndex, LineBreaker lineBreaker, String callFrom)

引数:
newTextIndex - テキストインデックス.
lineBreaker - LineBreaker オブジェクト
callFrom - デバッグ用

処理:

このメソッドは、newTextIndexを与えて、フィールド変数lineIndex, columIndexを更新するもので、

LineBreaker.getLineColumnIndices メソッドを使う。

columnOffset public void columnOffset(int offset, LineBreaker lineBreaker)

引数:
offset - 水平方向オフセット量
lineBreaker - LineBreaker オブジェクト

処理:

このメソッドは矢印キーでテキストカーソルを左右に動かした場合にTextBox.keyPressedから呼ばれる。
下のoffsetCaretpositionをコールして処理する。

offsetCaretPosition private void offsetCaretPosition(int lineIndex, int columnIndex, int offset, LineBreaker lineBreaker)

引数:
lineIndex - 行インデックス
columIndex - カラムインデックス
offset - オフセット量
lineBreaker - LineBreaker オブジェクト

See => 図5.1, 5.1 CaretPositionの使い方 

処理:

このメソッドは上のcolumnOffsetメソッドと下のlineOffsetメソッドから呼ばれる。
テキスト先頭から(lineIndex, columIndex)までの文字数をLineBreaker.getCharacterCountメソッドでカウントし、オフセット分を加え新しいフィールド変数textIndexの値を計算し、updateCaretPositionメソッドで更新する。

lineOffset public void lineOffset(int lineOffset, LineBreaker lineBreaker)

引数:
lineOffset - 垂直方向オフセット量
lineBreaker - LineBreaker オブジェクト

処理:

このメソッドは矢印キーでテキストカーソルを上下に動かした場合にTextBox.keyPressedから呼ばれる。
上のoffsetCaretpositionをコールして処理する。

toString

public String toString()

キャレット位置の文字列表現を返す。

getCaretPosition
(static)
public static CaretPosition getCaretPosition(int textIndex, boolean lineFeedOption, LineBreaker lineBreaker, String callFrom)
引数:
textIndex - テキストインデックス.
lineFeedOption - 改行オプション(フィールド変数lineFeedOption参照)
lineBreaker - LineBreaker オブジェクト
callFrom - 呼び出し元のクラス、メソッド名称(デバッグ用)
戻り値:
CaretPosition - CaretPosition オブジェクト.

処理:

このメソッドは、textIndexからlineIndex, columIndexを計算して新しいCaretPositionオブジェクトを作成する。

LineBreaker.getLineColumnIndices メソッドを使う。

getCaretPosition
(static)
public static CaretPosition getCaretPosition(int lineIndex, int columnIndex, boolean lineFeedOption, LineBreaker lineBreaker, String callFrom)

引数:
lineIndex - 行インデックス
columIndex - カラムインデックス
lineFeedOption - 改行オプション(フィールド変数lineFeedOption参照)
lineBreaker - LineBreaker オブジェクト
callFrom - 呼び出し元のクラス、メソッド名称(デバッグ用)

See => 図5.1, 5.1 CaretPositionの使い方 

戻り値:
CaretPosition - CaretPosition オブジェクト.

処理:

このメソッドは、lineIndex, columIndexからtextIndexを計算して新しいCaretPositionオブジェクトを作成する。

LineBreaker.getTextIndex メソッドを使う。





Copyright (c) 2009-2013
All other trademarks are property of their respective owners.