diff --git a/src/Notes-master/Notes-master.rar b/src/Notes-master/Notes-master.rar deleted file mode 100644 index c3186b6..0000000 Binary files a/src/Notes-master/Notes-master.rar and /dev/null differ diff --git a/src/Notes-master/res/layout/note_edit.xml b/src/Notes-master/res/layout/note_edit.xml index abde9ae..abec1dc 100644 --- a/src/Notes-master/res/layout/note_edit.xml +++ b/src/Notes-master/res/layout/note_edit.xml @@ -67,20 +67,11 @@ android:layout_marginRight="8dip" /> - - - + + + + + + + + + 要查看的便签不存在 不能为空便签设置闹钟提醒 不能将空便签发送到桌面 + 清单模式下不支持格式化 + 请先选择文本 导出成功 导出失败 已将文本文件(%1$s)输出至SD卡(%2$s)目录 @@ -137,6 +139,14 @@ 请输入密码 密码错误,请重试 密码不能为空 + + 加粗 + 斜体 + 下划线 + 符号列表 + 编号列表 + 文字颜色 + 背景颜色 %1$s 条符合“%2$s”的搜索结果 diff --git a/src/Notes-master/res/values/strings.xml b/src/Notes-master/res/values/strings.xml index 170e4f0..9889716 100644 --- a/src/Notes-master/res/values/strings.xml +++ b/src/Notes-master/res/values/strings.xml @@ -88,6 +88,8 @@ The note is not exist Sorry, can not set clock on empty note Sorry, can not send and empty note to home + Formatting is not supported in list mode + Please select text first Export successful Export fail Export text file (%1$s) to SD (%2$s) directory @@ -144,6 +146,14 @@ Enter password please Error, try again The password can not be empty + + Bold + Italic + Underline + Bullet list + Numbered list + Text color + Background color %1$s result for \"%2$s\" diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java index 4d39639..944d043 100644 --- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java @@ -33,8 +33,12 @@ import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; +import android.text.Html; import android.text.format.DateUtils; import android.text.style.BackgroundColorSpan; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.graphics.Typeface; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -168,6 +172,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, private SharedPreferences mSharedPrefs; // 共享偏好设置 private int mFontSizeId; // 字体大小ID private ImageButton mBtnInsertImage; // 插入图片按钮 OMO + + // 富文本工具栏按钮 + private ImageButton mBtnBold; // 加粗按钮 + private ImageButton mBtnItalic; // 斜体按钮 + private ImageButton mBtnUnderline; // 下划线按钮 private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; // 字体大小偏好设置键 private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; // 快捷图标标题最大长度 @@ -351,11 +360,84 @@ public class NoteEditActivity extends Activity implements OnClickListener, String content = mWorkingNote.getContent(); Log.d(TAG, "Initializing note content: " + content); - // 将文本内容转换为包含ImageSpan的SpannableString - SpannableString spannableString = convertTextToSpannableWithImages(content); - mNoteEditor.setText(spannableString); - // 将光标定位到文本末尾 - mNoteEditor.setSelection(spannableString.length()); + if (TextUtils.isEmpty(content)) { + mNoteEditor.setText(""); + mNoteEditor.setSelection(0); + } else { + Spannable spannable; + + // 检查是否是HTML格式(包含HTML标签) + if (content.contains("<") && content.contains(">")) { + try { + // 从HTML格式恢复SpannableString,保留样式(加粗、斜体、下划线) + // Html.fromHtml会自动将HTML标签转换为对应的Span + spannable = (Spannable) Html.fromHtml(content); + } catch (Exception e) { + // 如果HTML解析失败,使用普通文本 + Log.e(TAG, "Error parsing HTML content: " + e.getMessage()); + spannable = new SpannableString(content); + } + } else { + // 普通文本格式,直接创建SpannableString + spannable = new SpannableString(content); + } + + // 处理图片:将包含[IMAGE]标签的文本转换为包含ImageSpan的SpannableString + // 注意:convertTextToSpannableWithImages会处理[IMAGE]标记,但会保留文本内容 + String textContent = spannable.toString(); + SpannableString finalSpannable = convertTextToSpannableWithImages(textContent); + + // 将样式从原始Spannable复制到处理后的SpannableString + // 由于convertTextToSpannableWithImages可能改变了文本长度(用空格替换了[IMAGE]标签), + // 我们需要基于文本内容来映射样式位置 + if (textContent.length() == finalSpannable.length()) { + // 文本长度相同,直接复制样式 + StyleSpan[] styleSpans = spannable.getSpans(0, spannable.length(), StyleSpan.class); + UnderlineSpan[] underlineSpans = spannable.getSpans(0, spannable.length(), UnderlineSpan.class); + + for (StyleSpan span : styleSpans) { + int start = spannable.getSpanStart(span); + int end = spannable.getSpanEnd(span); + if (start >= 0 && end <= finalSpannable.length() && start < end) { + finalSpannable.setSpan(new StyleSpan(span.getStyle()), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + for (UnderlineSpan span : underlineSpans) { + int start = spannable.getSpanStart(span); + int end = spannable.getSpanEnd(span); + if (start >= 0 && end <= finalSpannable.length() && start < end) { + finalSpannable.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } else { + // 文本长度不同,说明有图片被处理了 + // 这种情况下,样式位置需要重新计算 + // 为了简化,我们基于原始文本位置映射样式(可能会有偏差,但应该能处理大部分情况) + StyleSpan[] styleSpans = spannable.getSpans(0, spannable.length(), StyleSpan.class); + UnderlineSpan[] underlineSpans = spannable.getSpans(0, spannable.length(), UnderlineSpan.class); + + for (StyleSpan span : styleSpans) { + int start = Math.max(0, Math.min(spannable.getSpanStart(span), finalSpannable.length())); + int end = Math.max(start, Math.min(spannable.getSpanEnd(span), finalSpannable.length())); + if (start < end) { + finalSpannable.setSpan(new StyleSpan(span.getStyle()), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + for (UnderlineSpan span : underlineSpans) { + int start = Math.max(0, Math.min(spannable.getSpanStart(span), finalSpannable.length())); + int end = Math.max(start, Math.min(spannable.getSpanEnd(span), finalSpannable.length())); + if (start < end) { + finalSpannable.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + mNoteEditor.setText(finalSpannable); + // 将光标定位到文本末尾 + mNoteEditor.setSelection(finalSpannable.length()); + } } // 隐藏所有背景选择的选中状态 @@ -597,6 +679,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, if (mBtnInsertImage != null) { mBtnInsertImage.setOnClickListener(this); } + + // 初始化富文本工具栏按钮 + mBtnBold = (ImageButton) findViewById(R.id.btn_bold); + mBtnItalic = (ImageButton) findViewById(R.id.btn_italic); + mBtnUnderline = (ImageButton) findViewById(R.id.btn_underline); + + // 为富文本工具栏按钮设置点击监听器 + if (mBtnBold != null) { + mBtnBold.setOnClickListener(this); + } + if (mBtnItalic != null) { + mBtnItalic.setOnClickListener(this); + } + if (mBtnUnderline != null) { + mBtnUnderline.setOnClickListener(this); + } } /** @@ -674,6 +772,15 @@ public class NoteEditActivity extends Activity implements OnClickListener, } else if (id == R.id.btn_insert_image) { // 处理插入图片按钮点击事件 OMO pickImageFromGallery(); + } else if (id == R.id.btn_bold) { + // 富文本工具栏 - 加粗按钮 + applyBoldStyle(); + } else if (id == R.id.btn_italic) { + // 富文本工具栏 - 斜体按钮 + applyItalicStyle(); + } else if (id == R.id.btn_underline) { + // 富文本工具栏 - 下划线按钮 + applyUnderlineStyle(); } } @@ -1159,7 +1266,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, } mWorkingNote.setWorkingText(sb.toString()); } else { - mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); + // 保存时,将SpannableString转换为HTML格式以保留样式信息 + Spannable spannable = mNoteEditor.getText(); + if (spannable != null && spannable.length() > 0) { + // 将Spannable转换为HTML格式,保留加粗、斜体、下划线等样式 + // Html.toHtml会自动处理StyleSpan和UnderlineSpan + String htmlContent = Html.toHtml(spannable); + mWorkingNote.setWorkingText(htmlContent); + } else { + mWorkingNote.setWorkingText(""); + } } return hasChecked; } @@ -1298,4 +1414,173 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.d(TAG, "Image selection canceled or failed, resultCode: " + resultCode); } } + + /** + * 应用文本样式(加粗、斜体、下划线) + * @param styleType 样式类型:Typeface.BOLD, Typeface.ITALIC, 或 -1 表示下划线 + */ + private void applyTextStyle(int styleType) { + if (mNoteEditor == null) { + return; + } + + // 检查是否在清单模式,如果是则提示用户切换到普通模式 + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + showToast(R.string.error_formatting_not_supported_in_list_mode); + return; + } + + Editable editable = mNoteEditor.getText(); + if (editable == null) { + return; + } + + int selectionStart = mNoteEditor.getSelectionStart(); + int selectionEnd = mNoteEditor.getSelectionEnd(); + + // 如果没有选中文本,则选中光标所在的单词或当前字符 + if (selectionStart == selectionEnd) { + // 尝试选中光标所在的单词 + String text = editable.toString(); + int start = selectionStart; + int end = selectionEnd; + + // 向前查找单词开始位置 + while (start > 0 && Character.isLetterOrDigit(text.charAt(start - 1))) { + start--; + } + + // 向后查找单词结束位置 + while (end < text.length() && Character.isLetterOrDigit(text.charAt(end))) { + end++; + } + + // 如果找到了单词,则选中它;否则选中当前字符 + if (start < end) { + selectionStart = start; + selectionEnd = end; + mNoteEditor.setSelection(selectionStart, selectionEnd); + } else { + // 如果没有找到单词,提示用户先选择文本 + showToast(R.string.error_please_select_text); + return; + } + } + + // 确保选择范围有效 + if (selectionStart < 0 || selectionEnd > editable.length() || selectionStart >= selectionEnd) { + showToast(R.string.error_please_select_text); + return; + } + + // 应用样式 + if (styleType == -1) { + // 下划线样式 + UnderlineSpan[] underlineSpans = editable.getSpans(selectionStart, selectionEnd, UnderlineSpan.class); + if (underlineSpans.length > 0) { + // 移除下划线 + for (UnderlineSpan span : underlineSpans) { + int spanStart = editable.getSpanStart(span); + int spanEnd = editable.getSpanEnd(span); + editable.removeSpan(span); + // 如果移除的span范围大于选中范围,需要重新应用样式到剩余部分 + if (spanStart < selectionStart) { + editable.setSpan(new UnderlineSpan(), spanStart, selectionStart, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (spanEnd > selectionEnd) { + editable.setSpan(new UnderlineSpan(), selectionEnd, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } else { + // 添加下划线 + editable.setSpan(new UnderlineSpan(), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } else { + // 加粗或斜体样式 + StyleSpan[] styleSpans = editable.getSpans(selectionStart, selectionEnd, StyleSpan.class); + + // 检查选中范围内是否已经有该样式 + boolean hasStyle = false; + int existingStyle = 0; + + // 收集选中范围内的所有样式 + for (StyleSpan span : styleSpans) { + int spanStart = editable.getSpanStart(span); + int spanEnd = editable.getSpanEnd(span); + + // 如果span与选中范围重叠 + if (spanStart < selectionEnd && spanEnd > selectionStart) { + int spanStyle = span.getStyle(); + existingStyle |= spanStyle; + + // 检查是否包含目标样式 + if ((spanStyle & styleType) == styleType) { + hasStyle = true; + } + } + } + + // 移除选中范围内的所有StyleSpan,以便重新应用 + for (int i = styleSpans.length - 1; i >= 0; i--) { + StyleSpan span = styleSpans[i]; + int spanStart = editable.getSpanStart(span); + int spanEnd = editable.getSpanEnd(span); + + // 如果span与选中范围重叠 + if (spanStart < selectionEnd && spanEnd > selectionStart) { + editable.removeSpan(span); + + // 处理span范围大于选中范围的情况,保留未选中部分的样式 + int spanStyle = span.getStyle(); + if (spanStart < selectionStart) { + // 保留选中范围之前的样式 + editable.setSpan(new StyleSpan(spanStyle), spanStart, selectionStart, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (spanEnd > selectionEnd) { + // 保留选中范围之后的样式 + editable.setSpan(new StyleSpan(spanStyle), selectionEnd, spanEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + // 切换样式:如果有则移除,如果没有则添加 + int finalStyle = existingStyle; + if (hasStyle) { + // 移除目标样式 + finalStyle &= ~styleType; + } else { + // 添加目标样式 + finalStyle |= styleType; + } + + // 应用最终样式 + if (finalStyle != 0) { + editable.setSpan(new StyleSpan(finalStyle), selectionStart, selectionEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + // 保持选中状态 + mNoteEditor.setSelection(selectionStart, selectionEnd); + } + + /** + * 应用加粗样式 + */ + private void applyBoldStyle() { + applyTextStyle(Typeface.BOLD); + } + + /** + * 应用斜体样式 + */ + private void applyItalicStyle() { + applyTextStyle(Typeface.ITALIC); + } + + /** + * 应用下划线样式 + */ + private void applyUnderlineStyle() { + applyTextStyle(-1); // -1 表示下划线 + } }