From c49887c0f271cdcd75b1d11281dfc15d8294edd8 Mon Sep 17 00:00:00 2001 From: ynmlzdwsp <2727940448@qq.com> Date: Sat, 30 Sep 2023 16:24:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../577f30d26378ec8a2bd2e4a43f3c79b3f04c402c | 76 +++-- .../net/micode/notes/ui/NoteEditActivity.java | 279 ++++++++++++++---- .../net/micode/notes/ui/NoteEditText.java | 28 +- 3 files changed, 271 insertions(+), 112 deletions(-) diff --git a/.idea/sonarlint/issuestore/5/7/577f30d26378ec8a2bd2e4a43f3c79b3f04c402c b/.idea/sonarlint/issuestore/5/7/577f30d26378ec8a2bd2e4a43f3c79b3f04c402c index 76eecf1..9f01f8f 100644 --- a/.idea/sonarlint/issuestore/5/7/577f30d26378ec8a2bd2e4a43f3c79b3f04c402c +++ b/.idea/sonarlint/issuestore/5/7/577f30d26378ec8a2bd2e4a43f3c79b3f04c402c @@ -1,20 +1,20 @@ t -java:S2293W"YReplace the type specification in this constructor call with the diamond operator ("<>").(ӏ +java:S2293Z"YReplace the type specification in this constructor call with the diamond operator ("<>").(ӏ t -java:S2293`"YReplace the type specification in this constructor call with the diamond operator ("<>").( +java:S2293c"YReplace the type specification in this constructor call with the diamond operator ("<>").( t -java:S2293i"YReplace the type specification in this constructor call with the diamond operator ("<>").( +java:S2293l"YReplace the type specification in this constructor call with the diamond operator ("<>").( o -java:S2293q"YReplace the type specification in this constructor call with the diamond operator ("<>").( +java:S2293t"YReplace the type specification in this constructor call with the diamond operator ("<>").( p -java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").(艥 +java:S2293"YReplace the type specification in this constructor call with the diamond operator ("<>").(艥 ? -java:S1604"(Make this anonymous inner class a lambda(ά +java:S1604"(Make this anonymous inner class a lambda(ά ? -java:S1604"(Make this anonymous inner class a lambda( +java:S1604"(Make this anonymous inner class a lambda( ? -java:S1604"(Make this anonymous inner class a lambda(ߒ +java:S1604"(Make this anonymous inner class a lambda(ߒ q java:S1104N"VMake tvModified a static final constant or non-public and provide accessors if needed.(ԣ r @@ -24,47 +24,43 @@ java:S1104R"WMake tvAlertDate a static final constant or non-public and provide s java:S1104T"XMake ibSetBgColor a static final constant or non-public and provide accessors if needed.( n -java:S1450"WRemove the "mPattern" field and declare it as a local variable in the relevant methods.( -n -java:S3776"RRefactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.(ݨ -W -java:S1874"@Remove this use of "SOFT_INPUT_ADJUST_RESIZE"; it is deprecated.(ѯ -\ -java:S1874"@Remove this use of "SOFT_INPUT_ADJUST_RESIZE"; it is deprecated.(ң -O -java:S1874"9Remove this use of "setTextAppearance"; it is deprecated.(( -K -java:S2864"4Iterate over the "entrySet" instead of the "keySet".(ԇ -N -java:S1135"2Complete the task associated to this TODO comment.( -8 -java:S1116"Remove this empty statement.( +java:S1450"WRemove the "mPattern" field and declare it as a local variable in the relevant methods.( +u +java:S3776"RRefactor this method to reduce its Cognitive Complexity from 26 to the 15 allowed.(ݨ81 +^ +java:S1874"@Remove this use of "SOFT_INPUT_ADJUST_RESIZE"; it is deprecated.(ѯ81 +c +java:S1874"@Remove this use of "SOFT_INPUT_ADJUST_RESIZE"; it is deprecated.(ң81 +P +java:S1874"9Remove this use of "setTextAppearance"; it is deprecated.( +R +java:S2864"4Iterate over the "entrySet" instead of the "keySet".(ԇ81 +U +java:S1135"2Complete the task associated to this TODO comment.(81 X -java:S1126"AReplace this if-then-else statement by a single return statement.( +java:S1126"AReplace this if-then-else statement by a single return statement.( 8 -java:S1116"Remove this empty statement.( +java:S1116"Remove this empty statement.( P -java:S1874"9Remove this use of "PreferenceManager"; it is deprecated.( +java:S1874"9Remove this use of "PreferenceManager"; it is deprecated.( Z -java:S1874"CRemove this use of "getDefaultSharedPreferences"; it is deprecated.( -U -java:S1874"9Remove this use of "setTextAppearance"; it is deprecated.( +java:S1874"CRemove this use of "getDefaultSharedPreferences"; it is deprecated.( +\ +java:S1874"9Remove this use of "setTextAppearance"; it is deprecated.(81 f -java:S1874"JDon't override a deprecated method or explicitly mark it as "@Deprecated".( +java:S1874"JDon't override a deprecated method or explicitly mark it as "@Deprecated".( Q -java:S1874"5Remove this use of "onBackPressed"; it is deprecated.( +java:S1874"5Remove this use of "onBackPressed"; it is deprecated.( G -java:S1874"0Remove this use of "getColor"; it is deprecated.( +java:S1874"0Remove this use of "getColor"; it is deprecated.( d -java:S3252"MUse static access with "android.text.Spanned" for "SPAN_INCLUSIVE_EXCLUSIVE".( -U -java:S1874"9Remove this use of "setTextAppearance"; it is deprecated.( +java:S3252"MUse static access with "android.text.Spanned" for "SPAN_INCLUSIVE_EXCLUSIVE".( T -java:S1874"=Remove this use of "EXTRA_SHORTCUT_INTENT"; it is deprecated.(ð +java:S1874"=Remove this use of "EXTRA_SHORTCUT_INTENT"; it is deprecated.(ð R -java:S1874";Remove this use of "EXTRA_SHORTCUT_NAME"; it is deprecated.( +java:S1874";Remove this use of "EXTRA_SHORTCUT_NAME"; it is deprecated.( [ -java:S1874"DRemove this use of "EXTRA_SHORTCUT_ICON_RESOURCE"; it is deprecated.(Ǿ -d java:S100"NRename this method name to match the regular expression '^[a-z][a-zA-Z0-9]*$'.(Ҏ +java:S1874"DRemove this use of "EXTRA_SHORTCUT_ICON_RESOURCE"; it is deprecated.(Ǿ +d java:S100"NRename this method name to match the regular expression '^[a-z][a-zA-Z0-9]*$'.(Ҏ B -java:S1172"+Remove this unused method parameter "view".(Ҏ \ No newline at end of file +java:S1172"+Remove this unused method parameter "view".(Ҏ \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java index 0bbc25d..5e53a5d 100644 --- a/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/app/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -84,6 +84,9 @@ public class NoteEditActivity extends Activity implements OnClickListener, public ImageView ibSetBgColor; } + /** + * 将按钮视图的ID与资源解析器中定义的颜色常量相对应。这样可以方便地根据按钮视图的ID获取到对应的颜色常量。 + */ private static final Map sBgSelectorBtnsMap = new HashMap(); static { sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); @@ -155,6 +158,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, this.setContentView(R.layout.note_edit); if (savedInstanceState == null && !initActivityState(getIntent())) { + //如果初始化失败,则调用 finish() 方法结束当前活动并返回。 finish(); return; } @@ -180,23 +184,20 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private boolean initActivityState(Intent intent) { - /** - * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, - * then jump to the NotesListActivity - */ mWorkingNote = null; + + // 如果用户指定了ACTION_VIEW但没有提供ID,则跳转到NotesListActivity if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); mUserQuery = ""; - /** - * Starting from the searched result - */ + // 从搜索结果开始 if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); } + // 检查笔记在数据库中是否可见 if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { Intent jump = new Intent(this, NotesListActivity.class); startActivity(jump); @@ -211,11 +212,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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, intent.getAction())) { - // New note + } + + // 如果用户指定了ACTION_INSERT_OR_EDIT + else if (TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { + // 新建笔记 long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); @@ -224,7 +230,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, ResourceParser.getDefaultBgId(this)); - // Parse call-record note + // 解析来自通话记录的笔记 String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); if (callDate != 0 && phoneNumber != null) { @@ -243,6 +249,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } else { mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); + // 转换为通话记录类型 mWorkingNote.convertToCallNote(phoneNumber, callDate); } } else { @@ -250,18 +257,25 @@ public class NoteEditActivity extends Activity implements OnClickListener, bgResId); } + // 设置软键盘模式为显示且调整窗口大小 getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } else { + } + + // 未指定动作的意图,错误日志记录,并结束活动 + else { Log.e(TAG, "Intent not specified action, should not support"); finish(); return false; } + + // 设置工作笔记对象的设置状态监听器 mWorkingNote.setOnSettingStatusChangedListener(this); return true; } + @Override protected void onResume() { super.onResume(); @@ -269,52 +283,65 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void initNoteScreen() { - mNoteEditor.setTextAppearance(this, TextAppearanceResources - .getTexAppearanceResource(mFontSizeId)); + // 设置笔记编辑器的文本外观 + mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + + // 如果是待办清单模式,则切换到列表模式 if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { switchToListMode(mWorkingNote.getContent()); } else { + // 设置笔记内容并将光标定位到末尾 mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); mNoteEditor.setSelection(mNoteEditor.getText().length()); } + + // 隐藏所有背景选择器中的选中状态视图 for (Integer id : sBgSelectorSelectionMap.keySet()) { findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); } + + // 设置标题栏和编辑区域的背景 mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + // 设置最近修改时间 mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, - mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME - | DateUtils.FORMAT_SHOW_YEAR)); + mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE + | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR)); /** - * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker - * is not ready + * TODO: 添加设置提醒的菜单。当前禁用它,因为DateTimePicker还未准备好。 */ showAlertHeader(); } + private void showAlertHeader() { + // 如果笔记设置了闹钟提醒 if (mWorkingNote.hasClockAlert()) { long time = System.currentTimeMillis(); + // 如果当前时间已经超过了设置的提醒时间,则显示“已过期” if (time > mWorkingNote.getAlertDate()) { mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); } else { + // 否则,显示距离提醒时间还有多久 mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); } + // 显示提醒时间和提醒图标 mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); } else { + // 如果没有设置闹钟提醒,隐藏提醒时间和提醒图标 mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); - }; + } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); + // 初始化活动状态 initActivityState(intent); } @@ -322,25 +349,27 @@ public class NoteEditActivity extends Activity implements OnClickListener, protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); /** - * For new note without note id, we should firstly save it to - * generate a id. If the editing note is not worth saving, there - * is no id which is equivalent to create new note + * 对于没有note id的新笔记,我们应该先保存它以生成一个id。 + * 如果当前编辑的笔记不值得保存,就没有id,这相当于创建新的笔记。 */ if (!mWorkingNote.existInDatabase()) { saveNote(); } + // 保存工作笔记的id到Bundle中 outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { + // 如果背景颜色选择器可见且点击区域不在选择器内,则隐藏选择器 if (mNoteBgColorSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mNoteBgColorSelector, ev)) { mNoteBgColorSelector.setVisibility(View.GONE); return true; } + // 如果字体大小选择器可见且点击区域不在选择器内,则隐藏选择器 if (mFontSizeSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mFontSizeSelector, ev)) { mFontSizeSelector.setVisibility(View.GONE); @@ -349,8 +378,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, return super.dispatchTouchEvent(ev); } + + /** + * 判断触摸事件的坐标是否在指定视图范围内 + * + * @param view 目标视图 + * @param ev 触摸事件 + * @return 是否在视图范围内 + */ private boolean inRangeOfView(View view, MotionEvent ev) { - int []location = new int[2]; + int[] location = new int[2]; view.getLocationOnScreen(location); int x = location[0]; int y = location[1]; @@ -358,54 +395,65 @@ public class NoteEditActivity extends Activity implements OnClickListener, || ev.getX() > (x + view.getWidth()) || ev.getY() < y || ev.getY() > (y + view.getHeight())) { - return false; - } + return false; + } return true; } + /** + * 初始化资源 + */ private void initResources() { 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); + + // 初始化笔记背景颜色选择器 mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); for (int id : sBgSelectorBtnsMap.keySet()) { ImageView iv = (ImageView) findViewById(id); iv.setOnClickListener(this); } + // 初始化字体大小选择器 mFontSizeSelector = findViewById(R.id.font_size_selector); for (int id : sFontSizeBtnsMap.keySet()) { View view = findViewById(id); view.setOnClickListener(this); - }; + } + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ + + // 修复存储资源ID在共享首选项中的bug if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; } + mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); } @Override protected void onPause() { super.onPause(); - if(saveNote()) { + if (saveNote()) { Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); } clearSettingState(); } + /** + * 更新小部件 + */ private void updateWidget() { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { @@ -418,35 +466,42 @@ public class NoteEditActivity extends Activity implements OnClickListener, } intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - mWorkingNote.getWidgetId() + mWorkingNote.getWidgetId() }); sendBroadcast(intent); setResult(RESULT_OK, intent); } + + /** + * 处理点击事件 + * + * @param v 点击的视图 + */ public void onClick(View v) { int id = v.getId(); if (id == R.id.btn_set_bg_color) { + // 点击了设置背景颜色按钮 mNoteBgColorSelector.setVisibility(View.VISIBLE); - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - - View.VISIBLE); + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); } else if (sBgSelectorBtnsMap.containsKey(id)) { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.GONE); + // 点击了背景颜色选择器中的颜色按钮 + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.GONE); mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); mNoteBgColorSelector.setVisibility(View.GONE); } else if (sFontSizeBtnsMap.containsKey(id)) { + // 点击了字体大小选择器中的大小按钮 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); mFontSizeId = sFontSizeBtnsMap.get(id); mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + // 如果是待办事项模式,则切换到列表模式 getWorkingText(); switchToListMode(mWorkingNote.getContent()); } else { - mNoteEditor.setTextAppearance(this, - TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); } mFontSizeSelector.setVisibility(View.GONE); } @@ -454,29 +509,43 @@ public class NoteEditActivity extends Activity implements OnClickListener, @Override public void onBackPressed() { - if(clearSettingState()) { + if (clearSettingState()) { + // 如果设置状态为打开状态,则清除设置状态 return; } - saveNote(); + saveNote(); // 保存笔记 super.onBackPressed(); } + /** + * 清除设置状态 + * + * @return 是否清除成功 + */ private boolean clearSettingState() { if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { + // 如果背景颜色选择器为可见状态,则隐藏选择器 mNoteBgColorSelector.setVisibility(View.GONE); return true; } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { + // 如果字体大小选择器为可见状态,则隐藏选择器 mFontSizeSelector.setVisibility(View.GONE); return true; } return false; } + + /** + * 当背景颜色改变时的处理方法 + */ public void onBackgroundColorChanged() { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.VISIBLE); + // 设置选中状态的背景颜色按钮为可见 + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); + // 设置笔记编辑区域的背景颜色 mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + // 设置标题栏的背景颜色 mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); } @@ -493,13 +562,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, getMenuInflater().inflate(R.menu.note_edit, menu); } if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + // 如果是待办事项模式,则设置菜单项为切换到普通模式 menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); } else { menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); } if (mWorkingNote.hasClockAlert()) { + // 如果有闹钟提醒,则隐藏设置提醒的菜单项 menu.findItem(R.id.menu_alert).setVisible(false); } else { + // 如果没有闹钟提醒,则隐藏取消提醒的菜单项 menu.findItem(R.id.menu_delete_remind).setVisible(false); } return true; @@ -509,6 +581,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_new_note: + // 创建新笔记 createNewNote(); break; case R.id.menu_delete: @@ -519,6 +592,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + // 删除当前笔记 deleteCurrentNote(); finish(); } @@ -527,24 +601,31 @@ public class NoteEditActivity extends Activity implements OnClickListener, builder.show(); break; case R.id.menu_font_size: + // 显示字体大小选择器 mFontSizeSelector.setVisibility(View.VISIBLE); + // 显示当前选中的字体大小按钮的选中状态 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); break; case R.id.menu_list_mode: + // 切换待办事项和普通模式 mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? TextNote.MODE_CHECK_LIST : 0); break; case R.id.menu_share: + // 分享笔记 getWorkingText(); sendTo(this, mWorkingNote.getContent()); break; case R.id.menu_send_to_desktop: + // 发送到桌面快捷方式 sendToDesktop(); break; case R.id.menu_alert: + // 设置提醒 setReminder(); break; case R.id.menu_delete_remind: + // 取消提醒 mWorkingNote.setAlertDate(0, false); break; default: @@ -553,19 +634,21 @@ public class NoteEditActivity extends Activity implements OnClickListener, return true; } + private void setReminder() { - DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); - d.setOnDateTimeSetListener(new OnDateTimeSetListener() { + // 创建一个日期时间选择对话框 + DateTimePickerDialog dialog = new DateTimePickerDialog(this, System.currentTimeMillis()); + dialog.setOnDateTimeSetListener(new OnDateTimeSetListener() { public void OnDateTimeSet(AlertDialog dialog, long date) { - mWorkingNote.setAlertDate(date , true); + // 设置提醒日期和状态 + mWorkingNote.setAlertDate(date, true); } }); - d.show(); + dialog.show(); } /** - * Share note to apps that support {@link Intent#ACTION_SEND} action - * and {@text/plain} type + * 将笔记分享给支持Intent.ACTION_SEND操作和text/plain类型的应用程序 */ private void sendTo(Context context, String info) { Intent intent = new Intent(Intent.ACTION_SEND); @@ -575,10 +658,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void createNewNote() { - // Firstly, save current editing notes + // 首先保存当前编辑的笔记 saveNote(); - // For safety, start a new NoteEditActivity + // 为了安全起见,启动一个新的NoteEditActivity finish(); Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); @@ -586,7 +669,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, startActivity(intent); } + /** + * 删除当前笔记 + */ private void deleteCurrentNote() { + // 如果笔记存在于数据库中,则进行删除操作 if (mWorkingNote.existInDatabase()) { HashSet ids = new HashSet(); long id = mWorkingNote.getNoteId(); @@ -595,40 +682,50 @@ public class NoteEditActivity extends Activity implements OnClickListener, } else { Log.d(TAG, "Wrong note id, should not happen"); } + // 若非同步模式,则使用 DataUtils.batchDeleteNotes() 删除笔记 if (!isSyncMode()) { if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { Log.e(TAG, "Delete Note error"); } - } else { + } else { // 若为同步模式,则将笔记移动到垃圾箱 if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { Log.e(TAG, "Move notes to trash folder error, should not happens"); } } } + // 标记为已删除 mWorkingNote.markDeleted(true); } + /** + * 判断是否为同步模式 + */ private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + /** + * 当提醒时间发生变化时,设置闹钟 + */ public void onClockAlertChanged(long date, boolean set) { /** * User could set clock to an unsaved note, so before setting the * alert clock, we should save the note first */ + // 如果笔记未保存过,则保存笔记 if (!mWorkingNote.existInDatabase()) { saveNote(); } + // 设置闹钟 if (mWorkingNote.getNoteId() > 0) { Intent intent = new Intent(this, AlarmReceiver.class); intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); showAlertHeader(); - if(!set) { + if(!set) { // 如果设置提醒时间为 false,则取消闹钟 alarmManager.cancel(pendingIntent); - } else { + } else { // 否则,设置闹钟 alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); } } else { @@ -642,30 +739,37 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + /** + * 当小部件发生变化时,更新小部件 + */ public void onWidgetChanged() { updateWidget(); } + /** + * 当删除编辑框中的文字时,更新索引并重新排列编辑框 + */ public void onEditTextDelete(int index, String text) { int childCount = mEditTextList.getChildCount(); + // 若编辑框数目已经为 1,则无法删除 if (childCount == 1) { return; } + // 更新索引 for (int i = index + 1; i < childCount; i++) { - ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) - .setIndex(i - 1); + ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)).setIndex(i - 1); } + // 删除编辑框 mEditTextList.removeViewAt(index); NoteEditText edit = null; if(index == 0) { - edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( - R.id.et_edit_text); + edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(R.id.et_edit_text); } else { - edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( - R.id.et_edit_text); + edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(R.id.et_edit_text); } + // 将待删除的文字添加到前一个编辑框中 int length = edit.length(); edit.append(text); edit.requestFocus(); @@ -674,17 +778,24 @@ public class NoteEditActivity extends Activity implements OnClickListener, public void onEditTextEnter(int index, String text) { /** - * Should not happen, check for debug + * 防止下标越界,打印错误日志 */ if(index > mEditTextList.getChildCount()) { Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); } + // 获取带有数字序号和复选框的 EditText View View view = getListItem(text, index); + + // 在指定下标处添加 EditText View mEditTextList.addView(view, index); + + // 获取新增的 EditText View,并使之获取焦点 NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); edit.requestFocus(); edit.setSelection(0); + + // 重新为所有 EditText View 设置数字序号 for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) .setIndex(i); @@ -692,7 +803,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void switchToListMode(String text) { + // 清空编辑器区域中的所有 EditText View mEditTextList.removeAllViews(); + + // 将文本按行分割,逐行添加 EditText View String[] items = text.split("\n"); int index = 0; for (String item : items) { @@ -701,20 +815,28 @@ public class NoteEditActivity extends Activity implements OnClickListener, index++; } } + + // 添加一个空的 EditText View,用于在末尾添加新的条目 mEditTextList.addView(getListItem("", index)); mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); + // 隐藏编辑器区域,显示列表区域 mNoteEditor.setVisibility(View.GONE); mEditTextList.setVisibility(View.VISIBLE); } private Spannable getHighlightQueryResult(String fullText, String userQuery) { + // 创建一个 SpannableString 对象,用于设置文本高亮效果 SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); + + // 如果用户设置了查询关键词,则将关键词进行高亮处理 if (!TextUtils.isEmpty(userQuery)) { + // 将用户输入的查询关键词编译成正则表达式 mPattern = Pattern.compile(userQuery); Matcher m = mPattern.matcher(fullText); int start = 0; while (m.find(start)) { + // 为匹配到的文本部分添加背景色,以实现高亮效果 spannable.setSpan( new BackgroundColorSpan(this.getResources().getColor( R.color.user_query_highlight)), m.start(), m.end(), @@ -726,12 +848,18 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private View getListItem(String item, int index) { + // 获取带有数字序号和复选框的 EditText View View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); + + // 设置字体样式 edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + + // 获取复选框,并为其设置状态监听器 CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + // 根据复选框的状态,切换文本删除线的显示方式 if (isChecked) { edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { @@ -740,41 +868,55 @@ public class NoteEditActivity extends Activity implements OnClickListener, } }); + // 如果条目以 "[x]" 开头,则设置复选框为选中状态,并在文本中去除 "[x]" if (item.startsWith(TAG_CHECKED)) { cb.setChecked(true); edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); item = item.substring(TAG_CHECKED.length(), item.length()).trim(); + + // 如果条目以 "[ ]" 开头,则设置复选框为未选中状态,并在文本中去除 "[ ]" } else if (item.startsWith(TAG_UNCHECKED)) { cb.setChecked(false); edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); } + // 为 EditText View 设置状态监听器、数字序号和文本内容 edit.setOnTextViewChangeListener(this); edit.setIndex(index); edit.setText(getHighlightQueryResult(item, mUserQuery)); return view; } + + /** + * 根据文本变化情况,控制复选框的可见性 + * @param index 列表项的序号 + * @param hasText 当前列表项是否有文本内容 + */ public void onTextChange(int index, boolean hasText) { if (index >= mEditTextList.getChildCount()) { Log.e(TAG, "Wrong index, should not happen"); return; } - if(hasText) { + if (hasText) { mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); } else { mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); } } + /** + * 切换到待办事项模式或文本模式 + * @param oldMode 原来的模式 + * @param newMode 新的模式 + */ public void onCheckListModeChanged(int oldMode, int newMode) { if (newMode == TextNote.MODE_CHECK_LIST) { switchToListMode(mNoteEditor.getText().toString()); } else { if (!getWorkingText()) { - mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", - "")); + mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", "")); } mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); mEditTextList.setVisibility(View.GONE); @@ -782,6 +924,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + /** + * 获取待办事项列表的文本内容并更新 WorkingNote 对象 + * @return 是否包含已选中的待办事项 + */ private boolean getWorkingText() { boolean hasChecked = false; if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { @@ -815,6 +961,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, * position in the list when back from edit view, while creating a * new node requires to the top of the list. This code * {@link #RESULT_OK} is used to identify the create/edit state + * 有两种模式可以从列表视图切换到编辑视图, + * 打开一个便签,创建/编辑一个便签。 + * 打开便签时需要返回到列表中原来的位置, + * 而创建新的便签则需要返回到列表的顶部。 + * 这段代码使用 RESULT_OK 来标识创建/编辑状态。 */ setResult(RESULT_OK); } diff --git a/app/src/main/java/net/micode/notes/ui/NoteEditText.java b/app/src/main/java/net/micode/notes/ui/NoteEditText.java index 2afe2a8..422e505 100644 --- a/app/src/main/java/net/micode/notes/ui/NoteEditText.java +++ b/app/src/main/java/net/micode/notes/ui/NoteEditText.java @@ -37,15 +37,17 @@ import net.micode.notes.R; import java.util.HashMap; import java.util.Map; -public class NoteEditText extends EditText { +public class NoteEditText extends androidx.appcompat.widget.AppCompatEditText { private static final String TAG = "NoteEditText"; private int mIndex; private int mSelectionStartBeforeDelete; + // 定义常量,用于处理不同的URL Schema private static final String SCHEME_TEL = "tel:" ; private static final String SCHEME_HTTP = "http:" ; private static final String SCHEME_EMAIL = "mailto:" ; + // 定义URL Schema对应的操作资源映射 private static final Map sSchemaActionResMap = new HashMap(); static { sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); @@ -54,23 +56,21 @@ public class NoteEditText extends EditText { } /** - * Call by the {@link NoteEditActivity} to delete or add edit text + * 用于监听文本变化的回调接口 */ public interface OnTextViewChangeListener { /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null + * 当按下删除键(KEYCODE_DEL)且文本为空时,删除当前编辑文本 */ void onEditTextDelete(int index, String text); /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen + * 当按下回车键(KEYCODE_ENTER)时,在当前编辑文本后添加一个新的编辑文本 */ void onEditTextEnter(int index, String text); /** - * Hide or show item option when text change + * 当文本内容发生变化时,控制选项菜单的显示与隐藏 */ void onTextChange(int index, boolean hasText); } @@ -82,10 +82,20 @@ public class NoteEditText extends EditText { mIndex = 0; } + /** + * 设置索引 + * + * @param index 索引值 + */ public void setIndex(int index) { mIndex = index; } + /** + * 设置文本变化监听器 + * + * @param listener 文本变化监听器 + */ public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { mOnTextViewChangeListener = listener; } @@ -205,7 +215,7 @@ public class NoteEditText extends EditText { menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - // goto a new intent + // 执行相应的操作 urls[0].onClick(NoteEditText.this); return true; } @@ -214,4 +224,6 @@ public class NoteEditText extends EditText { } super.onCreateContextMenu(menu); } + } +