diff --git a/src/Notes-master/custom_background_design.md b/src/Notes-master/custom_background_design.md
new file mode 100644
index 0000000..06af831
--- /dev/null
+++ b/src/Notes-master/custom_background_design.md
@@ -0,0 +1,80 @@
+# 自定义背景图片功能实现方案
+
+## 1. 功能概述
+
+在现有背景颜色修改功能的基础上,增加自定义背景图片功能。用户可通过点击指定按钮访问手机本地存储中的图片文件,选择目标图片后进行裁剪操作,完成裁剪后将该图片设置为当前笔记的背景。
+
+## 2. 设计思路
+
+### 2.1 与现有系统兼容
+
+- 利用现有背景颜色ID系统,为自定义背景图片分配一个特殊的ID值
+- 当背景颜色ID为特殊值时,应用加载自定义图片而非预定义背景资源
+
+### 2.2 存储设计
+
+- 在WorkingNote类中添加字段存储自定义背景图片路径
+- 将裁剪后的图片保存到应用私有存储目录
+
+### 2.3 功能流程
+
+1. 用户点击背景颜色选择器中的"自定义图片"选项
+2. 系统调用图片选择器,用户选择本地图片
+3. 系统调用图片裁剪器,用户调整图片大小和比例
+4. 裁剪完成后,图片保存到应用私有存储
+5. 更新笔记背景为裁剪后的图片
+6. 保存笔记时,同时保存背景图片信息
+
+## 3. 具体实现方案
+
+### 3.1 代码修改点
+
+#### 3.1.1 ResourceParser类
+
+- 添加CUSTOM_IMAGE常量,用于标识自定义背景图片
+- 修改相关方法,支持自定义背景图片的处理
+
+#### 3.1.2 WorkingNote类
+
+- 添加mCustomBgImagePath字段,存储自定义背景图片路径
+- 添加相应的getter和setter方法
+- 修改setBgColorId和getBgColorResId方法,支持自定义背景图片
+
+#### 3.1.3 NoteEditActivity类
+
+- 在背景颜色选择器中添加"自定义图片"选项
+- 实现图片选择功能(调用系统图片选择器)
+- 实现图片裁剪功能(调用系统裁剪器)
+- 实现图片保存和设置为背景的功能
+- 处理权限请求(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE)
+
+### 3.2 界面设计
+
+- 在现有背景颜色选择器中添加一个新的选项卡片,显示"自定义图片"文字和图标
+- 点击该选项后,启动图片选择流程
+
+### 3.3 权限处理
+
+- 在AndroidManifest.xml中添加必要的权限声明
+- 在运行时请求相应的权限
+
+### 3.4 图片处理
+
+- 选择图片后,调用系统裁剪功能,限制裁剪比例为笔记的宽高比
+- 将裁剪后的图片保存到应用的私有存储目录
+- 使用FileProvider确保安全访问
+
+## 4. 兼容性考虑
+
+- 确保自定义背景图片功能与原有背景颜色修改功能可以无缝切换
+- 当用户选择预定义背景颜色时,清除自定义背景图片信息
+- 当用户选择自定义背景图片时,更新背景颜色ID为CUSTOM_IMAGE
+
+## 5. 测试要点
+
+- 测试图片选择功能是否正常工作
+- 测试图片裁剪功能是否正常工作
+- 测试自定义背景图片是否能正确设置为笔记背景
+- 测试自定义背景图片与原有背景颜色功能的切换是否正常
+- 测试笔记保存和加载时,自定义背景图片是否能正确恢复
+- 测试不同尺寸和比例的图片是否都能正确显示
\ No newline at end of file
diff --git a/src/Notes-master/res/layout/note_edit.xml b/src/Notes-master/res/layout/note_edit.xml
index d60768e..ac9a3d8 100644
--- a/src/Notes-master/res/layout/note_edit.xml
+++ b/src/Notes-master/res/layout/note_edit.xml
@@ -385,6 +385,30 @@
android:visibility="gone"
android:src="@drawable/selected" />
+
+
+
+
+
+
+
+
viewsToAdd = new ArrayList<>();
+ for (String item : items) {
+ if(!TextUtils.isEmpty(item)) {
+ View listItem = getListItem(item, index);
+ viewsToAdd.add(listItem);
+ index++;
+ }
+ }
+
+ // 添加最后一个空列表项
+ lastItem = getListItem("", index);
+ viewsToAdd.add(lastItem);
+
+ // 一次性添加所有视图到容器
+ for (View view : viewsToAdd) {
+ mEditTextList.addView(view);
+ }
+
+ // 设置焦点
+ ListItemViewHolder holder = (ListItemViewHolder) lastItem.getTag();
+ holder.etEditText.requestFocus();
+
+ mNoteEditor.setVisibility(View.GONE); // 隐藏普通编辑器
+ mEditTextList.setVisibility(View.VISIBLE); // 显示清单编辑器
+
+ long endTime = System.currentTimeMillis();
+ Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms");
+ }
+ });
}
- }
- mEditTextList.addView(getListItem("", index)); // 添加最后一个空列表项
- mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); // 设置焦点
-
- mNoteEditor.setVisibility(View.GONE); // 隐藏普通编辑器
- mEditTextList.setVisibility(View.VISIBLE); // 显示清单编辑器
+ }).start();
}
/**
@@ -1306,24 +1351,42 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* @return 带有高亮效果的文本
*/
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
+ long startTime = System.currentTimeMillis();
+
String text = fullText == null ? "" : fullText;
SpannableString spannable = new SpannableString(text);
// 处理查询关键词高亮
- if (!TextUtils.isEmpty(userQuery)) {
- mPattern = Pattern.compile(userQuery); // 编译正则表达式
- Matcher m = mPattern.matcher(text); // 创建匹配器
- int start = 0;
- while (m.find(start)) {
- // 设置高亮背景色
- spannable.setSpan(
- new BackgroundColorSpan(this.getResources().getColor(
- R.color.user_query_highlight)), m.start(), m.end(),
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
- start = m.end();
+ if (!TextUtils.isEmpty(userQuery) && !TextUtils.isEmpty(text)) {
+ try {
+ mPattern = Pattern.compile(userQuery); // 编译正则表达式
+ Matcher m = mPattern.matcher(text); // 创建匹配器
+ int start = 0;
+ int matchCount = 0;
+ while (m.find(start)) {
+ // 设置高亮背景色
+ spannable.setSpan(
+ new BackgroundColorSpan(this.getResources().getColor(
+ R.color.user_query_highlight)), m.start(), m.end(),
+ Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
+ start = m.end();
+ matchCount++;
+
+ // 限制匹配数量,避免处理过多匹配项导致性能问题
+ if (matchCount > 100) {
+ Log.w(TAG, "getHighlightQueryResult: too many matches, stopping at 100");
+ break;
+ }
+ }
+ Log.d(TAG, "getHighlightQueryResult: found " + matchCount + " matches");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in getHighlightQueryResult: " + e.getMessage());
}
}
-
+
+ long endTime = System.currentTimeMillis();
+ Log.d(TAG, "getHighlightQueryResult completed in " + (endTime - startTime) + "ms");
+
return spannable;
}
@@ -1336,11 +1399,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
- final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
+
+ // 创建并缓存ViewHolder
+ ListItemViewHolder holder = new ListItemViewHolder();
+ holder.etEditText = (NoteEditText) view.findViewById(R.id.et_edit_text);
+ holder.cbEditItem = (CheckBox) view.findViewById(R.id.cb_edit_item);
+ view.setTag(holder);
+
+ final NoteEditText edit = holder.etEditText;
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 设置复选框的选中状态变化监听器
- CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
+ CheckBox cb = holder.cbEditItem;
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
@@ -1379,10 +1449,14 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "Wrong index, should not happen");
return;
}
+
+ View view = mEditTextList.getChildAt(index);
+ ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
+
if(hasText) {
- mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
+ holder.cbEditItem.setVisibility(View.VISIBLE);
} else {
- mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
+ holder.cbEditItem.setVisibility(View.GONE);
}
}
@@ -1461,9 +1535,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
- NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
+ ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
+ NoteEditText edit = holder.etEditText;
if (!TextUtils.isEmpty(edit.getText())) {
- if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
+ if (holder.cbEditItem.isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
index 6d5cc26..00c1d98 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
@@ -648,8 +648,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST + " AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
} else {
- // 根文件夹下的笔记模式:显示文件夹、普通笔记和非空通话记录文件夹
- selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" +
+ // 根文件夹下的笔记模式:只显示文件夹、普通笔记和非空通话记录文件夹,不显示清单
+ selection = "(" + NoteColumns.TYPE + " IN (" + Notes.TYPE_NOTE + ", " + Notes.TYPE_FOLDER + ") AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
}
} else {
diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java b/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java
index 46c35f0..140fee5 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NotesListItem.java
@@ -144,6 +144,98 @@ public class NotesListItem extends LinearLayout {
}
}
});
+
+ // 初始化复选框点击监听器
+ mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ // 切换完成状态
+ if (mItemData != null) {
+ mItemData.setChecked(isChecked);
+
+ // 更新UI
+ updateChecklistStyle(isChecked);
+
+ // 通知监听器,保存完成状态到数据库
+ if (mOnCheckStatusChangedListener != null) {
+ mOnCheckStatusChangedListener.onCheckStatusChanged(mItemData.getId(), isChecked);
+ }
+ }
+ }
+ });
+
+ // 初始化长按事件,长按后可编辑
+ mChecklistTitle.setLongClickable(true);
+ mChecklistTitle.setOnLongClickListener(new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ // 设置为可编辑状态
+ mChecklistTitle.setFocusable(true);
+ mChecklistTitle.setFocusableInTouchMode(true);
+ mChecklistTitle.setClickable(true);
+ mChecklistTitle.setCursorVisible(true);
+
+ // 获得焦点并弹出键盘
+ mChecklistTitle.requestFocus();
+ mChecklistTitle.setSelection(mChecklistTitle.getText().length());
+
+ // 强制弹出键盘
+ InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(mChecklistTitle, InputMethodManager.SHOW_FORCED);
+ }
+
+ return true; // 消耗长按事件,避免触发其他长按事件
+ }
+ });
+
+ // 初始化双击事件,双击切换完成状态
+ mChecklistTitle.setClickable(true);
+ mChecklistTitle.setOnClickListener(new OnClickListener() {
+ private long lastClickTime = 0;
+
+ @Override
+ public void onClick(View v) {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastClickTime < 300) {
+ // 双击事件:切换完成状态
+ if (mItemData != null) {
+ boolean isChecked = !mItemData.isChecked();
+ mItemData.setChecked(isChecked);
+
+ // 更新复选框状态
+ mCheckBox.setChecked(isChecked);
+
+ // 更新UI
+ updateChecklistStyle(isChecked);
+
+ // 添加完成特效
+ addCheckEffect();
+
+ // 通知监听器,保存完成状态到数据库
+ if (mOnCheckStatusChangedListener != null) {
+ mOnCheckStatusChangedListener.onCheckStatusChanged(mItemData.getId(), isChecked);
+ }
+ }
+ }
+ lastClickTime = currentTime;
+ }
+ });
+
+ // 初始化焦点变化监听器,失去焦点时保存修改并恢复为不可编辑状态
+ mChecklistTitle.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (!hasFocus) {
+ // 失去焦点时,保存修改并恢复为不可编辑状态
+ mChecklistTitle.setFocusable(false);
+ mChecklistTitle.setFocusableInTouchMode(false);
+ // 保持可点击状态,以便双击事件能够触发
+ mChecklistTitle.setClickable(true);
+ mChecklistTitle.setCursorVisible(false);
+ }
+ }
+ });
}
/**
@@ -229,7 +321,8 @@ public class NotesListItem extends LinearLayout {
// 初始状态下,EditText不可编辑,只能长按后编辑
mChecklistTitle.setFocusable(false);
mChecklistTitle.setFocusableInTouchMode(false);
- mChecklistTitle.setClickable(false);
+ // 保持可点击状态,以便双击事件能够触发
+ mChecklistTitle.setClickable(true);
mChecklistTitle.setCursorVisible(false);
// 根据完成状态设置复选框和样式
@@ -237,62 +330,8 @@ public class NotesListItem extends LinearLayout {
mCheckBox.setChecked(isChecked);
updateChecklistStyle(isChecked);
- // 添加复选框点击事件,切换完成状态
+ // 启用复选框点击事件
mCheckBox.setClickable(true);
- mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- // 切换完成状态
- data.setChecked(isChecked);
-
- // 更新UI
- updateChecklistStyle(isChecked);
-
- // 通知监听器,保存完成状态到数据库
- if (mOnCheckStatusChangedListener != null) {
- mOnCheckStatusChangedListener.onCheckStatusChanged(data.getId(), isChecked);
- }
- }
- });
-
- // 添加长按事件,长按后可编辑
- mChecklistTitle.setLongClickable(true);
- mChecklistTitle.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- // 设置为可编辑状态
- mChecklistTitle.setFocusable(true);
- mChecklistTitle.setFocusableInTouchMode(true);
- mChecklistTitle.setClickable(true);
- mChecklistTitle.setCursorVisible(true);
-
- // 获得焦点并弹出键盘
- mChecklistTitle.requestFocus();
- mChecklistTitle.setSelection(mChecklistTitle.getText().length());
-
- // 强制弹出键盘
- InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.showSoftInput(mChecklistTitle, InputMethodManager.SHOW_FORCED);
- }
-
- return true; // 消耗长按事件,避免触发其他长按事件
- }
- });
-
- // 添加焦点变化监听器,失去焦点时保存修改并恢复为不可编辑状态
- mChecklistTitle.setOnFocusChangeListener(new OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (!hasFocus) {
- // 失去焦点时,保存修改并恢复为不可编辑状态
- mChecklistTitle.setFocusable(false);
- mChecklistTitle.setFocusableInTouchMode(false);
- mChecklistTitle.setClickable(false);
- mChecklistTitle.setCursorVisible(false);
- }
- }
- });
} else {
// 普通模式:显示TextView,隐藏编辑框
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
@@ -352,6 +391,45 @@ public class NotesListItem extends LinearLayout {
}
}
+ /**
+ * 添加完成特效
+ * 为双击完成的清单项目添加视觉反馈
+ */
+ private void addCheckEffect() {
+ // 创建缩放动画
+ android.view.animation.ScaleAnimation scaleAnimation = new android.view.animation.ScaleAnimation(
+ 1.0f, 1.2f, 1.0f, 1.2f, // 从正常大小到放大1.2倍
+ android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f, // 缩放中心X
+ android.view.animation.Animation.RELATIVE_TO_SELF, 0.5f // 缩放中心Y
+ );
+ scaleAnimation.setDuration(200); // 动画持续时间
+ scaleAnimation.setRepeatMode(android.view.animation.Animation.REVERSE); // 反向重复
+ scaleAnimation.setRepeatCount(1); // 重复一次
+
+ // 创建淡入淡出动画
+ android.view.animation.AlphaAnimation alphaAnimation = new android.view.animation.AlphaAnimation(
+ 1.0f, 0.6f // 从完全不透明到半透明
+ );
+ alphaAnimation.setDuration(200); // 动画持续时间
+ alphaAnimation.setRepeatMode(android.view.animation.Animation.REVERSE); // 反向重复
+ alphaAnimation.setRepeatCount(1); // 重复一次
+
+ // 创建动画集
+ android.view.animation.AnimationSet animationSet = new android.view.animation.AnimationSet(true);
+ animationSet.addAnimation(scaleAnimation);
+ animationSet.addAnimation(alphaAnimation);
+
+ // 应用动画到复选框
+ if (mCheckBox != null) {
+ mCheckBox.startAnimation(animationSet);
+ }
+
+ // 应用动画到标题
+ if (mChecklistTitle != null) {
+ mChecklistTitle.startAnimation(animationSet);
+ }
+ }
+
/**
* 设置笔记项的背景
* @param data 笔记项数据