From 4eef6ca901baf2b76d14963d9e5be0c2753e4713 Mon Sep 17 00:00:00 2001 From: chroe <454604461@qq.com> Date: Sat, 24 Jan 2026 09:33:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=9B=9E=E9=80=80=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/micode/notes/ui/NoteEditActivity.java | 178 +++++++++++++++++- src/main/res/drawable/ic_redo.xml | 10 + src/main/res/drawable/ic_undo.xml | 10 + src/main/res/layout/note_edit.xml | 20 ++ src/main/res/values/strings.xml | 2 + 5 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 src/main/res/drawable/ic_redo.xml create mode 100644 src/main/res/drawable/ic_undo.xml diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 8149d67..402f220 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -110,6 +110,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, public TextView tvModified; // 修改时间显示 public ImageView ivAlertIcon; // 提醒图标 public TextView tvAlertDate; // 提醒日期显示 + public ImageButton ibUndo; // 撤销按钮 + public ImageButton ibRedo; // 重做按钮 public ImageView ibSetBgColor; // 设置背景色按钮 public ImageButton ibInsertImage; // 插入图片按钮 public TextView tvTitleHint; // 标题提示文字 @@ -190,6 +192,13 @@ public class NoteEditActivity extends Activity implements OnClickListener, private LinearLayout mEditTextList; // 编辑列表(用于checklist模式) private String mUserQuery; // 用户查询(用于搜索高亮) private Pattern mPattern; // 搜索模式(用于搜索高亮) + + // 撤销/重做相关 + private static final int MAX_HISTORY_SIZE = 50; // 最大历史记录数量 + private java.util.Stack mUndoStack; // 撤销栈 + private java.util.Stack mRedoStack; // 重做栈 + private boolean mIsRestoringFromHistory; // 是否正在从历史记录恢复文本 + private String mLastSavedText; // 最后保存的文本 /** * 初始化活动 onCreate 方法 @@ -553,6 +562,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon); mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); + mNoteHeaderHolder.ibUndo = (ImageButton) findViewById(R.id.btn_undo); + mNoteHeaderHolder.ibUndo.setOnClickListener(this); + mNoteHeaderHolder.ibRedo = (ImageButton) findViewById(R.id.btn_redo); + mNoteHeaderHolder.ibRedo.setOnClickListener(this); mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); mNoteHeaderHolder.ibInsertImage = (ImageButton) findViewById(R.id.add_img_btn); @@ -616,21 +629,50 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 初始化编辑列表 mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); - // 添加编辑器文本监听器,实现实时字数更新 + // 初始化撤销/重做栈 + mUndoStack = new java.util.Stack(); + mRedoStack = new java.util.Stack(); + mIsRestoringFromHistory = false; + + // 记录初始文本 + mLastSavedText = mNoteEditor.getText().toString(); + mUndoStack.push(mLastSavedText); + + // 添加编辑器文本监听器,实现实时字数更新和undo/redo状态监听 mNoteEditor.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // 只在非历史恢复模式下记录历史 + if (!mIsRestoringFromHistory) { + String currentText = s.toString(); + // 总是记录历史记录,因为文本即将改变 + mUndoStack.push(currentText); + // 限制栈大小 + if (mUndoStack.size() > MAX_HISTORY_SIZE) { + mUndoStack.remove(0); + } + // 清空重做栈 + mRedoStack.clear(); + } } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { updateWordCount(); + updateUndoRedoButtons(); } @Override public void afterTextChanged(Editable s) { + // 只在非历史恢复模式下更新最后保存的文本 + if (!mIsRestoringFromHistory) { + mLastSavedText = s.toString(); + } } }); + + // 初始更新undo/redo按钮状态 + updateUndoRedoButtons(); } /** @@ -683,7 +725,17 @@ public class NoteEditActivity extends Activity implements OnClickListener, */ public void onClick(View v) { int id = v.getId(); - if (id == R.id.btn_set_bg_color) { + if (id == R.id.btn_undo) { + // 执行撤销操作 + if (canUndo()) { + undo(); + } + } else if (id == R.id.btn_redo) { + // 执行重做操作 + if (canRedo()) { + redo(); + } + } else if (id == R.id.btn_set_bg_color) { mNoteBgColorSelector.setVisibility(View.VISIBLE); findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.VISIBLE); @@ -1223,7 +1275,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, content = mNoteEditor.getText().toString(); } - int wordCount = content != null ? content.length() : 0; + // 过滤掉所有 [IMAGE:图片路径] 格式的图片标记,只统计纯文本字符数 + int wordCount = calculateTextLength(content); mNoteHeaderHolder.tvWordCount.setText(String.valueOf(wordCount)); if (wordCount > 5000) { @@ -1235,6 +1288,125 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + /** + * 计算文本长度,排除所有 [IMAGE:图片路径] 格式的图片标记 + * @param text 包含图片标记的文本 + * @return 纯文本字符数 + */ + private int calculateTextLength(String text) { + if (TextUtils.isEmpty(text)) { + return 0; + } + + int length = 0; + int startIndex = 0; + int imageStart = text.indexOf("[IMAGE:", startIndex); + + while (imageStart != -1) { + // 统计图片标记前的文本长度 + length += imageStart - startIndex; + + // 查找图片标记的结束位置 + int imageEnd = text.indexOf("]", imageStart); + if (imageEnd == -1) { + // 如果没有找到结束标记,说明格式不正确,统计剩余所有文本 + length += text.length() - imageStart; + break; + } + + // 更新起始位置到图片标记结束后 + startIndex = imageEnd + 1; + + // 查找下一个图片标记 + imageStart = text.indexOf("[IMAGE:", startIndex); + } + + // 统计最后一个图片标记后的文本长度 + length += text.length() - startIndex; + + return length; + } + + /** + * 更新撤销和重做按钮的状态 + */ + private void updateUndoRedoButtons() { + if (mNoteHeaderHolder != null) { + boolean canUndo = canUndo(); + boolean canRedo = canRedo(); + + mNoteHeaderHolder.ibUndo.setEnabled(canUndo); + mNoteHeaderHolder.ibRedo.setEnabled(canRedo); + + // 设置按钮透明度,禁用时半透明 + mNoteHeaderHolder.ibUndo.setAlpha(canUndo ? 1.0f : 0.5f); + mNoteHeaderHolder.ibRedo.setAlpha(canRedo ? 1.0f : 0.5f); + } + } + + /** + * 检查是否可以撤销 + * @return 是否可以撤销 + */ + private boolean canUndo() { + return mUndoStack.size() > 1; + } + + /** + * 检查是否可以重做 + * @return 是否可以重做 + */ + private boolean canRedo() { + return !mRedoStack.isEmpty(); + } + + /** + * 执行撤销操作 + */ + private void undo() { + if (canUndo()) { + // 将当前文本加入重做栈 + mRedoStack.push(mNoteEditor.getText().toString()); + + // 弹出上一个文本 + mUndoStack.pop(); + + // 恢复上一个文本 + String previousText = mUndoStack.peek(); + mIsRestoringFromHistory = true; + mNoteEditor.setText(previousText); + mNoteEditor.setSelection(previousText.length()); + mLastSavedText = previousText; + mIsRestoringFromHistory = false; + + // 更新按钮状态 + updateUndoRedoButtons(); + } + } + + /** + * 执行重做操作 + */ + private void redo() { + if (canRedo()) { + // 将当前文本加入撤销栈 + mUndoStack.push(mNoteEditor.getText().toString()); + + // 弹出下一个文本 + String nextText = mRedoStack.pop(); + + // 恢复下一个文本 + mIsRestoringFromHistory = true; + mNoteEditor.setText(nextText); + mNoteEditor.setSelection(nextText.length()); + mLastSavedText = nextText; + mIsRestoringFromHistory = false; + + // 更新按钮状态 + updateUndoRedoButtons(); + } + } + private boolean saveNote() { getWorkingText(); String title = mNoteHeaderHolder.etTitle.getText().toString(); diff --git a/src/main/res/drawable/ic_redo.xml b/src/main/res/drawable/ic_redo.xml new file mode 100644 index 0000000..c634af2 --- /dev/null +++ b/src/main/res/drawable/ic_redo.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/main/res/drawable/ic_undo.xml b/src/main/res/drawable/ic_undo.xml new file mode 100644 index 0000000..dbcd4a1 --- /dev/null +++ b/src/main/res/drawable/ic_undo.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index 29e9cc2..971e984 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -58,11 +58,31 @@ android:layout_marginRight="8dip" android:textAppearance="@style/TextAppearanceSecondaryItem" /> + + Italic Normal Insert Image + Undo + Redo Failed to insert image -- 2.34.1