From 59c1588e07b44e8bcb86a8fb65822202ea12ad96 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sat, 14 Sep 2024 12:10:11 +0800 Subject: [PATCH 01/13] add line numbers support --- .../java/org/billthefarmer/editor/Editor.java | 22 +- .../org/billthefarmer/editor/NewEditText.java | 240 ++++++++++++++++++ src/main/res/layout/edit.xml | 2 +- src/main/res/menu/main.xml | 5 + src/main/res/values-zh-rCN/strings.xml | 1 + src/main/res/values/strings.xml | 1 + 6 files changed, 265 insertions(+), 6 deletions(-) create mode 100644 src/main/java/org/billthefarmer/editor/NewEditText.java diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 77f5fee..8f42a16 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -423,7 +423,7 @@ public class Editor extends Activity private String path; private Uri content; private String match; - private EditText textView; + private NewEditText textView; private TextView customView; private MenuItem searchItem; private SearchView searchView; @@ -444,7 +444,8 @@ public class Editor extends Activity private boolean edit = false; private boolean view = false; - private boolean wrap = false; + private boolean wrap = true; + private boolean lineNumbers = false; private boolean suggest = true; private boolean changed = false; @@ -907,6 +908,7 @@ public boolean onPrepareOptionsMenu(Menu menu) menu.findItem(R.id.openLast).setChecked(last); menu.findItem(R.id.autoSave).setChecked(save); menu.findItem(R.id.wrap).setChecked(wrap); + menu.findItem(R.id.lineNumbers).setChecked(lineNumbers); menu.findItem(R.id.suggest).setChecked(suggest); menu.findItem(R.id.highlight).setChecked(highlight); @@ -1080,6 +1082,9 @@ public boolean onOptionsItemSelected(MenuItem item) case R.id.wrap: wrapClicked(item); break; + case R.id.lineNumbers: + lineNumbersClicked(item); + break; case R.id.suggest: suggestClicked(item); break; @@ -1815,8 +1820,7 @@ public void onPageFinished(WebView view, String url) } }); - String htmlDocument = - HTML_HEAD + Html.toHtml(textView.getText()) + HTML_TAIL; + String htmlDocument = HTML_HEAD + Html.toHtml(textView.getText()) + HTML_TAIL; webView.loadData(htmlDocument, TEXT_HTML, UTF_8); } @@ -1894,6 +1898,14 @@ private void wrapClicked(MenuItem item) recreate(this); } + // lineNumbersClicked + private void lineNumbersClicked(MenuItem item) + { + lineNumbers = !lineNumbers; + item.setChecked(lineNumbers); + textView.setLineNumbersEnabled(lineNumbers); + } + // suggestClicked private void suggestClicked(MenuItem item) { @@ -3562,7 +3574,7 @@ protected List doInBackground(String... params) File entry = new File(path); entries.add(entry); } - + // Check the entries for (File file : entries) { diff --git a/src/main/java/org/billthefarmer/editor/NewEditText.java b/src/main/java/org/billthefarmer/editor/NewEditText.java new file mode 100644 index 0000000..c2741bd --- /dev/null +++ b/src/main/java/org/billthefarmer/editor/NewEditText.java @@ -0,0 +1,240 @@ +package org.billthefarmer.editor; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.text.Editable; +import android.text.Layout; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.widget.EditText; + +public class NewEditText extends EditText { + private boolean isLineNumbersEnabled; + private final LineNumbersDrawer lineNumbersDrawer = new LineNumbersDrawer(this); + + public NewEditText(Context context) { + super(context); + } + + public NewEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NewEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + public boolean onPreDraw() { + lineNumbersDrawer.setTextSize(getTextSize()); + return super.onPreDraw(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (isLineNumbersEnabled) { + lineNumbersDrawer.draw(canvas); + } + } + + public void setLineNumbersEnabled(final boolean enable) { + if (enable ^ isLineNumbersEnabled) { + post(this::invalidate); + } + isLineNumbersEnabled = enable; + if (isLineNumbersEnabled) { + lineNumbersDrawer.startLineTracking(); + } else { + lineNumbersDrawer.reset(); + lineNumbersDrawer.stopLineTracking(); + } + } + + /** + * Count instances of a single char in a char sequence. + */ + public static int countChar(final CharSequence s, int start, int end, final char c) { + int count = 0; + for (int i = start; i < end; i++) { + if (s.charAt(i) == c) { + count++; + } + } + return count; + } + + static class LineNumbersDrawer { + + private final EditText _editor; + private final Paint _paint = new Paint(); + + private final int _defaultPaddingLeft; + private static final int LINE_NUMBER_PADDING_LEFT = 6; + private static final int LINE_NUMBER_PADDING_RIGHT = 12; + + private final Rect _visibleArea = new Rect(); + private final Rect _lineNumbersArea = new Rect(); + + private int _numberX; + private int _gutterX; + private int _maxNumber = 1; // to gauge gutter width + private int _maxNumberDigits; + private float _oldTextSize; + private final int[] _startLine = {0, 1}; // {line index, actual line number} + + private final TextWatcher _lineTrackingWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + _maxNumber -= countChar(s, start, start + count, '\n'); + _maxNumber -= countChar(s, start, start + count, '\n'); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + _maxNumber += countChar(s, start, start + count, '\n'); + } + + @Override + public void afterTextChanged(Editable editable) { + + } + }; + + public LineNumbersDrawer(final EditText editor) { + _editor = editor; + _paint.setColor(0xFF999999); + _paint.setTextAlign(Paint.Align.RIGHT); + _defaultPaddingLeft = editor.getPaddingLeft(); + } + + public void setTextSize(final float textSize) { + _paint.setTextSize(textSize); + } + + public boolean isTextSizeChanged() { + if (_paint.getTextSize() == _oldTextSize) { + return false; + } else { + _oldTextSize = _paint.getTextSize(); + return true; + } + } + + public boolean isMaxNumberDigitsChanged() { + final int oldDigits = _maxNumberDigits; + + if (_maxNumber < 10) { + _maxNumberDigits = 1; + } else if (_maxNumber < 100) { + _maxNumberDigits = 2; + } else if (_maxNumber < 1000) { + _maxNumberDigits = 3; + } else if (_maxNumber < 10000) { + _maxNumberDigits = 4; + } else { + _maxNumberDigits = 5; + } + return _maxNumberDigits != oldDigits; + } + + public boolean isOutOfLineNumbersArea() { + final int margin = (int) (_visibleArea.height() * 0.5f); + final int top = _visibleArea.top - margin; + final int bottom = _visibleArea.bottom + margin; + + if (top < _lineNumbersArea.top || bottom > _lineNumbersArea.bottom) { + // Reset line numbers area + // height of line numbers area = (1.5 + 1 + 1.5) * height of visible area + _lineNumbersArea.top = top - _visibleArea.height(); + _lineNumbersArea.bottom = bottom + _visibleArea.height(); + return true; + } else { + return false; + } + } + + public void startLineTracking() { + _editor.removeTextChangedListener(_lineTrackingWatcher); + _maxNumber = 1; + final CharSequence text = _editor.getText(); + if (text != null) { + _maxNumber += countChar(text, 0, text.length(), '\n'); + } + _editor.addTextChangedListener(_lineTrackingWatcher); + } + + public void stopLineTracking() { + _editor.removeTextChangedListener(_lineTrackingWatcher); + } + + /** + * Draw line numbers. + * + * @param canvas The canvas on which the line numbers will be drawn. + */ + public void draw(final Canvas canvas) { + if (!_editor.getLocalVisibleRect(_visibleArea)) { + return; + } + + final CharSequence text = _editor.getText(); + final Layout layout = _editor.getLayout(); + if (text == null || layout == null) { + return; + } + + // If text size or the max line number of digits changed, + // update the variables and reset padding + if (isTextSizeChanged() || isMaxNumberDigitsChanged()) { + _numberX = LINE_NUMBER_PADDING_LEFT + (int) _paint.measureText(String.valueOf(_maxNumber)); + _gutterX = _numberX + LINE_NUMBER_PADDING_RIGHT; + _editor.setPadding(_gutterX + 12, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom()); + } + + int i = _startLine[0], number = _startLine[1]; + // If current visible area is out of current line numbers area, + // iterate from the first line to recalculate the start line + if (isOutOfLineNumbersArea()) { + i = 0; + number = 1; + _startLine[0] = -1; + } + + // Draw border of the gutter + canvas.drawLine(_gutterX, _lineNumbersArea.top, _gutterX, _lineNumbersArea.bottom, _paint); + + // Draw line numbers + final int count = layout.getLineCount(); + final int offsetY = _editor.getPaddingTop(); + for (; i < count; i++) { + final int start = layout.getLineStart(i); + if (start == 0 || text.charAt(start - 1) == '\n') { + final int y = layout.getLineBaseline(i); + if (y > _lineNumbersArea.bottom) { + break; + } + if (y > _lineNumbersArea.top) { + if (_startLine[0] < 0) { + _startLine[0] = i; + _startLine[1] = number; + } + canvas.drawText(String.valueOf(number), _numberX, y + offsetY, _paint); + } + number++; + } + } + } + + /** + * Reset to the state without line numbers. + */ + public void reset() { + _editor.setPadding(_defaultPaddingLeft, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom()); + _maxNumberDigits = 0; + } + } +} diff --git a/src/main/res/layout/edit.xml b/src/main/res/layout/edit.xml index 81b08d7..73ba932 100644 --- a/src/main/res/layout/edit.xml +++ b/src/main/res/layout/edit.xml @@ -17,7 +17,7 @@ android:fillViewport="true" tools:ignore="LabelFor"> - + 最后打开 自动保存 自动换行 + 行号 建议 突出显示语法 主题 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 9d540e0..295ef51 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -30,6 +30,7 @@ Open last Auto save Word wrap + Line numbers Suggestions Highlight syntax Theme From e0e120bcb9dfeeded3ba68ca82305334f8bd78b3 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sat, 14 Sep 2024 13:31:49 +0800 Subject: [PATCH 02/13] improve Simplified Chinese translation --- src/main/res/values-zh-rCN/strings.xml | 137 +++++++++++++------------ 1 file changed, 70 insertions(+), 67 deletions(-) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index f2776cc..6cfe3b2 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -1,80 +1,83 @@ - 编辑器 + 编辑器 - - 打开文件 - 名字 - 路径 - 创造 - 编辑 - 查看 - 打开文件… - 保存 - 存储空间 - 打开最近文本 - 清除列表 - 查找… - 全局搜索… - 保存为… - 检测 - 转到… - 打印… - 查看 Markdown… - 选项 - 查看文件 - 最后打开 - 自动保存 - 自动换行 - 行号 - 建议 - 突出显示语法 - 主题 - - - 系统 - - 黑色的 - 复古 - 文本大小 - - - - 字体 - 等宽 - 变宽 - 无衬线 - 衬线 - 关于 + 新建文件 + 打开文件 + 名称 + 路径 + 创建 + 编辑 + 查看 + 打开 + 保存 + 存储 + 打开最近 + 清除列表 + 查找 + 查找全部 + 另存为 + 检测 + 转到 + 打印 + 查看 Markdown + 选项 + 查看文件 + 打开最后 + 自动保存 + 自动换行 + 行号 + 建议 + 语法高亮 + 主题 + 明亮 + 黑暗 + 系统 + 白色 + 黑色 + 怀旧 + 文本大小 + + + + 字体 + 等宽 + 成比例 + 无衬线 + 有衬线 + 关于 - %s\n\n - 编译时间 %s\n\n版权 \u00A9 2017 Bill Farmer\n\n中文(简体)译者Lu Chang (ludoux)\n\n开源协议GNU GPLv3 - + + %s\n\n + 编译时间 %s\n\n + Copyright \u00A9 2017 Bill Farmer\n\n + 简体中文译者\n + Lu Chang (ludoux)\n + Li Guanglin\n\n + 开源协议 GNU GPLv3 + - 太大了 %s - 选择一个文件 - 加载中… + 过大 %s + 选择 + 加载中 - - 你有未保存的更改。你想保存这些更改吗? - + + 有未保存的更改。是否保存更改? + - - 文件已被更改,你想重载它吗? - + + 文件已被更改,是否重新加载? + - - 文件已被更改,你想覆写它吗? + + 文件已被更改,是否覆盖写入? - OK - 重载 - 覆写 - 放弃更改 - 取消 + OK + 重载 + 覆写 + 丢弃 + 取消 From acb9ee815110fd663a5057207c7d3d6db7adf5d2 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sat, 14 Sep 2024 13:50:26 +0800 Subject: [PATCH 03/13] minor improvements --- .../java/org/billthefarmer/editor/Editor.java | 15 ++------------- src/main/res/layout/wrap.xml | 2 +- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 8f42a16..2ca25be 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -44,6 +44,7 @@ import android.print.PrintAttributes; import android.print.PrintDocumentAdapter; import android.print.PrintManager; +import android.support.v4.content.FileProvider; import android.text.Editable; import android.text.Html; import android.text.InputType; @@ -67,7 +68,6 @@ import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.webkit.WebView; @@ -81,12 +81,10 @@ import android.widget.SeekBar; import android.widget.TextView; -import android.support.v4.content.FileProvider; - import com.ibm.icu.text.CharsetDetector; import com.ibm.icu.text.CharsetMatch; -import org.commonmark.node.*; +import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; @@ -96,30 +94,21 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; import java.io.FileWriter; -import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; -import java.io.Reader; - import java.lang.ref.WeakReference; - import java.nio.charset.Charset; - import java.text.DateFormat; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; - import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/res/layout/wrap.xml b/src/main/res/layout/wrap.xml index 8205192..8d78dd0 100644 --- a/src/main/res/layout/wrap.xml +++ b/src/main/res/layout/wrap.xml @@ -11,7 +11,7 @@ android:fillViewport="true" tools:ignore="LabelFor"> - Date: Sat, 14 Sep 2024 16:50:19 +0800 Subject: [PATCH 04/13] support storing the state of line numbers option --- src/main/java/org/billthefarmer/editor/Editor.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 2ca25be..0d5b6e8 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -135,6 +135,7 @@ public class Editor extends Activity public final static String PREF_THEME = "pref_theme"; public final static String PREF_TYPE = "pref_type"; public final static String PREF_WRAP = "pref_wrap"; + public final static String PREF_LINE_NUMBERS = "pref_line_numbers"; public final static String DOCUMENTS = "Documents"; public final static String FOLDER = "Folder"; @@ -433,7 +434,7 @@ public class Editor extends Activity private boolean edit = false; private boolean view = false; - private boolean wrap = true; + private boolean wrap = false; private boolean lineNumbers = false; private boolean suggest = true; @@ -464,6 +465,7 @@ protected void onCreate(Bundle savedInstanceState) view = preferences.getBoolean(PREF_VIEW, true); last = preferences.getBoolean(PREF_LAST, false); wrap = preferences.getBoolean(PREF_WRAP, false); + lineNumbers = preferences.getBoolean(PREF_LINE_NUMBERS, false); suggest = preferences.getBoolean(PREF_SUGGEST, true); highlight = preferences.getBoolean(PREF_HIGH, false); @@ -538,6 +540,8 @@ protected void onCreate(Bundle savedInstanceState) if (savedInstanceState != null) edit = savedInstanceState.getBoolean(EDIT); + textView.setLineNumbersEnabled(lineNumbers); + if (!edit) { textView.setRawInputType(InputType.TYPE_NULL); @@ -816,6 +820,7 @@ public void onPause() editor.putBoolean(PREF_VIEW, view); editor.putBoolean(PREF_LAST, last); editor.putBoolean(PREF_WRAP, wrap); + editor.putBoolean(PREF_LINE_NUMBERS, lineNumbers); editor.putBoolean(PREF_SUGGEST, suggest); editor.putBoolean(PREF_HIGH, highlight); From cab1f1afa0f5fe13e25c1583ac0d88d60a3a4a0e Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sat, 14 Sep 2024 17:28:01 +0800 Subject: [PATCH 05/13] minor improvements --- src/main/java/org/billthefarmer/editor/Editor.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 0d5b6e8..5b2a0e3 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -1336,6 +1336,8 @@ private void viewClicked(MenuItem item) textView.setRawInputType(InputType.TYPE_NULL); textView.setTextIsSelectable(true); textView.clearFocus(); + if (lineNumbers) + textView.setLineNumbersEnabled(true); // Update boolean edit = false; From 3c7f855139e18e7eb3535b4e2552aaf75b6c5954 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sat, 14 Sep 2024 18:10:26 +0800 Subject: [PATCH 06/13] fix the issue of lag updating line numbers --- src/main/java/org/billthefarmer/editor/Editor.java | 1 + src/main/java/org/billthefarmer/editor/NewEditText.java | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 5b2a0e3..f3b0e8e 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -749,6 +749,7 @@ public void onTextChanged(CharSequence s, // onScrollChange scrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { + if (lineNumbers) textView.invalidate(); // Update line numbers if (updateHighlight != null) { textView.removeCallbacks(updateHighlight); diff --git a/src/main/java/org/billthefarmer/editor/NewEditText.java b/src/main/java/org/billthefarmer/editor/NewEditText.java index c2741bd..c381001 100644 --- a/src/main/java/org/billthefarmer/editor/NewEditText.java +++ b/src/main/java/org/billthefarmer/editor/NewEditText.java @@ -41,11 +41,11 @@ protected void onDraw(Canvas canvas) { } } - public void setLineNumbersEnabled(final boolean enable) { - if (enable ^ isLineNumbersEnabled) { + public void setLineNumbersEnabled(final boolean enabled) { + if (enabled ^ isLineNumbersEnabled) { post(this::invalidate); } - isLineNumbersEnabled = enable; + isLineNumbersEnabled = enabled; if (isLineNumbersEnabled) { lineNumbersDrawer.startLineTracking(); } else { From 5e4a44e065bd3222127b4ea4a25a8e2765f0bd61 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sun, 15 Sep 2024 21:48:53 +0800 Subject: [PATCH 07/13] reduce the frequency of updating line numbers --- src/main/java/org/billthefarmer/editor/Editor.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index f3b0e8e..8e36aed 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -447,6 +447,7 @@ public class Editor extends Activity private int type = MONO; private int syntax; + private long lineNumbersRefreshTime = 0; // onCreate @Override @@ -749,7 +750,14 @@ public void onTextChanged(CharSequence s, // onScrollChange scrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { - if (lineNumbers) textView.invalidate(); // Update line numbers + if (lineNumbers) { + final long time = System.currentTimeMillis(); + if (time - lineNumbersRefreshTime > 100) { + lineNumbersRefreshTime = time; + textView.invalidate(); // Update line numbers + } + } + if (updateHighlight != null) { textView.removeCallbacks(updateHighlight); From f9affddf986cc6382312425b82237630101acb4a Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Sun, 15 Sep 2024 22:00:23 +0800 Subject: [PATCH 08/13] minor improvements --- src/main/java/org/billthefarmer/editor/Editor.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 8e36aed..5592b3d 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -1398,6 +1398,9 @@ private void newFile(String text) if (text != null) textView.append(text); + if (lineNumbers) + textView.setLineNumbersEnabled(true); + setTitle(uri.getLastPathSegment()); match = UTF_8; getActionBar().setSubtitle(match); From c86bc4de52f675246df9fc1c500da3c785cb30a3 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Mon, 16 Sep 2024 21:20:23 +0800 Subject: [PATCH 09/13] fixed line numbers view --- .../java/org/billthefarmer/editor/Editor.java | 24 ++-- ...EditText.java => LineNumbersTextView.java} | 115 +++++++++--------- src/main/res/layout/edit.xml | 48 +++++--- src/main/res/layout/wrap.xml | 2 +- src/main/res/values-zh-rCN/strings.xml | 6 +- 5 files changed, 105 insertions(+), 90 deletions(-) rename src/main/java/org/billthefarmer/editor/{NewEditText.java => LineNumbersTextView.java} (72%) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 5592b3d..f7860e0 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -413,11 +413,12 @@ public class Editor extends Activity private String path; private Uri content; private String match; - private NewEditText textView; + private EditText textView; private TextView customView; private MenuItem searchItem; private SearchView searchView; private ScrollView scrollView; + private LineNumbersTextView lineNumbersView; private Runnable updateHighlight; private Runnable updateWordCount; @@ -447,7 +448,7 @@ public class Editor extends Activity private int type = MONO; private int syntax; - private long lineNumbersRefreshTime = 0; + private long refreshTime = 0; // Line numbers refresh time on scroll changed // onCreate @Override @@ -541,8 +542,6 @@ protected void onCreate(Bundle savedInstanceState) if (savedInstanceState != null) edit = savedInstanceState.getBoolean(EDIT); - textView.setLineNumbersEnabled(lineNumbers); - if (!edit) { textView.setRawInputType(InputType.TYPE_NULL); @@ -556,6 +555,10 @@ else if (!suggest) setSizeAndTypeface(size, type); + lineNumbersView = findViewById(R.id.lineNumbersView); + lineNumbersView.setEditText(textView); + lineNumbersView.setLineNumbersEnabled(lineNumbers); + Intent intent = getIntent(); Uri uri = intent.getData(); @@ -752,9 +755,9 @@ public void onTextChanged(CharSequence s, { if (lineNumbers) { final long time = System.currentTimeMillis(); - if (time - lineNumbersRefreshTime > 100) { - lineNumbersRefreshTime = time; - textView.invalidate(); // Update line numbers + if (time - refreshTime > 125) { + refreshTime = time; + lineNumbersView.setText(""); // To refresh line numbers } } @@ -1345,8 +1348,6 @@ private void viewClicked(MenuItem item) textView.setRawInputType(InputType.TYPE_NULL); textView.setTextIsSelectable(true); textView.clearFocus(); - if (lineNumbers) - textView.setLineNumbersEnabled(true); // Update boolean edit = false; @@ -1398,9 +1399,6 @@ private void newFile(String text) if (text != null) textView.append(text); - if (lineNumbers) - textView.setLineNumbersEnabled(true); - setTitle(uri.getLastPathSegment()); match = UTF_8; getActionBar().setSubtitle(match); @@ -1911,7 +1909,7 @@ private void lineNumbersClicked(MenuItem item) { lineNumbers = !lineNumbers; item.setChecked(lineNumbers); - textView.setLineNumbersEnabled(lineNumbers); + lineNumbersView.setLineNumbersEnabled(lineNumbers); } // suggestClicked diff --git a/src/main/java/org/billthefarmer/editor/NewEditText.java b/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java similarity index 72% rename from src/main/java/org/billthefarmer/editor/NewEditText.java rename to src/main/java/org/billthefarmer/editor/LineNumbersTextView.java index c381001..d2f0aa4 100644 --- a/src/main/java/org/billthefarmer/editor/NewEditText.java +++ b/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java @@ -9,71 +9,55 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.widget.EditText; +import android.widget.TextView; -public class NewEditText extends EditText { +public class LineNumbersTextView extends TextView { private boolean isLineNumbersEnabled; - private final LineNumbersDrawer lineNumbersDrawer = new LineNumbersDrawer(this); + private LineNumbersDrawer lineNumbersDrawer; - public NewEditText(Context context) { + public LineNumbersTextView(Context context) { super(context); } - public NewEditText(Context context, AttributeSet attrs) { + public LineNumbersTextView(Context context, AttributeSet attrs) { super(context, attrs); } - public NewEditText(Context context, AttributeSet attrs, int defStyleAttr) { + public LineNumbersTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - @Override - public boolean onPreDraw() { - lineNumbersDrawer.setTextSize(getTextSize()); - return super.onPreDraw(); - } - @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (isLineNumbersEnabled) { lineNumbersDrawer.draw(canvas); } } + public void setEditText(final EditText editor) { + lineNumbersDrawer = new LineNumbersDrawer(editor, this); + } + public void setLineNumbersEnabled(final boolean enabled) { if (enabled ^ isLineNumbersEnabled) { post(this::invalidate); } isLineNumbersEnabled = enabled; if (isLineNumbersEnabled) { - lineNumbersDrawer.startLineTracking(); + lineNumbersDrawer.prepare(); } else { - lineNumbersDrawer.reset(); - lineNumbersDrawer.stopLineTracking(); + lineNumbersDrawer.stop(); } } - /** - * Count instances of a single char in a char sequence. - */ - public static int countChar(final CharSequence s, int start, int end, final char c) { - int count = 0; - for (int i = start; i < end; i++) { - if (s.charAt(i) == c) { - count++; - } - } - return count; - } - static class LineNumbersDrawer { - private final EditText _editor; + public final EditText _editor; + public final LineNumbersTextView _textView; private final Paint _paint = new Paint(); - private final int _defaultPaddingLeft; - private static final int LINE_NUMBER_PADDING_LEFT = 6; + private static final int LINE_NUMBER_PADDING_LEFT = 1; private static final int LINE_NUMBER_PADDING_RIGHT = 12; private final Rect _visibleArea = new Rect(); @@ -89,13 +73,14 @@ static class LineNumbersDrawer { private final TextWatcher _lineTrackingWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { - _maxNumber -= countChar(s, start, start + count, '\n'); - _maxNumber -= countChar(s, start, start + count, '\n'); + _maxNumber -= countLines(s, start, start + count); + _textView.setText(" "); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - _maxNumber += countChar(s, start, start + count, '\n'); + _maxNumber += countLines(s, start, start + count); + _textView.setText(" "); } @Override @@ -104,27 +89,35 @@ public void afterTextChanged(Editable editable) { } }; - public LineNumbersDrawer(final EditText editor) { + public LineNumbersDrawer(final EditText editor, final LineNumbersTextView textView) { _editor = editor; + _textView = textView; _paint.setColor(0xFF999999); _paint.setTextAlign(Paint.Align.RIGHT); - _defaultPaddingLeft = editor.getPaddingLeft(); } - public void setTextSize(final float textSize) { - _paint.setTextSize(textSize); + private int countLines(final CharSequence s, int start, int end) { + int count = 0; + for (int i = start; i < end; i++) { + if (s.charAt(i) == '\n') { + count++; + } + } + return count; } - public boolean isTextSizeChanged() { - if (_paint.getTextSize() == _oldTextSize) { + private boolean isTextSizeChanged() { + final float textSize = _editor.getTextSize(); + if (textSize == _oldTextSize) { return false; } else { - _oldTextSize = _paint.getTextSize(); + _paint.setTextSize(textSize); + _oldTextSize = textSize; return true; } } - public boolean isMaxNumberDigitsChanged() { + private boolean isMaxNumberDigitsChanged() { final int oldDigits = _maxNumberDigits; if (_maxNumber < 10) { @@ -141,7 +134,7 @@ public boolean isMaxNumberDigitsChanged() { return _maxNumberDigits != oldDigits; } - public boolean isOutOfLineNumbersArea() { + private boolean isOutOfLineNumbersArea() { final int margin = (int) (_visibleArea.height() * 0.5f); final int top = _visibleArea.top - margin; final int bottom = _visibleArea.bottom + margin; @@ -157,20 +150,34 @@ public boolean isOutOfLineNumbersArea() { } } - public void startLineTracking() { + private void startLineTracking() { _editor.removeTextChangedListener(_lineTrackingWatcher); _maxNumber = 1; final CharSequence text = _editor.getText(); if (text != null) { - _maxNumber += countChar(text, 0, text.length(), '\n'); + _maxNumber += countLines(text, 0, text.length()); } _editor.addTextChangedListener(_lineTrackingWatcher); } - public void stopLineTracking() { + private void stopLineTracking() { _editor.removeTextChangedListener(_lineTrackingWatcher); } + public void prepare() { + startLineTracking(); + _textView.setVisibility(VISIBLE); + } + + public void updateState() { + // If text size or the max line number of digits changed, update related variables + if (isTextSizeChanged() || isMaxNumberDigitsChanged()) { + _numberX = LINE_NUMBER_PADDING_LEFT + (int) _paint.measureText(String.valueOf(_maxNumber)); + _gutterX = _numberX + LINE_NUMBER_PADDING_RIGHT; + _textView.setWidth(_gutterX + 1); + } + } + /** * Draw line numbers. * @@ -187,13 +194,7 @@ public void draw(final Canvas canvas) { return; } - // If text size or the max line number of digits changed, - // update the variables and reset padding - if (isTextSizeChanged() || isMaxNumberDigitsChanged()) { - _numberX = LINE_NUMBER_PADDING_LEFT + (int) _paint.measureText(String.valueOf(_maxNumber)); - _gutterX = _numberX + LINE_NUMBER_PADDING_RIGHT; - _editor.setPadding(_gutterX + 12, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom()); - } + updateState(); int i = _startLine[0], number = _startLine[1]; // If current visible area is out of current line numbers area, @@ -230,10 +231,12 @@ public void draw(final Canvas canvas) { } /** - * Reset to the state without line numbers. + * Stop drawing line numbers and reset states. */ - public void reset() { - _editor.setPadding(_defaultPaddingLeft, _editor.getPaddingTop(), _editor.getPaddingRight(), _editor.getPaddingBottom()); + public void stop() { + stopLineTracking(); + _textView.setWidth(0); + _textView.setVisibility(GONE); _maxNumberDigits = 0; } } diff --git a/src/main/res/layout/edit.xml b/src/main/res/layout/edit.xml index 73ba932..98d7fbf 100644 --- a/src/main/res/layout/edit.xml +++ b/src/main/res/layout/edit.xml @@ -1,6 +1,5 @@ - - - - + android:orientation="horizontal"> + + + + + + + + - + diff --git a/src/main/res/layout/wrap.xml b/src/main/res/layout/wrap.xml index 8d78dd0..8205192 100644 --- a/src/main/res/layout/wrap.xml +++ b/src/main/res/layout/wrap.xml @@ -11,7 +11,7 @@ android:fillViewport="true" tools:ignore="LabelFor"> - 系统 白色 黑色 - 怀旧 + 复古 文本大小 字体 等宽 - 成比例 + 比例 无衬线 - 有衬线 + 衬线 关于 From 0828a8c4bcdb5e31e826c94b717d4f51aaace8a2 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Mon, 16 Sep 2024 21:40:36 +0800 Subject: [PATCH 10/13] minor improvements --- src/main/res/layout/wrap.xml | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/main/res/layout/wrap.xml b/src/main/res/layout/wrap.xml index 8205192..80bd57c 100644 --- a/src/main/res/layout/wrap.xml +++ b/src/main/res/layout/wrap.xml @@ -1,6 +1,5 @@ - - + + + + + + + From fc2b0f857c6036ec9e90342fdd6123b229db8c4a Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Thu, 19 Sep 2024 15:30:05 +0800 Subject: [PATCH 11/13] force refresh line numbers on goto action finished --- .../java/org/billthefarmer/editor/Editor.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index f7860e0..00e597b 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -448,7 +448,8 @@ public class Editor extends Activity private int type = MONO; private int syntax; - private long refreshTime = 0; // Line numbers refresh time on scroll changed + private long highlightRefreshTime = 0; // Syntax highlighting refresh time on scroll changed + private long lineNumbersRefreshTime = 0; // Line numbers refresh time on scroll changed // onCreate @Override @@ -753,16 +754,15 @@ public void onTextChanged(CharSequence s, // onScrollChange scrollView.getViewTreeObserver().addOnScrollChangedListener(() -> { - if (lineNumbers) { - final long time = System.currentTimeMillis(); - if (time - refreshTime > 125) { - refreshTime = time; - lineNumbersView.setText(""); // To refresh line numbers - } + final long time = System.currentTimeMillis(); + + if (lineNumbers && time - lineNumbersRefreshTime > 125) { + lineNumbersRefreshTime = time; + lineNumbersView.setText(""); // Use 'setText("")' to arouse LineNumbersTextView refresh } - if (updateHighlight != null) - { + if (updateHighlight != null && time - highlightRefreshTime > 125) { + highlightRefreshTime = time; textView.removeCallbacks(updateHighlight); textView.postDelayed(updateHighlight, UPDATE_DELAY); } @@ -1783,9 +1783,9 @@ public void onProgressChanged(SeekBar seekBar, public void onStartTrackingTouch (SeekBar seekBar) {} @Override - public void onStopTrackingTouch (SeekBar seekBar) - { + public void onStopTrackingTouch (SeekBar seekBar) { dialog.dismiss(); + lineNumbersView.setText(""); } }); } From 57e05b20e033aeeabe1e0b7799b10872dec76bc7 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Thu, 19 Sep 2024 15:55:21 +0800 Subject: [PATCH 12/13] minor improvements --- src/main/java/org/billthefarmer/editor/Editor.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 00e597b..9321300 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -1777,6 +1777,10 @@ public void onProgressChanged(SeekBar seekBar, { if (fromUser) listener.onProgressChanged(seekBar, progress); + + if (lineNumbers) { + lineNumbersView.setText(""); + } } @Override @@ -1785,7 +1789,6 @@ public void onStartTrackingTouch (SeekBar seekBar) {} @Override public void onStopTrackingTouch (SeekBar seekBar) { dialog.dismiss(); - lineNumbersView.setText(""); } }); } From 02fc9e7d9611400a2a87c2d458f5b3f7b5eae264 Mon Sep 17 00:00:00 2001 From: Li Guanglin Date: Tue, 24 Sep 2024 22:03:59 +0800 Subject: [PATCH 13/13] optimize algorithm of drawing line numbers --- .../java/org/billthefarmer/editor/Editor.java | 4 +- .../editor/LineNumbersTextView.java | 284 ++++++++++-------- 2 files changed, 169 insertions(+), 119 deletions(-) diff --git a/src/main/java/org/billthefarmer/editor/Editor.java b/src/main/java/org/billthefarmer/editor/Editor.java index 9321300..0e5676d 100644 --- a/src/main/java/org/billthefarmer/editor/Editor.java +++ b/src/main/java/org/billthefarmer/editor/Editor.java @@ -758,7 +758,7 @@ public void onTextChanged(CharSequence s, if (lineNumbers && time - lineNumbersRefreshTime > 125) { lineNumbersRefreshTime = time; - lineNumbersView.setText(""); // Use 'setText("")' to arouse LineNumbersTextView refresh + lineNumbersView.forceRefresh(); } if (updateHighlight != null && time - highlightRefreshTime > 125) { @@ -1779,7 +1779,7 @@ public void onProgressChanged(SeekBar seekBar, listener.onProgressChanged(seekBar, progress); if (lineNumbers) { - lineNumbersView.setText(""); + lineNumbersView.forceRefresh(); } } diff --git a/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java b/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java index d2f0aa4..d1f7c05 100644 --- a/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java +++ b/src/main/java/org/billthefarmer/editor/LineNumbersTextView.java @@ -11,8 +11,11 @@ import android.widget.EditText; import android.widget.TextView; +/** + * @author Li Guanglin + */ public class LineNumbersTextView extends TextView { - private boolean isLineNumbersEnabled; + private boolean lineNumbersEnabled; private LineNumbersDrawer lineNumbersDrawer; public LineNumbersTextView(Context context) { @@ -30,200 +33,247 @@ public LineNumbersTextView(Context context, AttributeSet attrs, int defStyleAttr @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (isLineNumbersEnabled) { + if (lineNumbersEnabled) { lineNumbersDrawer.draw(canvas); } } - public void setEditText(final EditText editor) { - lineNumbersDrawer = new LineNumbersDrawer(editor, this); + public void forceRefresh() { + setText(""); // Use setText("") to activate LineNumbersTextView refresh + } + + public void setEditText(final EditText editText) { + lineNumbersDrawer = new LineNumbersDrawer(editText, this); } public void setLineNumbersEnabled(final boolean enabled) { - if (enabled ^ isLineNumbersEnabled) { - post(this::invalidate); - } - isLineNumbersEnabled = enabled; - if (isLineNumbersEnabled) { + lineNumbersEnabled = enabled; + if (lineNumbersEnabled) { lineNumbersDrawer.prepare(); } else { - lineNumbersDrawer.stop(); + lineNumbersDrawer.reset(); } } + // public boolean isLineNumbersEnabled() { return lineNumbersEnabled; } + static class LineNumbersDrawer { + public final EditText editText; + public final LineNumbersTextView textView; - public final EditText _editor; - public final LineNumbersTextView _textView; - private final Paint _paint = new Paint(); + private final Paint paint = new Paint(); - private static final int LINE_NUMBER_PADDING_LEFT = 1; - private static final int LINE_NUMBER_PADDING_RIGHT = 12; + private static final int NUMBER_PADDING_LEFT = 1; + private static final int NUMBER_PADDING_RIGHT = 12; - private final Rect _visibleArea = new Rect(); - private final Rect _lineNumbersArea = new Rect(); + private final Rect visibleArea = new Rect(); + private final Rect lineNumbersArea = new Rect(); - private int _numberX; - private int _gutterX; - private int _maxNumber = 1; // to gauge gutter width - private int _maxNumberDigits; - private float _oldTextSize; - private final int[] _startLine = {0, 1}; // {line index, actual line number} + private int fenceX; + private int numberX; + private int maxNumber = 1; // To gauge the width of line numbers fence + private int maxNumberDigits; + private int lastMaxNumber; + private int lastLayoutLineCount; + private float lastTextSize; - private final TextWatcher _lineTrackingWatcher = new TextWatcher() { + private final int[] startLine = {0, 1}; // {line index, actual line number} + + private final TextWatcher lineTrackingWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { - _maxNumber -= countLines(s, start, start + count); - _textView.setText(" "); + maxNumber -= countLines(s, start, start + count); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - _maxNumber += countLines(s, start, start + count); - _textView.setText(" "); + maxNumber += countLines(s, start, start + count); } @Override public void afterTextChanged(Editable editable) { - + if (isLayoutLineCountChanged() || isMaxNumberChanged()) { + textView.forceRefresh(); + } } }; - public LineNumbersDrawer(final EditText editor, final LineNumbersTextView textView) { - _editor = editor; - _textView = textView; - _paint.setColor(0xFF999999); - _paint.setTextAlign(Paint.Align.RIGHT); + public LineNumbersDrawer(final EditText editText, final LineNumbersTextView textView) { + this.editText = editText; + this.textView = textView; + paint.setColor(0xFF999999); + paint.setTextAlign(Paint.Align.RIGHT); } - private int countLines(final CharSequence s, int start, int end) { - int count = 0; - for (int i = start; i < end; i++) { - if (s.charAt(i) == '\n') { - count++; - } + private boolean isOutOfLineNumbersArea() { + final int margin = (int) (visibleArea.height() * 0.5f); + final int top = visibleArea.top - margin; + final int bottom = visibleArea.bottom + margin; + + if (top < lineNumbersArea.top || bottom > lineNumbersArea.bottom) { + // Set line numbers area + // height of line numbers area = (1.5 + 1 + 1.5) * height of visible area + lineNumbersArea.top = top - visibleArea.height(); + lineNumbersArea.bottom = bottom + visibleArea.height(); + return true; + } else { + return false; } - return count; } private boolean isTextSizeChanged() { - final float textSize = _editor.getTextSize(); - if (textSize == _oldTextSize) { + if (editText.getTextSize() == lastTextSize) { return false; } else { - _paint.setTextSize(textSize); - _oldTextSize = textSize; + lastTextSize = editText.getTextSize(); + paint.setTextSize(lastTextSize); return true; } } - private boolean isMaxNumberDigitsChanged() { - final int oldDigits = _maxNumberDigits; - - if (_maxNumber < 10) { - _maxNumberDigits = 1; - } else if (_maxNumber < 100) { - _maxNumberDigits = 2; - } else if (_maxNumber < 1000) { - _maxNumberDigits = 3; - } else if (_maxNumber < 10000) { - _maxNumberDigits = 4; + private boolean isMaxNumberChanged() { + if (maxNumber == lastMaxNumber) { + return false; } else { - _maxNumberDigits = 5; + lastMaxNumber = maxNumber; + return true; } - return _maxNumberDigits != oldDigits; } - private boolean isOutOfLineNumbersArea() { - final int margin = (int) (_visibleArea.height() * 0.5f); - final int top = _visibleArea.top - margin; - final int bottom = _visibleArea.bottom + margin; - - if (top < _lineNumbersArea.top || bottom > _lineNumbersArea.bottom) { - // Reset line numbers area - // height of line numbers area = (1.5 + 1 + 1.5) * height of visible area - _lineNumbersArea.top = top - _visibleArea.height(); - _lineNumbersArea.bottom = bottom + _visibleArea.height(); - return true; + private boolean isMaxNumberDigitsChanged() { + int digits; + if (maxNumber < 10) { + digits = 1; + } else if (maxNumber < 100) { + digits = 2; + } else if (maxNumber < 1000) { + digits = 3; + } else if (maxNumber < 10000) { + digits = 4; } else { + digits = 5; + } + + if (digits == maxNumberDigits) { return false; } + + maxNumberDigits = digits; + return true; } - private void startLineTracking() { - _editor.removeTextChangedListener(_lineTrackingWatcher); - _maxNumber = 1; - final CharSequence text = _editor.getText(); - if (text != null) { - _maxNumber += countLines(text, 0, text.length()); + private boolean isLayoutLineCountChanged() { + final Layout layout = editText.getLayout(); + if (layout == null) { + return true; + } + + final int lineCount = layout.getLineCount(); + if (lineCount == lastLayoutLineCount) { + return false; + } else { + lastLayoutLineCount = lineCount; + return true; } - _editor.addTextChangedListener(_lineTrackingWatcher); } - private void stopLineTracking() { - _editor.removeTextChangedListener(_lineTrackingWatcher); + private int countLines(final CharSequence s, int start, int end) { + int count = 0; + for (; start < end; start++) { + if (s.charAt(start) == '\n') { + count++; + } + } + return count; } - public void prepare() { - startLineTracking(); - _textView.setVisibility(VISIBLE); - } + private void lineTracking(boolean enabled) { + editText.removeTextChangedListener(lineTrackingWatcher); - public void updateState() { - // If text size or the max line number of digits changed, update related variables - if (isTextSizeChanged() || isMaxNumberDigitsChanged()) { - _numberX = LINE_NUMBER_PADDING_LEFT + (int) _paint.measureText(String.valueOf(_maxNumber)); - _gutterX = _numberX + LINE_NUMBER_PADDING_RIGHT; - _textView.setWidth(_gutterX + 1); + if (enabled) { + maxNumber = 1; + final CharSequence text = editText.getText(); + if (text != null) { + maxNumber += countLines(text, 0, text.length()); + } + editText.addTextChangedListener(lineTrackingWatcher); } } + /** + * Prepare for drawing line numbers. + */ + public void prepare() { + lineTracking(true); + textView.setVisibility(VISIBLE); + } + /** * Draw line numbers. * * @param canvas The canvas on which the line numbers will be drawn. */ public void draw(final Canvas canvas) { - if (!_editor.getLocalVisibleRect(_visibleArea)) { + if (!editText.getLocalVisibleRect(visibleArea)) { return; } - final CharSequence text = _editor.getText(); - final Layout layout = _editor.getLayout(); + final CharSequence text = editText.getText(); + final Layout layout = editText.getLayout(); if (text == null || layout == null) { return; } - updateState(); + // If text size or the max line number of digits changed, update related variables + if (isTextSizeChanged() || isMaxNumberDigitsChanged()) { + numberX = NUMBER_PADDING_LEFT + (int) paint.measureText(String.valueOf(maxNumber)); + fenceX = numberX + NUMBER_PADDING_RIGHT; + textView.setWidth(fenceX + 1); + } - int i = _startLine[0], number = _startLine[1]; // If current visible area is out of current line numbers area, - // iterate from the first line to recalculate the start line + // will recalculate the start line + boolean invalid = false; if (isOutOfLineNumbersArea()) { - i = 0; - number = 1; - _startLine[0] = -1; + invalid = true; + startLine[0] = 0; + startLine[1] = 1; } - // Draw border of the gutter - canvas.drawLine(_gutterX, _lineNumbersArea.top, _gutterX, _lineNumbersArea.bottom, _paint); + // Draw right border of the fence + canvas.drawLine(fenceX, lineNumbersArea.top, fenceX, lineNumbersArea.bottom, paint); // Draw line numbers + int i = startLine[0]; + int number = startLine[1]; + int y = layout.getLineBaseline(i); final int count = layout.getLineCount(); - final int offsetY = _editor.getPaddingTop(); + final int offsetY = editText.getPaddingTop(); + + if (y > lineNumbersArea.top) { + if (invalid) { + invalid = false; + startLine[0] = i; + startLine[1] = number; + } + canvas.drawText(String.valueOf(number), numberX, layout.getLineBaseline(i) + offsetY, paint); + } + i++; + number++; + for (; i < count; i++) { - final int start = layout.getLineStart(i); - if (start == 0 || text.charAt(start - 1) == '\n') { - final int y = layout.getLineBaseline(i); - if (y > _lineNumbersArea.bottom) { - break; - } - if (y > _lineNumbersArea.top) { - if (_startLine[0] < 0) { - _startLine[0] = i; - _startLine[1] = number; + if (text.charAt(layout.getLineStart(i) - 1) == '\n') { + y = layout.getLineBaseline(i); + if (y > lineNumbersArea.top) { + if (invalid) { + invalid = false; + startLine[0] = i; + startLine[1] = number; + } + canvas.drawText(String.valueOf(number), numberX, y + offsetY, paint); + if (y > lineNumbersArea.bottom) { + break; } - canvas.drawText(String.valueOf(number), _numberX, y + offsetY, _paint); } number++; } @@ -231,13 +281,13 @@ public void draw(final Canvas canvas) { } /** - * Stop drawing line numbers and reset states. + * Reset states related line numbers. */ - public void stop() { - stopLineTracking(); - _textView.setWidth(0); - _textView.setVisibility(GONE); - _maxNumberDigits = 0; + public void reset() { + lineTracking(false); + maxNumberDigits = 0; + textView.setWidth(0); + textView.setVisibility(GONE); } } }