From 9119283e011bd7a533fc60c1e2ea321e9aad681e Mon Sep 17 00:00:00 2001 From: Joker21a <1095435669@qq.com> Date: Thu, 18 Jan 2024 20:28:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=87=E6=B3=A8=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/micode/notes/model/WorkingNote.java | 79 ++- .../micode/notes/ui/NotesListActivity.java | 517 +++++++++++++++++- 2 files changed, 588 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/micode/notes/model/WorkingNote.java b/src/main/java/net/micode/notes/model/WorkingNote.java index 7733b4b..f97bbfe 100644 --- a/src/main/java/net/micode/notes/model/WorkingNote.java +++ b/src/main/java/net/micode/notes/model/WorkingNote.java @@ -52,7 +52,7 @@ public class WorkingNote { // Note for the working note private String mPassword; - private Note mNote;//这个在Note里面定义了Note类的基本类型, + //这个在Note里面定义了Note类的基本类型, // Note Id private long mNoteId; // Note content @@ -134,6 +134,14 @@ public class WorkingNote { // New note construct //初始化一个新的操作的Note + /** + * @method: WorkingNote + * @description: 初始化一个新的操作的Note + * @date: 2024/1/6 11:28 + * @author: 周石宇 + * @param: [android.content.Context, long]:[context, folderId] + * @return: + */ private WorkingNote(Context context, long folderId) { mContext = context; mAlertDate = 0;//这里为什么是0还存疑 @@ -191,7 +199,14 @@ public class WorkingNote { } loadNoteData(); } - //基本同上一个函数。值得注意的是这里对数据进行了筛选(通过selection),还对MIME进行了判断后加载 + /** + * @method: loadNoteData + * @description: 基本同上一个函数。值得注意的是这里对数据进行了筛选(通过selection),还对MIME进行了判断后加载 + * @date: 2024/1/6 11:29 + * @author: 周石宇 + * @param: []:[] + * @return: void + */ private void loadNoteData() { Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { @@ -199,15 +214,21 @@ public class WorkingNote { }, null); if (cursor != null) { + // 检查是否有数据 if (cursor.moveToFirst()) { do { + // 获取类型 String type = cursor.getString(DATA_MIME_TYPE_COLUMN); if (DataConstants.NOTE.equals(type)) { + // 获取内容 mContent = cursor.getString(DATA_CONTENT_COLUMN); + // 获取模式 mMode = cursor.getInt(DATA_MODE_COLUMN); + // 获取ID mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); } else if (DataConstants.CALL_NOTE.equals(type)) { + // 设置通话ID mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); } else { Log.d(TAG, "Wrong note type with type:" + type); @@ -221,12 +242,27 @@ public class WorkingNote { } } //简单的初始化函数,一会看一下用法 + + + /** + * @method: createEmptyNote + * @description: 创建一个空的笔记 + * @date: 2024/1/2 10:21 + * @author: 周石宇 + * @param: [android.content.Context, long, int, int, int]:[context, folderId, widgetId, widgetType, defaultBgColorId] + * @return: net.micode.notes.model.WorkingNote + */ public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { + // 创建一个新的WorkingNote实例 WorkingNote note = new WorkingNote(context, folderId); + // 设置笔记的背景颜色 note.setBgColorId(defaultBgColorId); + // 设置笔记的widgetId note.setWidgetId(widgetId); + // 设置笔记的widgetType note.setWidgetType(widgetType); + // 返回新的WorkingNote实例 return note; } @@ -234,9 +270,21 @@ public class WorkingNote { return new WorkingNote(context, id, 0); } //这里涉及了多线程的操作。 + /** + * @method: saveNote + * @description: 保存笔记 + * @date: 2024/1/2 10:23 + * @author: 周石宇 + * @param: []:[] + * @return: boolean + */ public synchronized boolean saveNote() { + + // 如果笔记 worth saving if (isWorthSaving()) { + // 如果笔记不存在数据库中 if (!existInDatabase()) { + // 获取新笔记id if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { Log.e(TAG, "Create new note fail with id:" + mNoteId); return false; @@ -269,6 +317,14 @@ public class WorkingNote { return mNoteId > 0; } + /** + * @method: isWorthSaving + * @description: 判断是否值得保存,即判断是否需要保存到数据库 + * @date: 2024/1/2 10:24 + * @author: 周石宇 + * @param: []:[] + * @return: boolean + */ private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -277,7 +333,15 @@ public class WorkingNote { return true; } } -//这个变量设置是用来记录状态设定是否有发生过更改。 + /** + * @method: setOnSettingStatusChangedListener + * @description: 用来记录状态设定是否有发生过更改。 + * @date: 2024/1/2 10:24 + * @author: 周石宇 + * @param: [net.micode.notes.model.WorkingNote.NoteSettingChangedListener]:[l] + * @return: void + */ + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } @@ -309,7 +373,14 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } -//在EditActivities中使用,用来设定在list时的状态 + /** + * @method: setCheckListMode + * @description: 在EditActivities中使用,用来设定在list时的状态 + * @date: 2024/1/6 11:30 + * @author: 周石宇 + * @param: [int]:[mode] + * @return: void + */ public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java index 08b46bb..b34f580 100644 --- a/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -80,6 +80,14 @@ import java.io.InputStreamReader; import java.util.HashSet; import java.util.Stack; +/** + * @class: NotesListActivity + * @extends: Activity + * @implements: OnClickListener, OnItemLongClickListener + * @description: 主要实现小米便签刚进入界面。该类为一种main Activity,(xml中有定义)覆盖点击事件监听及长按事件监听类。。 + * @author: 王毅 + * @date: 2024/1/5 16:17 + */ public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; @@ -140,6 +148,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; + /** + * @method: onCreate + * @description: 创建Activity,启动其生命周期。完成设置界面样式、初始化资源、且在用户第一次使用App时进行介绍 + * @date: 2024/1/2 10:26 + * @author: 王毅 + * @param: [android.os.Bundle]:[savedInstanceState] + * @return: void + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -152,6 +168,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt setAppInfoFromRawRes(); } + /** + * @method: onActivityResult + * @description: intent返回数据接收函数。形参为请求码、结果码、其他intent返回的数据。主要与NoteEditActivity交互 + * @date: 2024/1/2 10:27 + * @author: 高浏洋 + * @param: [int, int, android.content.Intent]:[requestCode, resultCode, data] + * @return: void + */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK @@ -162,19 +186,34 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: setAppInfoFromRawRes + * @description: 第一次使用App时调用,显示introduction。 + * @date: 2024/1/5 16:18 + * @author: 高浏洋 + * @param: []:[] + * @return: void + */ private void setAppInfoFromRawRes() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { StringBuilder sb = new StringBuilder(); InputStream in = null; try { + // 获取资源文件 in = getResources().openRawResource(R.raw.introduction); if (in != null) { + // 创建输入流读取器 InputStreamReader isr = new InputStreamReader(in); + // 创建缓冲流读取器 BufferedReader br = new BufferedReader(isr); + // 创建字符数组 char [] buf = new char[1024]; + // 定义读取长度 int len = 0; + // 循环读取 while ((len = br.read(buf)) > 0) { + // 将读取的字符串添加到字符串缓冲区 sb.append(buf, 0, len); } } else { @@ -200,6 +239,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt ResourceParser.RED); note.setWorkingText(sb.toString()); if (note.saveNote()) { + // 保存笔记成功,保存添加笔记的偏好设置 sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { Log.e(TAG, "Save introduction note error"); @@ -208,12 +248,28 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: onStart + * @description: onCreate调用后回调,此Activity对用户可见。 + * @date: 2024/1/5 16:19 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ @Override protected void onStart() { super.onStart(); startAsyncNotesListQuery(); } + /** + * @method: initResources + * @description: 初始化资源 + * @date: 2024/1/5 16:20 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); @@ -236,30 +292,58 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mModeCallBack = new ModeCallback(); } + /** + * @class: ModeCallback + * @implements: ListView.MultiChoiceModeListener, OnMenuItemClickListener + * @description: 定义了ActionMode的使用。便签长按事件通过ActionMode执行。 + * 便签长按后出现选择(多个)便签删除的菜单栏,其在ActionBar中显示。 + * @author: 王毅 + * @date: 2024/1/5 16:21 + */ private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; private ActionMode mActionMode; private MenuItem mMoveMenu; + /** + * @method: onCreateActionMode + * @description: ActionMode创建函数,形参为创建的ActionMode及需要通过该ActionMode显示的Menu对象 + * @date: 2024/1/5 16:23 + * @author: 高浏阳 + * @param: [android.view.ActionMode, android.view.Menu]:[mode, menu] + * @return: boolean + */ public boolean onCreateActionMode(ActionMode mode, Menu menu) { getMenuInflater().inflate(R.menu.note_list_options, menu); + // 设置菜单项的点击事件 menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + // 获取移动菜单 mMoveMenu = menu.findItem(R.id.move); + // 如果当前文件夹是通话记录文件夹或者用户文件夹的数量为0,则隐藏移动菜单 if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER || DataUtils.getUserFolderCount(mContentResolver) == 0) { mMoveMenu.setVisible(false); } else { + // 否则显示移动菜单 mMoveMenu.setVisible(true); + // 设置移动菜单的点击事件 mMoveMenu.setOnMenuItemClickListener(this); } + // 保存当前模式 mActionMode = mode; + // 设置列表选择模式 mNotesListAdapter.setChoiceMode(true); + // 设置列表不可点击 mNotesListView.setLongClickable(false); + // 隐藏添加新笔记按钮 mAddNewNote.setVisibility(View.GONE); + // 创建自定义菜单 View customView = LayoutInflater.from(NotesListActivity.this).inflate( R.layout.note_list_dropdown_menu, null); + // 设置自定义菜单 mode.setCustomView(customView); + // 创建下拉菜单 mDropDownMenu = new DropdownMenu(NotesListActivity.this, (Button) customView.findViewById(R.id.selection_menu), R.menu.note_list_dropdown); @@ -274,16 +358,31 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * @method: updateMenu + * @description: 用于进行多选(全选)时菜单栏界面的更新 + * @date: 2024/1/5 16:23 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void updateMenu() { + + + + //获取已选择的项数 int selectedCount = mNotesListAdapter.getSelectedCount(); - // Update dropdown menu + //根据已选择的项数,设置菜单标题 String format = getResources().getString(R.string.menu_select_title, selectedCount); mDropDownMenu.setTitle(format); + //设置菜单中的action_select_all的标题 MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); if (item != null) { + //如果已选择全部,则设置action_select_all的标题为menu_deselect_all if (mNotesListAdapter.isAllSelected()) { item.setChecked(true); item.setTitle(R.string.menu_deselect_all); + //否则设置action_select_all的标题为menu_select_all } else { item.setChecked(false); item.setTitle(R.string.menu_select_all); @@ -291,32 +390,81 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + + /** + * @method: onPrepareActionMode + * @description: //重新刷新菜单栏时调用 + * @date: 2024/1/5 16:26 + * @author: 高浏阳 + * @param: [android.view.ActionMode, android.view.Menu]:[mode, menu] + * @return: boolean + */ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub return false; } + /** + * @method: onActionItemClicked + * @description: 菜单栏点击动作按钮时调用、形参为ActionMode与点击的菜单项 + * @date: 2024/1/5 16:26 + * @author: 高浏阳 + * @param: [android.view.ActionMode, android.view.MenuItem]:[mode, item] + * @return: boolean + */ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub return false; } + /** + * @method: onDestroyActionMode + * @description: 销毁或退出ActionMode时调用 + * @date: 2024/1/5 16:26 + * @author: 高浏阳 + * @param: [android.view.ActionMode]:[mode] + * @return: void + */ public void onDestroyActionMode(ActionMode mode) { mNotesListAdapter.setChoiceMode(false); mNotesListView.setLongClickable(true); mAddNewNote.setVisibility(View.VISIBLE); } + /** + * @method: finishActionMode + * @description: 结束ActionMode时调用 + * @date: 2024/1/5 16:27 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ public void finishActionMode() { mActionMode.finish(); } + /** + * @method: onItemCheckedStateChanged + * @description: 描述一下方法的作用 + * @date: 2024/1/5 16:27 + * @author: 高浏阳 + * @param: [android.view.ActionMode, int, long, boolean]:[mode, position, id, checked] + * @return: void + */ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } + /** + * @method: onMenuItemClick + * @description: 菜单项点击时调用。 + * @date: 2024/1/6 11:04 + * @author: 高浏阳 + * @param: [android.view.MenuItem]:[item] + * @return: boolean + */ public boolean onMenuItemClick(MenuItem item) { if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), @@ -326,21 +474,29 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt int itemId = item.getItemId(); if (itemId == R.id.delete) { + // 创建一个AlertDialog.Builder对象,用于构建AlertDialog AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.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_notes, mNotesListAdapter.getSelectedCount())); + // 设置确定按钮 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - batchDelete(); + batchDelete();//批处理删除 } }); + // 设置取消按钮 builder.setNegativeButton(android.R.string.cancel, null); + // 显示AlertDialog builder.show(); } else if (itemId == R.id.move) { + // 启动移动到文件夹查询 startQueryDestinationFolders(); } else { return false; @@ -349,19 +505,31 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @class: NewNoteOnTouchListener + * @implements: OnTouchListener + * @description: 新便签触摸事件监听类。根据触摸事件(比如手在屏幕滑动)修正视图的位置参数 + * @author: 王毅 + * @date: 2024/1/6 11:05 + */ private class NewNoteOnTouchListener implements OnTouchListener { public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { + //获取当前屏幕的高度 Display display = getWindowManager().getDefaultDisplay(); int screenHeight = display.getHeight(); + //获取新建笔记视图的高度 int newNoteViewHeight = mAddNewNote.getHeight(); + //计算新建笔记视图的起始位置 int start = screenHeight - newNoteViewHeight; + //计算当前点击的y坐标 int eventY = start + (int) event.getY(); /** * Minus TitleBar's height */ + //如果当前状态是子文件夹,则减去标题栏的高度 if (mState == ListEditState.SUB_FOLDER) { eventY -= mTitleBar.getHeight(); start -= mTitleBar.getHeight(); @@ -376,13 +544,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt * also change. This is very bad, just for the UI designer's strong requirement. */ if (event.getY() < (event.getX() * (-0.12) + 94)) { + // 获取最后一个可见的View View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - mNotesListView.getFooterViewsCount()); if (view != null && view.getBottom() > start && (view.getTop() < (start + 94))) { + // 记录Y轴的起始位置 mOriginY = (int) event.getY(); + // 记录Y轴的偏移量 mDispatchY = eventY; + // 设置新的坐标 event.setLocation(event.getX(), mDispatchY); + // 设置偏移量 mDispatch = true; return mNotesListView.dispatchTouchEvent(event); } @@ -390,6 +563,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } case MotionEvent.ACTION_MOVE: { + // 移动时,将Y坐标值增加 if (mDispatch) { mDispatchY += (int) event.getY() - mOriginY; event.setLocation(event.getX(), mDispatchY); @@ -411,6 +585,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }; + /** + * @method: startAsyncNotesListQuery + * @description: 已同步便签查询。当进入一个新文件夹时会向数据库发送查询当前文件夹下的便签请求。返回查询结果。这是一个异步查询进程 + * @date: 2024/1/6 11:06 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ + private void startAsyncNotesListQuery() { String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; @@ -420,20 +603,38 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * @class: BackgroundQueryHandler + * @extends: AsyncQueryHandler + * @description: 背景查询消息处理类。主要功能是将查询后得到的便签在界面显示 + * @author: 王毅 + * @date: 2024/1/6 11:07 + */ + private final class BackgroundQueryHandler extends AsyncQueryHandler { public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); } + /** + * @method: onQueryComplete + * @description: 下面就是查询结束后对返回数据的处理,完成界面更新 + * @date: 2024/1/6 11:08 + * @author: 高浏阳 + * @param: [int, java.lang.Object, android.database.Cursor]:[token, cookie, cursor] + * @return: void + */ @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { case FOLDER_NOTE_LIST_QUERY_TOKEN: + // 更新笔记列表适配器 mNotesListAdapter.changeCursor(cursor); break; case FOLDER_LIST_QUERY_TOKEN: + // 如果查询成功,则显示文件夹列表菜单 if (cursor != null && cursor.getCount() > 0) { - showFolderListMenu(cursor); + showFolderListMenu(cursor);//根据查询出的cursor来显示列表 } else { Log.e(TAG, "Query folder failed"); } @@ -444,27 +645,50 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + + /** + * @method: showFolderListMenu + * @description: 通过游标显示文件夹列表菜单,功能为移动便签至某个文件件 + * @date: 2024/1/6 11:08 + * @author: 高浏阳 + * @param: [android.database.Cursor]:[cursor] + * @return: void + */ private void showFolderListMenu(Cursor cursor) { AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + // 设置标题 builder.setTitle(R.string.menu_title_select_folder); + // 创建一个FoldersListAdapter对象 final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + // 设置适配器,并设置点击事件 builder.setAdapter(adapter, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + // 移动选中的笔记到指定文件夹 DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); + // 弹出提示信息 Toast.makeText( NotesListActivity.this, getString(R.string.format_move_notes_to_folder, mNotesListAdapter.getSelectedCount(), adapter.getFolderName(NotesListActivity.this, which)), Toast.LENGTH_SHORT).show(); + // 结束ActionMode mModeCallBack.finishActionMode(); } }); builder.show(); } + /** + * @method: createNewNote + * @description: 创建新便签。主要是创建intent并启动。 + * @date: 2024/1/7 22:21 + * @author: 高浏洋 + * @param: []:[] + * @return: void + */ private void createNewNote() { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); @@ -484,6 +708,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } + /** + * @method: batchDelete + * @description: 批处理删除。用于多个标签删除时 + * @date: 2024/1/6 11:15 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void batchDelete() { new AsyncTask>() { protected HashSet doInBackground(Void... unused) { @@ -521,6 +753,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } + /** + * @method: deleteFolder + * @description: 文件夹删除功能 + * @date: 2024/1/6 11:16 + * @author: 高浏阳 + * @param: [long]:[folderId] + * @return: void + */ private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); @@ -548,32 +788,67 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: openNode + * @description: 打开便签功能。形参为NoteItemData,其为文件夹或便签的实例。 + * @date: 2024/1/6 11:17 + * @author: 高浏阳 + * @param: [net.micode.notes.ui.NoteItemData]:[data] + * @return: void + */ private void openNode(NoteItemData data) { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, data.getId()); this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); } +/** + * @method openFolder + * @description 打开文件夹方法 + * @date: 2023/12/21 7:54 + * @author: 王毅 + * @param + * @return + */ private void openFolder(NoteItemData data) { + // 设置当前文件夹的ID Long TempId=new Long(mCurrentFolderId); mFolderIdStack.add(TempId); mCurrentFolderId = data.getId(); + // 开始异步查询笔记列表 startAsyncNotesListQuery(); + // 如果当前文件夹的ID等于Notes.ID_CALL_RECORD_FOLDER if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 设置当前状态为CALL_RECORD_FOLDER mState = ListEditState.CALL_RECORD_FOLDER; + // 隐藏添加新笔记按钮 mAddNewNote.setVisibility(View.GONE); } else { + // 否则设置当前状态为SUB_FOLDER mState = ListEditState.SUB_FOLDER; } + // 如果当前文件夹的ID等于Notes.ID_CALL_RECORD_FOLDER if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 设置标题栏文本为call_record_folder_name mTitleBar.setText(R.string.call_record_folder_name); } else { + // 否则设置标题栏文本为data.getSnippet() mTitleBar.setText(data.getSnippet()); } + // 设置标题栏可见 mTitleBar.setVisibility(View.VISIBLE); } + /** + * @method: onClick + * @description: 视图点击事件。实现创建便签。 + * @date: 2024/1/6 11:17 + * @author: 高浏阳 + * @param: [android.view.View]:[v] + * @return: void + */ + public void onClick(View v) { if (v.getId() == R.id.btn_new_note) { selectCreatemode(this); @@ -581,6 +856,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: showSoftInput + * @description: 显示软键盘 + * @date: 2024/1/6 11:17 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void showSoftInput() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { @@ -588,66 +871,111 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: hideSoftInput + * @description: 不显示软键盘 + * @date: 2024/1/6 11:18 + * @author: 高浏阳 + * @param: [android.view.View]:[view] + * @return: void + */ private void hideSoftInput(View view) { + + + + //获取输入法管理服务 InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + //隐藏输入法 inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } + //文件夹新建、查看及重命名功能。 + /** + * @method showCreateOrModifyFolderDialog + * @description 显示创建或修改文件夹对话框 + * @date: 2023/12/21 9:44 + * @author: 王毅 + * @param :[boolean]:[create] + * @return void + */ private void showCreateOrModifyFolderDialog(final boolean create) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); + // 显示输入框 showSoftInput(); if (!create) { if (mFocusNoteDataItem != null) { + // 设置文本框中的文本为当前文件夹的摘要 etName.setText(mFocusNoteDataItem.getSnippet()); + // 设置标题为修改文件夹名称 builder.setTitle(getString(R.string.menu_folder_change_name)); } else { Log.e(TAG, "The long click data item is null"); return; } } else { + // 创建文件夹,清空文本框 etName.setText(""); + // 设置标题为创建文件夹 builder.setTitle(this.getString(R.string.menu_create_folder)); } - + //设置确定按钮 builder.setPositiveButton(android.R.string.ok, null); + //设置取消按钮 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + //隐藏软键盘 hideSoftInput(etName); } }); + //创建对话框 final Dialog dialog = builder.setView(view).show(); + //获取确定按钮 final Button positive = (Button)dialog.findViewById(android.R.id.button1); positive.setOnClickListener(new OnClickListener() { public void onClick(View v) { + //隐藏软键盘 hideSoftInput(etName); + //获取输入框中的文本 String name = etName.getText().toString(); + //检查输入的文本是否已经存在 if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + //如果存在,则提示用户 Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), Toast.LENGTH_LONG).show(); + //将光标移动到输入框的末尾 etName.setSelection(0, etName.length()); return; } + + //如果输入的文本不存在 if (!create) { + //如果输入的文本不为空 if (!TextUtils.isEmpty(name)) { + //创建一个ContentValues对象 ContentValues values = new ContentValues(); + //将输入的文本添加到ContentValues对象中 values.put(NoteColumns.SNIPPET, name); values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); values.put(NoteColumns.LOCAL_MODIFIED, 1); + //更新指定id的记录 mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?", new String[] { String.valueOf(mFocusNoteDataItem.getId()) }); } + //新建文件夹的关键,如果是创建文件夹 } else if (!TextUtils.isEmpty(name)) { //如果在根文件夹中创建 if (mCurrentFolderId == Notes.ID_ROOT_FOLDER){ //创建一个ContentValues对象 ContentValues values = new ContentValues(); + //将输入的文本添加到ContentValues对象中 values.put(NoteColumns.SNIPPET, name); values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + //插入新记录 mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); } //如果在子文件夹中创建 @@ -681,6 +1009,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } public void onTextChanged(CharSequence s, int start, int before, int count) { + // 当输入框内容发生变化时,判断输入框内容是否为空,如果为空,则禁用positive按钮,否则启用positive按钮 if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } else { @@ -695,29 +1024,46 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); } + /** + * @method: onBackPressed + * @description: 用于处理子Acitivity返回父Activity。相当于对按下“返回箭头”的处理。即返回NotesListActivity活动。 + * @date: 2024/1/6 11:18 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ @Override public void onBackPressed() { switch (mState) { case SUB_FOLDER: + // 设置当前文件夹ID为根文件夹ID mCurrentFolderId = mFolderIdStack.pop().longValue(); //mCurrentFolderId = Notes.ID_ROOT_FOLDER; if(mCurrentFolderId == Notes.ID_ROOT_FOLDER){ + // 设置当前状态为NOTE_LIST mState = ListEditState.NOTE_LIST; } else{ mState = ListEditState.SUB_FOLDER; } startAsyncNotesListQuery(); + // 设置标题栏不可见 mTitleBar.setVisibility(View.GONE); break; case CALL_RECORD_FOLDER: + // 设置当前文件夹ID为根文件夹ID mCurrentFolderId = Notes.ID_ROOT_FOLDER; + // 设置当前状态为NOTE_LIST mState = ListEditState.NOTE_LIST; + // 设置添加新笔记按钮可见 mAddNewNote.setVisibility(View.VISIBLE); + // 设置标题栏不可见 mTitleBar.setVisibility(View.GONE); + // 开始异步查询笔记列表 startAsyncNotesListQuery(); break; case NOTE_LIST: + // 调用父类的onBackPressed方法 super.onBackPressed(); break; default: @@ -725,68 +1071,142 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * @method: updateWidget + * @description: 更新小组件 + * @date: 2024/1/6 11:19 + * @author: 高浏阳 + * @param: [int, int]:[appWidgetId, appWidgetType] + * @return: void + */ private void updateWidget(int appWidgetId, int appWidgetType) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + // 判断widget的类型 if (appWidgetType == Notes.TYPE_WIDGET_2X) { + // 设置widget的更新类 intent.setClass(this, NoteWidgetProvider_2x.class); } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + // 设置widget的更新类 intent.setClass(this, NoteWidgetProvider_4x.class); } else { Log.e(TAG, "Unspported widget type"); return; } + // 设置更新widget的id intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId }); + // 发送更新widget的广播 sendBroadcast(intent); + // 设置更新结果 setResult(RESULT_OK, intent); } + + /** + * @class: NotesListActivity + * @extends: + * @description: 实现按文件夹item的上下文菜单的回调函数,查看、删除、修改文件夹名称的浮动菜单栏(ContextMenu)创建(就是长按后出现) + * @author: 王毅 + * @date: 2023/12/23 9:28 + */ private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + //创建ContextMenu,形参为创建的菜单、长按事件绑定的视图、视图元素信息 + + + /** + * @method: onCreateContextMenu + * @description: 构造长按文件夹item的上下文菜单 + * @date: 2023/12/23 9:12 + * @author: 王毅 + * @param: [android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo]:[menu, v, menuInfo] + * @return: void + */ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + + + + // 如果mFocusNoteDataItem不为空 if (mFocusNoteDataItem != null) { + // 设置菜单的标题为mFocusNoteDataItem的摘要 menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + // 添加菜单项,菜单项的ID为MENU_FOLDER_VIEW,文本为R.string.menu_folder_view menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); + // 添加菜单项,菜单项的ID为MENU_FOLDER_DELETE,文本为R.string.menu_folder_delete menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); + // 添加菜单项,菜单项的ID为MENU_FOLDER_CHANGE_NAME,文本为R.string.menu_folder_change_name menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); } } }; + //下面都是对浮动菜单栏的一些函数 + + /** + * @method onContextMenuClosed + * @description 浮动菜单栏关闭 + * @date: 2023/12/21 8:32 + * @author: 王毅 + * param [android.view.Menu]:[menu] + * @return void + */ @Override public void onContextMenuClosed(Menu menu) { + + + + // 移除对mNotesListView的监听 + // 否则,当用户在列表项上右键时,会调用onContextMenuClosed()方法 + + // 移除对mNotesListView的监听 if (mNotesListView != null) { mNotesListView.setOnCreateContextMenuListener(null); } super.onContextMenuClosed(menu); } + //浮动菜单栏点击按钮后事件处理。 @Override public boolean onContextItemSelected(MenuItem item) { + + + // 判断当前焦点数据项是否为空 if (mFocusNoteDataItem == null) { Log.e(TAG, "The long click data item is null"); return false; } + // 根据根据点击菜单项id进行判断 switch (item.getItemId()) { + // 打开文件夹 case MENU_FOLDER_VIEW: openFolder(mFocusNoteDataItem); break; + // 删除文件夹 case MENU_FOLDER_DELETE: + // 实例化一个Builder类 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_folder)); + // 设置确定按钮 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + // 删除文件夹 deleteFolder(mFocusNoteDataItem.getId()); } }); + // 设置取消按钮 builder.setNegativeButton(android.R.string.cancel, null); + // 显示对话框 builder.show(); break; + // 修改文件夹名称 case MENU_FOLDER_CHANGE_NAME: showCreateOrModifyFolderDialog(false); break; @@ -797,17 +1217,31 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + //下面是OptionsMenu菜单创建的有关函数。 + // + /** + * @method: onPrepareOptionsMenu + * @description: 针对不同界面打开不同的选项菜单,OptionsMenu创建 + * @date: 2023/12/23 9:55 + * @author: 王毅 + * @param: [android.view.Menu]:[menu] + * @return: boolean + */ @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); if (mState == ListEditState.NOTE_LIST) { + // 加载note_list菜单 getMenuInflater().inflate(R.menu.note_list, menu); // set sync or sync_cancel + // 设置sync或sync_cancel menu.findItem(R.id.menu_sync).setTitle( GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); } else if (mState == ListEditState.SUB_FOLDER) { + // 加载sub_folder菜单 getMenuInflater().inflate(R.menu.sub_folder, menu); } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + // 加载call_record_folder菜单 getMenuInflater().inflate(R.menu.call_record_folder, menu); } else { Log.e(TAG, "Wrong state:" + mState); @@ -815,39 +1249,73 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * @method: onOptionsItemSelected + * @description: 点击不同的选项菜单栏打开不同的界面 + * @date: 2023/12/24 10:07 + * @author: 王毅 + * @param: [android.view.MenuItem]:[item] + * @return: boolean + */ @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); if (itemId == R.id.menu_new_folder) { + // 创建或修改文件夹 showCreateOrModifyFolderDialog(true); } else if (itemId == R.id.menu_export_text) { + // 导出笔记到文本 exportNoteToText(); } else if (itemId == R.id.menu_sync) { + // 同步笔记 if (isSyncMode()) { if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + // 开始同步 GTaskSyncService.startSync(this); } else { + // 取消同步 GTaskSyncService.cancelSync(this); } } else { + // 开始偏好设置 startPreferenceActivity(); } } else if (itemId == R.id.menu_setting) { + // 开始偏好设置 startPreferenceActivity(); } else if (itemId == R.id.menu_new_note) { + // 创建新笔记 createNewNote(); } else if (itemId == R.id.menu_search) { + // 请求搜索 onSearchRequested(); } return true; } + /** + * @method: onSearchRequested + * @description: 搜索栏功能实现(就是最上面那个搜索便签得的),调用了ui组件 + * @date: 2024/1/6 11:20 + * @author: 高浏阳 + * @param: []:[] + * @return: boolean + */ @Override public boolean onSearchRequested() { startSearch(null, false, null /* appData */, false); return true; } + //下面使用了异步线程类AsynTask。可在类中可以直接进行UI操作,并将后台计算的结果及时的交给UI线程进行UI界面显示。 + /** + * @method: exportNoteToText + * @description: 将便签内容输出为文本 + * @date: 2024/1/6 11:21 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void exportNoteToText() { final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); new AsyncTask() { @@ -860,6 +1328,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override protected void onPostExecute(Integer result) { if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + // 弹出提示框,提示SD卡未卸载 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this .getString(R.string.failed_sdcard_export)); @@ -868,6 +1337,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setPositiveButton(android.R.string.ok, null); builder.show(); } else if (result == BackupUtils.STATE_SUCCESS) { + // 弹出提示框,提示SD卡导出成功 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this .getString(R.string.success_sdcard_export)); @@ -877,6 +1347,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setPositiveButton(android.R.string.ok, null); builder.show(); } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + // 弹出提示框,提示SD卡导出失败 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this .getString(R.string.failed_sdcard_export)); @@ -894,19 +1365,46 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + /** + * @method: startPreferenceActivity + * @description: 用于跳转到保存的设置界面。这个maybe在isSyncMode==0时调用。作用在没进行过保存设置时。 + * @date: 2024/1/6 11:22 + * @author: 高浏阳 + * @param: []:[] + * @return: void + */ private void startPreferenceActivity() { Activity from = getParent() != null ? getParent() : this; Intent intent = new Intent(from, NotesPreferenceActivity.class); from.startActivityIfNeeded(intent, -1); } + /** + * @class: OnListItemClickListener + * @implements OnItemClickListener + * @description: 点击事件监听类 + * @author: 王毅 + * @date: 2024/1/6 11:22 + */ private class OnListItemClickListener implements OnItemClickListener { + + /** + * @method: onItemClick + * @description: 点击事件处理。将点击事件其与视图绑定。 + * @date: 2024/1/6 11:23 + * @author: 高浏阳 + * @param: [android.widget.AdapterView, android.view.View, int, long]:[parent, view, position, id] + * @return: void + */ public void onItemClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { NoteItemData item = ((NotesListItem) view).getItemData(); + // 判断是否处于选择模式 if (mNotesListAdapter.isInChoiceMode()) { + // 如果当前是笔记,则调用选择回调函数 if (item.getType() == Notes.TYPE_NOTE) { + // 减去头部视图的数量 position = position - mNotesListView.getHeaderViewsCount(); mModeCallBack.onItemCheckedStateChanged(null, position, id, !mNotesListAdapter.isSelectedItem(position)); @@ -916,9 +1414,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt switch (mState) { case NOTE_LIST: + // 如果当前是文件夹或者系统文件夹,则打开文件夹 if (item.getType() == Notes.TYPE_FOLDER || item.getType() == Notes.TYPE_SYSTEM) { openFolder(item); + // 如果当前是笔记,则打开笔记 } else if (item.getType() == Notes.TYPE_NOTE) { testNode(item); } else { @@ -927,6 +1427,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; case SUB_FOLDER: case CALL_RECORD_FOLDER: + // 如果当前是笔记,则打开笔记 if (item.getType() == Notes.TYPE_NOTE) { testNode(item); // openNode(item); @@ -963,6 +1464,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * @method onItemLongClick + * @description 处理用户长按控件时的行为 + * @date: 2023/12/21 8:37 + * @author: 王毅 + * @param :[android.widget.AdapterView, android.view.View, int, long]:[parent, view, position, id] + * @return boolean + */ public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData();