diff --git a/src/main/java/net/micode/notes/data/Notes.java b/src/main/java/net/micode/notes/data/Notes.java index b148c5b..69927ad 100644 --- a/src/main/java/net/micode/notes/data/Notes.java +++ b/src/main/java/net/micode/notes/data/Notes.java @@ -247,6 +247,18 @@ public class Notes { *

类型 : TEXT

*/ public static final String NUMERIC_PASSWORD = "numeric_password"; + + /** + * 标题,便签的标题 + *

类型 : TEXT

+ */ + public static final String TITLE = "title"; + + /** + * 标题最大长度 + *

类型 : INTEGER

+ */ + public static final int TITLE_MAX_LENGTH = 50; } /** diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index 4be5e25..7600a45 100644 --- a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -36,7 +36,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final String DB_NAME = "note.db"; // 数据库版本号 - private static final int DB_VERSION = 7; + private static final int DB_VERSION = 8; // 数据库表名定义 public interface TABLE { @@ -432,6 +432,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { oldVersion++; } + if (oldVersion == 7) { + upgradeToV8(db); + oldVersion++; + } + if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); @@ -530,4 +535,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.NUMERIC_PASSWORD + " TEXT NOT NULL DEFAULT ''"); } + + /** + * 将数据库从v7升级到v8 + * 此版本升级添加了标题字段,用于支持便签标题功能 + * @param db SQLite数据库实例 + */ + private void upgradeToV8(SQLiteDatabase db) { + // 为笔记表添加标题字段 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TITLE + + " TEXT NOT NULL DEFAULT ''"); + } } diff --git a/src/main/java/net/micode/notes/model/WorkingNote.java b/src/main/java/net/micode/notes/model/WorkingNote.java index 5b111d3..40bab6a 100644 --- a/src/main/java/net/micode/notes/model/WorkingNote.java +++ b/src/main/java/net/micode/notes/model/WorkingNote.java @@ -128,6 +128,11 @@ public class WorkingNote { */ private String mNumericPassword; + /** + * 标题,便签的标题 + */ + private String mTitle; + /** * 数据投影数组,用于从数据库中查询便签数据 */ @@ -154,7 +159,8 @@ public class WorkingNote { NoteColumns.IS_LOCKED, // 锁定状态 NoteColumns.LOCK_PASSWORD, // 锁定密码 NoteColumns.PASSWORD_TYPE, // 密码类型 - NoteColumns.NUMERIC_PASSWORD // 数字密码 + NoteColumns.NUMERIC_PASSWORD, // 数字密码 + NoteColumns.TITLE // 标题 }; /** @@ -164,7 +170,6 @@ public class WorkingNote { private static final int DATA_CONTENT_COLUMN = 1; // 数据内容列索引 private static final int DATA_MIME_TYPE_COLUMN = 2; // 数据类型列索引 private static final int DATA_MODE_COLUMN = 3; // 数据模式列索引 - /** * 便签投影列索引 */ @@ -178,6 +183,7 @@ public class WorkingNote { private static final int NOTE_LOCK_PASSWORD_COLUMN = 7; // 锁定密码列索引 private static final int NOTE_PASSWORD_TYPE_COLUMN = 8; // 密码类型列索引 private static final int NOTE_NUMERIC_PASSWORD_COLUMN = 9; // 数字密码列索引 + private static final int NOTE_TITLE_COLUMN = 10; // 标题列索引 /** * 构造方法,创建一个新的便签 @@ -234,6 +240,7 @@ public class WorkingNote { mLockPassword = cursor.getString(NOTE_LOCK_PASSWORD_COLUMN); mPasswordType = cursor.getString(NOTE_PASSWORD_TYPE_COLUMN); mNumericPassword = cursor.getString(NOTE_NUMERIC_PASSWORD_COLUMN); + mTitle = cursor.getString(NOTE_TITLE_COLUMN); } cursor.close(); } else { @@ -645,6 +652,25 @@ public class WorkingNote { return mNumericPassword; } + /** + * 设置标题 + * @param title 标题内容 + */ + public void setTitle(String title) { + if (!TextUtils.equals(mTitle, title)) { + mTitle = title; + mNote.setNoteValue(NoteColumns.TITLE, title); + } + } + + /** + * 获取标题 + * @return 标题内容 + */ + public String getTitle() { + return mTitle; + } + /** * 便签设置变化监听器接口 * 用于监听便签设置的变化,如背景颜色、提醒时间、小部件等 diff --git a/src/main/java/net/micode/notes/tool/ResourceParser.java b/src/main/java/net/micode/notes/tool/ResourceParser.java index 402dab1..27c724b 100644 --- a/src/main/java/net/micode/notes/tool/ResourceParser.java +++ b/src/main/java/net/micode/notes/tool/ResourceParser.java @@ -147,6 +147,32 @@ public class ResourceParser { public static int getNoteTitleBgResource(int id) { return BG_EDIT_TITLE_RESOURCES[id]; } + + /** + * 获取便签文本颜色 + *

+ * 根据颜色ID获取对应的文本颜色资源ID + *

+ * + * @param id 颜色ID + * @return 文本颜色资源ID + */ + public static int getNoteTextColor(int id) { + switch (id) { + case ResourceParser.YELLOW: + return R.color.note_text_yellow; + case ResourceParser.BLUE: + return R.color.note_text_blue; + case ResourceParser.WHITE: + return R.color.note_text_white; + case ResourceParser.GREEN: + return R.color.note_text_green; + case ResourceParser.RED: + return R.color.note_text_red; + default: + return R.color.note_text_white; + } + } } /** diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 0b3d568..e6805e3 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -32,6 +32,8 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; +import android.text.TextWatcher; +import android.text.Editable; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.BackgroundColorSpan; @@ -96,6 +98,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, public ImageView ivAlertIcon; // 提醒图标 public TextView tvAlertDate; // 提醒日期显示 public ImageView ibSetBgColor; // 设置背景色按钮 + public TextView tvTitleHint; // 标题提示文字 + public EditText etTitle; // 标题输入框 + public TextView tvTitleCount; // 字符数提示 + public View titleArea; // 标题区域 + public TextView tvWordCountLabel; // 字数统计标签 + public TextView tvWordCount; // 字数统计数字 } /** @@ -335,6 +343,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteEditor.setTextAppearance(this, TextAppearanceResources .getTexAppearanceResource(mFontSizeId)); + // 加载标题内容 + String title = mWorkingNote.getTitle(); + if (title != null && !title.isEmpty()) { + mNoteHeaderHolder.etTitle.setText(title); + mNoteHeaderHolder.tvTitleCount.setText(title.length() + "/" + Notes.NoteColumns.TITLE_MAX_LENGTH); + } else { + mNoteHeaderHolder.etTitle.setText(""); + mNoteHeaderHolder.tvTitleCount.setText("0/" + Notes.NoteColumns.TITLE_MAX_LENGTH); + } + // 根据便签模式显示内容 if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { // 切换到列表模式 @@ -354,6 +372,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 设置头部和编辑器背景 mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + mNoteHeaderHolder.titleArea.setBackgroundResource(mWorkingNote.getTitleBgResId()); + + int textColor = ResourceParser.NoteBgResources.getNoteTextColor(mWorkingNote.getBgColorId()); + mNoteHeaderHolder.tvWordCountLabel.setTextColor(getResources().getColor(textColor)); + mNoteHeaderHolder.tvWordCount.setTextColor(getResources().getColor(textColor)); // 设置修改时间 mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, @@ -363,6 +386,9 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 显示提醒头部 showAlertHeader(); + + // 更新字数统计 + updateWordCount(); } /** @@ -495,6 +521,34 @@ public class NoteEditActivity extends Activity implements OnClickListener, mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); + mNoteHeaderHolder.tvTitleHint = (TextView) findViewById(R.id.tv_title_hint); + mNoteHeaderHolder.etTitle = (EditText) findViewById(R.id.et_title); + mNoteHeaderHolder.etTitle.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + String title = s.toString(); + int length = title.length(); + if (length > Notes.NoteColumns.TITLE_MAX_LENGTH) { + mNoteHeaderHolder.tvTitleCount.setText(length + "/" + Notes.NoteColumns.TITLE_MAX_LENGTH); + mNoteHeaderHolder.tvTitleCount.setTextColor(getResources().getColor(R.color.warning_text)); + } else { + mNoteHeaderHolder.tvTitleCount.setText(length + "/" + Notes.NoteColumns.TITLE_MAX_LENGTH); + mNoteHeaderHolder.tvTitleCount.setTextColor(getResources().getColor(R.color.secondary_text_dark)); + } + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + mNoteHeaderHolder.tvTitleCount = (TextView) findViewById(R.id.tv_title_count); + mNoteHeaderHolder.titleArea = findViewById(R.id.note_title_area); + mNoteHeaderHolder.tvWordCountLabel = (TextView) findViewById(R.id.tv_word_count_label); + mNoteHeaderHolder.tvWordCount = (TextView) findViewById(R.id.tv_word_count); // 初始化编辑器 mNoteEditor = (EditText) findViewById(R.id.note_edit_view); @@ -525,6 +579,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, // 初始化编辑列表 mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); + + // 添加编辑器文本监听器,实现实时字数更新 + mNoteEditor.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + updateWordCount(); + } + + @Override + public void afterTextChanged(Editable s) { + } + }); } /** @@ -648,6 +718,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, View.VISIBLE); mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); + mNoteHeaderHolder.titleArea.setBackgroundResource(mWorkingNote.getTitleBgResId()); + + int textColor = ResourceParser.NoteBgResources.getNoteTextColor(mWorkingNote.getBgColorId()); + mNoteHeaderHolder.tvWordCountLabel.setTextColor(getResources().getColor(textColor)); + mNoteHeaderHolder.tvWordCount.setTextColor(getResources().getColor(textColor)); } /** @@ -1088,11 +1163,42 @@ public class NoteEditActivity extends Activity implements OnClickListener, } else { mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); } + updateWordCount(); return hasChecked; } + private void updateWordCount() { + String content = ""; + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + 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); + if (!TextUtils.isEmpty(edit.getText())) { + sb.append(edit.getText()).append("\n"); + } + } + content = sb.toString(); + } else { + content = mNoteEditor.getText().toString(); + } + + int wordCount = content != null ? content.length() : 0; + mNoteHeaderHolder.tvWordCount.setText(String.valueOf(wordCount)); + + if (wordCount > 5000) { + mNoteHeaderHolder.tvWordCount.setTextColor(getResources().getColor(R.color.warning_text)); + } else if (wordCount > 1000) { + mNoteHeaderHolder.tvWordCount.setTextColor(getResources().getColor(R.color.alert_text)); + } else { + mNoteHeaderHolder.tvWordCount.setTextColor(getResources().getColor(R.color.secondary_text_dark)); + } + } + private boolean saveNote() { getWorkingText(); + String title = mNoteHeaderHolder.etTitle.getText().toString(); + mWorkingNote.setTitle(title); boolean saved = mWorkingNote.saveNote(); if (saved) { /** diff --git a/src/main/java/net/micode/notes/ui/NoteItemData.java b/src/main/java/net/micode/notes/ui/NoteItemData.java index 3f5c1cc..7d2876f 100644 --- a/src/main/java/net/micode/notes/ui/NoteItemData.java +++ b/src/main/java/net/micode/notes/ui/NoteItemData.java @@ -49,6 +49,7 @@ public class NoteItemData { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.IS_LOCKED, + NoteColumns.TITLE, }; /** @@ -111,6 +112,10 @@ public class NoteItemData { * 锁定状态列索引 */ private static final int IS_LOCKED_COLUMN = 14; + /** + * 标题列索引 + */ + private static final int TITLE_COLUMN = 15; /** * 笔记ID @@ -172,6 +177,10 @@ public class NoteItemData { * 锁定状态 */ private boolean mIsLocked; + /** + * 标题 + */ + private String mTitle; /** * 联系人姓名 */ @@ -226,6 +235,7 @@ public class NoteItemData { mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0); + mTitle = cursor.getString(TITLE_COLUMN); mPhoneNumber = ""; // 如果是通话记录文件夹,获取电话号码和联系人姓名 @@ -447,6 +457,14 @@ public class NoteItemData { return mSnippet; } + /** + * 获取标题 + * @return 标题 + */ + public String getTitle() { + return mTitle; + } + /** * 是否有提醒 * @return 是否有提醒 diff --git a/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/main/java/net/micode/notes/ui/NotesListItem.java index c23f4a2..3bf7ff6 100644 --- a/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -137,7 +137,19 @@ public class NotesListItem extends LinearLayout { mAlert.setVisibility(View.GONE); } else { // 普通笔记 - mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + String title = data.getTitle(); + if (title != null && !title.isEmpty()) { + // 如果有标题,显示标题 + mTitle.setText(title); + } else { + // 如果没有标题,显示正文前20个字符 + String snippet = data.getSnippet(); + if (snippet != null && snippet.length() > 20) { + mTitle.setText(snippet.substring(0, 20) + "..."); + } else { + mTitle.setText(snippet); + } + } // 设置提醒图标 if (data.hasAlert()) { mAlert.setImageResource(R.drawable.clock); diff --git a/src/main/res/drawable/bg_word_count_panel.xml b/src/main/res/drawable/bg_word_count_panel.xml new file mode 100644 index 0000000..ed363d4 --- /dev/null +++ b/src/main/res/drawable/bg_word_count_panel.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/layout/note_edit.xml b/src/main/res/layout/note_edit.xml index ac3cad0..26c908f 100644 --- a/src/main/res/layout/note_edit.xml +++ b/src/main/res/layout/note_edit.xml @@ -65,6 +65,53 @@ android:background="@drawable/bg_btn_set_color" /> + + + + + + + + + + + + + + diff --git a/src/main/res/values/colors.xml b/src/main/res/values/colors.xml index 123ffbf..acf4063 100644 --- a/src/main/res/values/colors.xml +++ b/src/main/res/values/colors.xml @@ -17,4 +17,13 @@ #335b5b5b + #FF5252 + #FF9800 + + + #5D4037 + #0D47A1 + #424242 + #2E7D32 + #C62828 diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 2d1f2a6..15bbe25 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -199,4 +199,19 @@ Trash emptied Failed to empty trash + + Title + Please enter title + Title max 50 characters + Title has reached maximum length + Bold + Italic + Normal + + + Word count: + Word count normal + Word count large + Word count too large +