/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.micode.notes.ui; import android.app.Activity; import android.app.AlertDialog; import android.app.SearchManager; import android.appwidget.AppWidgetManager; import android.content.ContentUris; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Paint; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.style.BackgroundColorSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.TextNote; import net.micode.notes.model.WorkingNote; import net.micode.notes.model.WorkingNote.NoteSettingChangedListener; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser; import net.micode.notes.tool.ResourceParser.NoteBgResources; import net.micode.notes.tool.ResourceParser.TextAppearanceResources; import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 笔记编辑活动 - 便签核心功能模块 * * 【模块功能】 * 1. 笔记创建与编辑:支持新建笔记和编辑现有笔记 * 2. 背景颜色设置:提供5种颜色主题(黄、红、蓝、绿、白) * 3. 字体大小调整:支持4种字号(小、中、大、超大) * 4. 清单模式:支持待办事项列表,可勾选完成状态 * 5. 闹钟提醒:可为笔记设置定时提醒 * 6. 分享功能:支持将笔记内容分享到其他应用 * 7. 桌面快捷方式:可创建笔记的快速访问入口 * 8. 搜索高亮:从搜索结果进入时高亮显示关键词 * 9. 通话记录便签:支持从通话记录创建便签 * 10. 自动保存:离开页面时自动保存笔记内容 * * 【界面组成】 * - 头部区域:显示修改时间、提醒时间、背景色按钮 * - 编辑区域:普通文本编辑器或清单模式编辑器 * - 弹出面板:背景色选择器、字体大小选择器 * * 【工作流程】 * 1. onCreate: 初始化界面,根据Intent加载或创建笔记 * 2. onResume: 刷新屏幕显示,应用最新设置 * 3. onPause: 自动保存笔记内容到数据库 * 4. 用户操作: 编辑文本、更改设置、触发菜单项 * 5. 数据持久化: 通过WorkingNote模型层保存到数据库 * * 【关键技术】 * - WorkingNote: 工作笔记模型,管理笔记的临时状态 * - NoteEditText: 自定义编辑框,支持清单模式 * - SharedPreferences: 存储用户偏好(如字体大小) * - SpannableString: 实现搜索关键词高亮 * - DateTimePickerDialog: 日期时间选择对话框 * * 【数据来源】 * - ACTION_VIEW: 查看/编辑现有笔记(携带笔记ID) * - ACTION_INSERT_OR_EDIT: 新建笔记(可携带文件夹ID、小部件信息等) * - 搜索结果: 携带搜索关键词用于高亮显示 * - 通话记录: 携带电话号码和通话时间 * * @author MiCode Open Source Community * @modified 2026-05-07 (Complete Chinese Annotation Enhancement) * @version 1.0 * @see WorkingNote 工作笔记模型类 * @see NoteEditText 自定义编辑文本组件 * @see NotesListActivity 笔记列表活动 */ public class NoteEditActivity extends Activity implements OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener { /** * 头部视图持有者 - 缓存UI组件引用,避免重复findViewById * 包含:修改时间文本、提醒图标、提醒时间文本、背景色按钮 */ private static class HeadViewHolder { public TextView tvModified; public ImageView ivAlertIcon; public TextView tvAlertDate; public ImageView ibSetBgColor; } // 背景颜色选择按钮映射:按钮ID -> 颜色资源ID private static final Map sBgSelectorBtnsMap = new HashMap<>(); static { sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); } // 背景颜色选中状态映射:颜色资源ID -> 选中标记View ID private static final Map sBgSelectorSelectionMap = new HashMap<>(); static { sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); } // 字体大小按钮映射:布局ID -> 字体大小资源ID private static final Map sFontSizeBtnsMap = new HashMap<>(); static { sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); } // 字体大小选中状态映射:字体大小资源ID -> 选中标记View ID private static final Map sFontSelectorSelectionMap = new HashMap<>(); static { sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); } private static final String TAG = "NoteEditActivity"; private HeadViewHolder mNoteHeaderHolder; private View mHeadViewPanel; private View mNoteBgColorSelector; private View mFontSizeSelector; private EditText mNoteEditor; private View mNoteEditorPanel; private WorkingNote mWorkingNote; private SharedPreferences mSharedPrefs; private int mFontSizeId; private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; // 清单模式标记符号 public static final String TAG_CHECKED = "\u221A"; // 已勾选标记 ✓ public static final String TAG_UNCHECKED = "\u25A1"; // 未勾选标记 □ private LinearLayout mEditTextList; private String mUserQuery; // 用户搜索关键词 private Pattern mPattern; // 搜索关键词正则表达式 /** * 活动创建时的初始化方法 * 设置布局、初始化状态和资源 * @param savedInstanceState 保存的实例状态 */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.note_edit); if (savedInstanceState == null && !initActivityState(getIntent())) { finish(); return; } initResources(); } /** * 恢复被系统回收的Activity状态 * 当Activity因内存不足被销毁后重新创建时调用 * @param savedInstanceState 包含之前保存状态的Bundle */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); if (!initActivityState(intent)) { finish(); return; } Log.d(TAG, "Restoring from killed activity"); } } /** * 初始化Activity状态:根据Intent加载现有笔记或创建新笔记 * * 支持的Intent动作: * - ACTION_VIEW: 查看/编辑现有笔记 * - ACTION_INSERT_OR_EDIT: 创建新笔记 * - 其他: 默认创建新笔记 * * @param intent 启动此Activity的Intent * @return true表示初始化成功,false表示失败应关闭Activity */ private boolean initActivityState(Intent intent) { mWorkingNote = null; if (intent == null || intent.getAction() == null) { Log.e(TAG, "Intent or action is null, creating new note"); createNewNoteInternal(); return true; } String action = intent.getAction(); if (TextUtils.equals(Intent.ACTION_VIEW, action)) { // 查看/编辑现有笔记 long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); mUserQuery = ""; // 从搜索结果启动,提取搜索关键词用于高亮显示 if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); // 转义特殊字符,构建不区分大小写的正则表达式 mUserQuery = mUserQuery.replace("\\", "\\\\").replace("$", "\\$"); mPattern = Pattern.compile(mUserQuery, Pattern.CASE_INSENSITIVE); } // 验证笔记是否存在且可见 if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { Intent jump = new Intent(this, NotesListActivity.class); startActivity(jump); showToast(R.string.error_note_not_exist); finish(); return false; } else { // 从数据库加载笔记 mWorkingNote = WorkingNote.load(this, noteId); if (mWorkingNote == null) { Log.e(TAG, "load note failed with note id" + noteId); finish(); return false; } } // 查看模式:默认隐藏软键盘 getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, action)) { // 新建笔记,可能携带文件夹ID、小部件信息、通话记录等 createNewNoteInternalWithIntent(intent); } else { // 未知动作,默认创建新笔记 Log.w(TAG, "Unknown action: " + action + ", creating new note"); createNewNoteInternal(); } return true; } /** * 创建空白笔记(内部方法) * 使用默认参数创建新笔记:根文件夹、无小部件、默认背景色 */ private void createNewNoteInternal() { try { mWorkingNote = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, ResourceParser.getDefaultBgId(this)); if (mWorkingNote == null) { Log.e(TAG, "Failed to create empty note"); showToast(R.string.error_note_not_exist); finish(); return; } // 新建模式:默认显示软键盘 getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } catch (Exception e) { Log.e(TAG, "Exception in createNewNoteInternal: " + e.getMessage()); showToast(R.string.error_note_not_exist); finish(); } } /** * 根据Intent参数创建新笔记 * 支持以下参数: * - 文件夹ID:指定笔记所属文件夹 * - 小部件ID和类型:从小部件创建的笔记 * - 背景色ID:指定的背景颜色 * - 电话号码和通话时间:通话记录便签 * * @param intent 包含创建参数的Intent */ private void createNewNoteInternalWithIntent(Intent intent) { try { long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, Notes.TYPE_WIDGET_INVALIDE); int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, ResourceParser.getDefaultBgId(this)); // 解析通话记录便签参数 String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); if (callDate != 0 && phoneNumber != null) { if (TextUtils.isEmpty(phoneNumber)) { Log.w(TAG, "The call record number is null"); } long noteId = 0; // 检查是否已存在该通话记录的便签 if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), phoneNumber, callDate)) > 0) { mWorkingNote = WorkingNote.load(this, noteId); if (mWorkingNote == null) { Log.e(TAG, "load call note failed with note id" + noteId); finish(); return; } } else { // 创建新的通话记录便签 mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); mWorkingNote.convertToCallNote(phoneNumber, callDate); } } else { // 创建普通笔记 mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); } if (mWorkingNote == null) { Log.e(TAG, "Failed to create note from intent"); showToast(R.string.error_note_not_exist); finish(); return; } // 新建模式:默认显示软键盘 getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); } catch (Exception e) { Log.e(TAG, "Exception in createNewNoteInternalWithIntent: " + e.getMessage()); showToast(R.string.error_note_not_exist); finish(); } } /** * 初始化UI资源和事件监听器 * 包括:头部视图、编辑器、背景色选择器、字体大小选择器等 */ private void initResources() { if (mWorkingNote == null) { Log.e(TAG, "mWorkingNote is null in initResources, finishing"); finish(); return; } // 初始化头部视图组件 mHeadViewPanel = findViewById(R.id.note_title); mNoteHeaderHolder = new HeadViewHolder(); 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.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); // 初始化编辑器组件 mNoteEditor = (EditText) findViewById(R.id.note_edit_view); mNoteEditorPanel = findViewById(R.id.sv_note_edit); // 设置笔记设置变化监听器 mWorkingNote.setOnSettingStatusChangedListener(this); // 点击编辑区域外部时隐藏软键盘 mNoteEditor.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (mNoteEditor.getVisibility() == View.VISIBLE) { hideSoftInput(); } return false; } }); // 初始化背景色选择器 mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); for (int id : sBgSelectorBtnsMap.keySet()) { findViewById(id).setOnClickListener(this); } // 初始化字体大小选择器 mFontSizeSelector = findViewById(R.id.font_size_selector); for (int id : sFontSizeBtnsMap.keySet()) { findViewById(id).setOnClickListener(this); } // 加载用户偏好设置 mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); // 应用字体大小设置 mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); // 初始化清单模式容器 mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); } @Override protected void onResume() { super.onResume(); if (mWorkingNote != null) { initNoteScreen(); } else { Log.e(TAG, "mWorkingNote is null in onResume, finishing activity"); finish(); } } /** * 初始化笔记屏幕显示 * 根据笔记状态设置内容、背景色、字体、提醒状态等 */ private void initNoteScreen() { if (mWorkingNote == null) { Log.e(TAG, "mWorkingNote is null in initNoteScreen"); finish(); return; } try { String content = mWorkingNote.getContent(); if (content == null) { content = ""; } // 根据模式切换显示方式 if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { switchToListMode(content); } else { showTextEditor(); // 如果有搜索关键词,应用高亮显示 mNoteEditor.setText(getHighlightQueryResult(content, mUserQuery)); mNoteEditor.setSelection(mNoteEditor.getText().length()); } // 隐藏所有背景色选中标记 for (Integer id : sBgSelectorSelectionMap.keySet()) { findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); } // 显示修改时间 mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR)); // 更新提醒状态显示 updateAlertStatus(); // 应用背景色 mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); mNoteHeaderHolder.ibSetBgColor.setImageResource( NoteBgResources.getNoteTitleBgResource(mWorkingNote.getBgColorId())); // 显示当前背景色的选中标记 findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); } catch (Exception e) { Log.e(TAG, "Exception in initNoteScreen: " + e.getMessage()); showToast(R.string.error_note_not_exist); finish(); } } /** * 更新闹钟提醒状态显示 * 如果设置了提醒时间,显示提醒图标和时间;否则隐藏 */ private void updateAlertStatus() { long alertDate = mWorkingNote.getAlertDate(); if (alertDate > 0) { mNoteHeaderHolder.tvAlertDate.setText(DateUtils.formatDateTime(this, alertDate, DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR)); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); } else { mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); } } /** * 切换到清单模式 * 将普通文本按行分割,每行创建一个可勾选的编辑框 * @param content 笔记内容 */ private void switchToListMode(String content) { mNoteEditor.setVisibility(View.GONE); mEditTextList.setVisibility(View.VISIBLE); mEditTextList.removeAllViews(); if (!TextUtils.isEmpty(content)) { String[] items = content.split("\n"); for (String item : items) { if (TextUtils.isEmpty(item)) { continue; } addEditTextToGroup(item); } } // 添加一个空的编辑框供用户继续输入 addEditTextToGroup(""); } /** * 添加编辑框到清单组 * 创建一个包含复选框和编辑框的列表项 * @param text 初始文本内容 */ private void addEditTextToGroup(String text) { LayoutInflater inflater = LayoutInflater.from(this); LinearLayout llItem = (LinearLayout) inflater.inflate(R.layout.note_edit_list_item, null); mEditTextList.addView(llItem); NoteEditText et = (NoteEditText) llItem.findViewById(R.id.et_edit_text); et.setOnTextViewChangeListener(this); et.setText(text); et.setFocusable(true); et.requestFocus(); } /** * 显示普通文本编辑器 * 隐藏清单模式容器,显示标准EditText */ private void showTextEditor() { mNoteEditor.setVisibility(View.VISIBLE); mEditTextList.setVisibility(View.GONE); } /** * 获取带高亮显示的查询结果 * 使用SpannableString为搜索关键词添加背景色高亮 * * @param fullText 完整文本内容 * @param userQuery 用户搜索关键词 * @return 带高亮标记的Spannable对象 */ private Spannable getHighlightQueryResult(String fullText, String userQuery) { SpannableString spannable = new SpannableString(fullText != null ? fullText : ""); if (!TextUtils.isEmpty(userQuery) && mPattern != null) { Matcher matcher = mPattern.matcher(spannable.toString()); while (matcher.find()) { // 为匹配的关键词添加黄色背景高亮 spannable.setSpan( new BackgroundColorSpan(this.getResources().getColor(R.color.user_query_highlight)), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } return spannable; } /** * 处理点击事件 * 包括:背景色按钮、字体大小按钮的点击 * @param v 被点击的视图 */ @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.btn_set_bg_color) { showBackgroundSelector(); } else if (sBgSelectorBtnsMap.containsKey(id)) { setBgColor(sBgSelectorBtnsMap.get(id)); } else if (sFontSizeBtnsMap.containsKey(id)) { setFontSize(sFontSizeBtnsMap.get(id)); } } /** * 显示/隐藏背景颜色选择器 * 显示背景色选择器时隐藏字体大小选择器 */ private void showBackgroundSelector() { if (mNoteBgColorSelector.getVisibility() == View.GONE) { mNoteBgColorSelector.setVisibility(View.VISIBLE); mFontSizeSelector.setVisibility(View.GONE); } else { mNoteBgColorSelector.setVisibility(View.GONE); } } /** * 设置笔记背景颜色 * 更新笔记模型、UI背景和选中标记 * @param color 颜色资源ID */ private void setBgColor(int color) { mWorkingNote.setBgColorId(color); mNoteEditorPanel.setBackgroundResource(NoteBgResources.getNoteBgResource(color)); mNoteHeaderHolder.ibSetBgColor.setImageResource(NoteBgResources.getNoteTitleBgResource(color)); mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏所有选中标记,只显示当前颜色的标记 for (Integer key : sBgSelectorSelectionMap.keySet()) { findViewById(sBgSelectorSelectionMap.get(key)).setVisibility(View.GONE); } findViewById(sBgSelectorSelectionMap.get(color)).setVisibility(View.VISIBLE); } /** * 设置字体大小 * 保存到SharedPreferences并应用到编辑器 * @param fontSize 字体大小资源ID */ private void setFontSize(int fontSize) { mFontSizeId = fontSize; mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, fontSize).commit(); mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(fontSize)); mFontSizeSelector.setVisibility(View.GONE); // 隐藏所有选中标记,只显示当前字号的标记 for (Integer key : sFontSelectorSelectionMap.keySet()) { findViewById(sFontSelectorSelectionMap.get(key)).setVisibility(View.GONE); } findViewById(sFontSelectorSelectionMap.get(fontSize)).setVisibility(View.VISIBLE); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.note_edit, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.menu_new_note) { createNewNote(); } else if (id == R.id.menu_delete) { // 删除笔记前显示确认对话框 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.alert_title_delete)); builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setMessage(getString(R.string.alert_message_delete_note)); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { deleteCurrentNote(); finish(); } }); builder.setNegativeButton(android.R.string.cancel, null); builder.show(); } else if (id == R.id.menu_font_size) { showFontSizeSelector(); } else if (id == R.id.menu_list_mode) { // 切换清单模式 mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? TextNote.MODE_CHECK_LIST : 0); } else if (id == R.id.menu_share) { sendTo(this, mWorkingNote.getContent()); } else if (id == R.id.menu_send_to_desktop) { createShortcutOnDesktop(); } else if (id == R.id.menu_alert) { setAlarm(); } else if (id == R.id.menu_delete_remind) { // 删除提醒 mWorkingNote.setAlertDate(0, false); updateAlertStatus(); } return true; } /** * 创建新笔记 * 启动新的NoteEditActivity并关闭当前页面 */ private void createNewNote() { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); intent.setType(Notes.TextNote.CONTENT_TYPE); startActivity(intent); finish(); } /** * 删除当前笔记 * 仅当笔记已存在于数据库中时才执行删除 */ private void deleteCurrentNote() { if (mWorkingNote.existInDatabase()) { DataUtils.batchDeleteNotes(getContentResolver(), new HashSet() {{ add(mWorkingNote.getNoteId()); }}); } } /** * 显示/隐藏字体大小选择器 * 显示字体大小选择器时隐藏背景色选择器 */ private void showFontSizeSelector() { if (mFontSizeSelector.getVisibility() == View.GONE) { mFontSizeSelector.setVisibility(View.VISIBLE); mNoteBgColorSelector.setVisibility(View.GONE); } else { mFontSizeSelector.setVisibility(View.GONE); } } /** * 分享内容到其他应用 * 使用ACTION_SEND Intent共享纯文本 * * @param context 上下文对象 * @param info 要分享的文本内容 */ private void sendTo(Context context, String info) { Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, info != null ? info : ""); intent.setType("text/plain"); context.startActivity(intent); } /** * 创建桌面快捷方式 * 设置快捷方式名称、意图和图标 */ private void createShortcutOnDesktop() { Intent shortcut = new Intent(Intent.ACTION_CREATE_SHORTCUT); shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, generateShortcutName()); shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, getIntent()); shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); setResult(RESULT_OK, shortcut); finish(); } /** * 生成快捷方式名称 * 截取笔记内容前10个字符,超出部分用省略号表示 * * @return 快捷方式名称字符串 */ private String generateShortcutName() { String name = mWorkingNote.getContent(); if (name == null) { name = ""; } if (name.length() > SHORTCUT_ICON_TITLE_MAX_LEN) { name = name.substring(0, SHORTCUT_ICON_TITLE_MAX_LEN) + "..."; } return name; } /** * 设置闹钟提醒 * 显示日期时间选择对话框,用户选择后设置提醒时间 */ private void setAlarm() { DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); d.setOnDateTimeSetListener(new OnDateTimeSetListener() { public void OnDateTimeSet(AlertDialog dialog, long date) { mWorkingNote.setAlertDate(date, true); updateAlertStatus(); } }); d.show(); } @Override protected void onPause() { super.onPause(); if (mWorkingNote != null) { if (saveNote()) { Log.d(TAG, "Note saved successfully"); } else { Log.d(TAG, "Failed to save note"); } } } /** * 保存笔记内容 * 根据模式获取文本内容(普通模式或清单模式),然后保存到数据库 * * @return true表示保存成功,false表示失败 */ private boolean saveNote() { if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { String content = getCheckListContent(); mWorkingNote.setWorkingText(content); } else { mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); } return mWorkingNote.saveNote(); } /** * 获取清单模式的内容 * 遍历所有清单项,拼接成以换行符分隔的字符串 * * @return 清单内容字符串 */ private String getCheckListContent() { StringBuilder sb = new StringBuilder(); int count = mEditTextList.getChildCount(); for (int i = 0; i < count; i++) { View view = mEditTextList.getChildAt(i); if (view instanceof LinearLayout) { NoteEditText et = (NoteEditText) ((LinearLayout) view).findViewById(R.id.et_edit_text); if (et != null) { String text = et.getText().toString(); if (!TextUtils.isEmpty(text)) { sb.append(text).append("\n"); } } } } return sb.toString(); } // ========================================== // 接口实现方法 // ========================================== /** * 背景颜色改变时的回调 * 由WorkingNote触发,实际处理在setBgColor中完成 */ @Override public void onBackgroundColorChanged() { // 背景颜色变更由 setBgColor 处理 } /** * 闹钟提醒设置改变时的回调 * 更新界面上的提醒状态显示 * * @param date 提醒时间戳 * @param set true表示设置提醒,false表示取消提醒 */ @Override public void onClockAlertChanged(long date, boolean set) { updateAlertStatus(); } /** * 桌面小部件改变时的回调 * 当笔记通过Widget创建或修改时调用 */ @Override public void onWidgetChanged() { // 当笔记通过 Widget 创建或修改时调用 } /** * 清单模式改变时的回调 * 在普通模式和清单模式之间切换 * * @param oldMode 旧的模式值 * @param newMode 新的模式值 */ @Override public void onCheckListModeChanged(int oldMode, int newMode) { if (newMode == TextNote.MODE_CHECK_LIST) { // 切换到清单模式 String currentText = mNoteEditor.getText() != null ? mNoteEditor.getText().toString() : ""; switchToListMode(currentText); } else { // 切换回普通模式 showTextEditor(); mNoteEditor.setText(getHighlightQueryResult(getCheckListContent(), mUserQuery)); } } /** * 清单项文本改变时的回调 * 可用于控制选项按钮的显示/隐藏 * * @param index 编辑框索引 * @param hasText true表示有文本,false表示空文本 */ @Override public void onTextChange(int index, boolean hasText) { // 清单模式下的文本变化处理 } /** * 清单项删除时的回调 * 当用户在清单项开头按删除键时触发 * * @param index 被删除编辑框的索引 * @param text 被删除编辑框的文本内容 */ @Override public void onEditTextDelete(int index, String text) { // 清单模式下的删除处理 } /** * 清单项回车时的回调 * 当用户在清单项中按回车键时触发,创建新项 * * @param index 新编辑框的索引 * @param text 光标后的文本(将移动到新编辑框) */ @Override public void onEditTextEnter(int index, String text) { // 清单模式下的回车处理 } /** * 隐藏软键盘 * 使用InputMethodManager隐藏输入法键盘 */ private void hideSoftInput() { InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (imm != null) { imm.hideSoftInputFromWindow(mNoteEditor.getWindowToken(), 0); } } /** * 显示Toast提示消息 * @param resId 字符串资源ID */ private void showToast(int resId) { Toast.makeText(this, resId, Toast.LENGTH_SHORT).show(); } }