今回はスクロールの制御を行う。スクロールの制御は何回かにわけてエントリを書く予定。まずはスクロール位置の制限をかける。スクロール位置を0以下にしないことと、テキストが表示されないところまでいったら、それ以上スクロールさせないようにする。テキストの変更があったらスクロールの限界の位置もかわるため、テキストが変更されたことを通知する必要がある。まだ編集機能は存在しないが、今のうちに通知機能を作っておく。そのためにまずDocumentChangeHandlerというインタフェースを作った。documentChangedとchangeLineがテキストが変更されたときに呼び出されるメソッドとなる。channgeLineは変更が及んだ全ての行番号を引数で受け取る形となっている。
package text.edit.document; public interface DocumentChangeHandler { void documentChanged(int row, int column, String text); void changeLine(int[] lines); }
次に変更するのはTextDocumentクラス。まずDocumantChangeHandlerのリストを持たせた。addDocumentChangeHandlerでハンドラを追加してfire〜で全てのハンドラのそれぞれのメソッドっを呼び出す。今のところsetTextでfire〜が呼び出される
//インスタンス変数 private final List<StringBuilder> textList; private final List<DocumentChangeHandler> handlers; //コンストラクタ public TextDocument(){ textList = new ArrayList<StringBuilder>(); textList.add(new StringBuilder()); handlers = new ArrayList<DocumentChangeHandler>(); } //ハンドラを追加する public void addDocumentChangeHandler(DocumentChangeHandler handler){ handlers.add(handler); } //全てのハンドラのdocumentChangedを呼び出す public void fireDocumentChanged(int row, int column, String text){ for(DocumentChangeHandler handler : handlers){ handler.documentChanged(row, column, text); } } //全てのハンドラのchangeLineを呼び出す public void fireChangeLine(int[] lines){ for(DocumentChangeHandler handler : handlers){ handler.changeLine(lines); } } public void setText(String text){ for(char c : text.toCharArray()){ append(c); } int[] lines = new int[getLineCount()]; for(int i = 0; i < lines.length; i++){ lines[i] = i; } fireDocumentChanged(0, 0, text); fireChangeLine(lines); }
最後にEditorView。まずは追加したインスタンス変数から。スクロール位置の限界やブレを無視するための定数を追加
//スクロールできる限界の位置 private int maxScrollX; private int maxScrollY; //measureText用 private TextPaint paint; //タッチイベントによるスクロール時のブレを無視するための閾値 private static final int IGNORE_SCROLL = 3;
次にコンストラクタと今回追加したinitScroll。コンストラクタではinitScrollを呼び出す部分を追加。initScrollはスクロールに関わる変数の初期化を行っている
public EditorView(Context context){ super(context); fontSize = 16; document = new TextDocument(); document.addDocumentChangeHandler(this); initColorMap(); initViewInfo(); initRuler(); initScroll(); setBackgroundColor(Color.WHITE); } private void initScroll(){ maxScrollX =0; maxScrollY = 0; paint = new TextPaint(); paint.setTextSize(fontSize); }
次にDocumentChangeHandlerの実装。クラスの宣言部分と、インタフェースで宣言されたメソッドの実装部分。documentChangedでmaxScrollYを設定して、changeLineでmaxScrollXを設定している。
public class EditorView extends View implements DocumentChangeHandler{
public void documentChanged(int row, int column, String text){ int rowCount = document.getLineCount(); maxScrollY = rowCount * fontSize; } public void changeLine(int[] lines){ int start = lines[0]; int end = lines[lines.length - 1]; for(int row = start; row < end; row++){ String line = document.getLine(row); int lineWidth = (int)paint.measureText(line); if(lineWidth > maxScrollX){ maxScrollX = lineWidth; } } }
最後にタッチイベント関係。スクロールのブレ対応や、スクロールの位置が最小値と最大値の間だけで行えるように変更
@Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()){ case MotionEvent.ACTION_DOWN: touchX = (int)event.getX(); touchY = (int)event.getY(); break; case MotionEvent.ACTION_MOVE: int currentX = (int)event.getX(); int currentY = (int)event.getY(); touchScroll(currentX, currentY); touchX = currentX; touchY = currentY; invalidate(); break; } return true; } private void touchScroll(int currentX, int currentY){ int scrollX = viewInfo.getInt(ConstName.SCROLL_X); int scrollY = viewInfo.getInt(ConstName.SCROLL_Y); //タッチイベントが発生した位置の差を求める int dx = (currentX - touchX); int dy = (currentY - touchY); //差の絶対値を求める int adx = Math.abs(dx); int ady = Math.abs(dy); //ブレ対応、xとyの差が小さいほうの絶対値が閾値以下ならブレとみなす if(adx < ady && adx < EditorView.IGNORE_SCROLL){ dx = 0; }else if(ady < adx && ady < EditorView.IGNORE_SCROLL){ dy = 0; }else if(adx == ady && adx < EditorView.IGNORE_SCROLL){ //両方向に僅かなスクロールの場合は水平方向をブレとみなす dx = 0; } scrollX -= dx; scrollY -= dy; //スクロール位置が0以下にならないようにする scrollX = scrollX < 0 ? 0 : scrollX; scrollY = scrollY < 0 ? 0 : scrollY; //スクロール位置が最大値を超えないようにする scrollX = scrollX < maxScrollX ? scrollX : maxScrollX; scrollY = scrollY < maxScrollY ? scrollY : maxScrollY; viewInfo.put(ConstName.SCROLL_X, scrollX); viewInfo.put(ConstName.SCROLL_Y, scrollY); }