diff --git a/Notes-master/src/net/micode/notes/ui/NoteEditText.java b/Notes-master/src/net/micode/notes/ui/NoteEditText.java index 2afe2a8..47dc8e4 100644 --- a/Notes-master/src/net/micode/notes/ui/NoteEditText.java +++ b/Notes-master/src/net/micode/notes/ui/NoteEditText.java @@ -37,15 +37,21 @@ import net.micode.notes.R; import java.util.HashMap; import java.util.Map; +/** + * 自定义编辑文本视图,用于笔记应用中,提供增强的文本编辑功能 + * 包括处理链接、特殊按键事件以及与笔记编辑活动的交互 + */ public class NoteEditText extends EditText { private static final String TAG = "NoteEditText"; - private int mIndex; - private int mSelectionStartBeforeDelete; + private int mIndex; // 当前编辑文本在笔记中的索引位置 + private int mSelectionStartBeforeDelete; // 删除操作前的选择起始位置 - private static final String SCHEME_TEL = "tel:" ; - private static final String SCHEME_HTTP = "http:" ; - private static final String SCHEME_EMAIL = "mailto:" ; + // 支持的URL链接协议前缀 + private static final String SCHEME_TEL = "tel:"; // 电话号码链接 + private static final String SCHEME_HTTP = "http:"; // HTTP链接 + private static final String SCHEME_EMAIL = "mailto:"; // 邮件链接 + // 链接协议与对应的菜单项文本资源ID的映射 private static final Map sSchemaActionResMap = new HashMap(); static { sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); @@ -54,56 +60,89 @@ public class NoteEditText extends EditText { } /** - * Call by the {@link NoteEditActivity} to delete or add edit text + * 文本视图变化监听器接口,由NoteEditActivity实现 + * 用于处理编辑文本的删除、添加和文本变化事件 */ public interface OnTextViewChangeListener { /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null + * 当按下删除键且文本为空时,删除当前编辑文本 + * @param index 当前编辑文本的索引 + * @param text 当前编辑文本的内容 */ void onEditTextDelete(int index, String text); /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen + * 当按下回车键时,在当前编辑文本后添加新的编辑文本 + * @param index 新编辑文本的索引 + * @param text 新编辑文本的初始内容 */ void onEditTextEnter(int index, String text); /** - * Hide or show item option when text change + * 当文本内容变化时,隐藏或显示菜单项 + * @param index 当前编辑文本的索引 + * @param hasText 是否有文本内容 */ void onTextChange(int index, boolean hasText); } - private OnTextViewChangeListener mOnTextViewChangeListener; + private OnTextViewChangeListener mOnTextViewChangeListener; // 文本变化监听器 + /** + * 构造函数,用于代码中创建视图 + * @param context 应用上下文 + */ public NoteEditText(Context context) { super(context, null); mIndex = 0; } + /** + * 设置当前编辑文本在笔记中的索引位置 + * @param index 索引位置 + */ public void setIndex(int index) { mIndex = index; } + /** + * 设置文本视图变化监听器 + * @param listener 监听器实例 + */ public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { mOnTextViewChangeListener = listener; } + /** + * 构造函数,用于XML布局文件中创建视图 + * @param context 应用上下文 + * @param attrs 属性集合 + */ public NoteEditText(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.editTextStyle); } + /** + * 构造函数,用于XML布局文件中创建视图并指定默认样式 + * @param context 应用上下文 + * @param attrs 属性集合 + * @param defStyle 默认样式资源ID + */ public NoteEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); // TODO Auto-generated constructor stub } + /** + * 处理触摸事件,重写以确保正确设置文本选择位置 + * @param event 触摸事件 + * @return 是否消费事件 + */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - + // 计算触摸点在文本中的偏移位置 int x = (int) event.getX(); int y = (int) event.getY(); x -= getTotalPaddingLeft(); @@ -111,6 +150,7 @@ public class NoteEditText extends EditText { x += getScrollX(); y += getScrollY(); + // 获取触摸点对应的文本位置并设置选择 Layout layout = getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); @@ -121,15 +161,23 @@ public class NoteEditText extends EditText { return super.onTouchEvent(event); } + /** + * 处理按键按下事件 + * @param keyCode 按键代码 + * @param event 按键事件 + * @return 是否消费事件 + */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_ENTER: + // 当按下回车键时,如果设置了监听器,则由监听器处理 if (mOnTextViewChangeListener != null) { return false; } break; case KeyEvent.KEYCODE_DEL: + // 记录删除操作前的选择起始位置,用于后续判断是否删除整个视图 mSelectionStartBeforeDelete = getSelectionStart(); break; default: @@ -138,11 +186,19 @@ public class NoteEditText extends EditText { return super.onKeyDown(keyCode, event); } + /** + * 处理按键释放事件 + * @param keyCode 按键代码 + * @param event 按键事件 + * @return 是否消费事件 + */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode) { case KeyEvent.KEYCODE_DEL: + // 处理删除键释放事件 if (mOnTextViewChangeListener != null) { + // 当光标在文本开头且不是第一个编辑文本时,删除当前编辑文本 if (0 == mSelectionStartBeforeDelete && mIndex != 0) { mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); return true; @@ -152,10 +208,13 @@ public class NoteEditText extends EditText { } break; case KeyEvent.KEYCODE_ENTER: + // 处理回车键释放事件 if (mOnTextViewChangeListener != null) { + // 获取当前光标位置,将文本分割为两部分 int selectionStart = getSelectionStart(); String text = getText().subSequence(selectionStart, length()).toString(); setText(getText().subSequence(0, selectionStart)); + // 在当前位置后添加新的编辑文本 mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); } else { Log.d(TAG, "OnTextViewChangeListener was not seted"); @@ -167,9 +226,16 @@ public class NoteEditText extends EditText { return super.onKeyUp(keyCode, event); } + /** + * 处理焦点变化事件 + * @param focused 是否获得焦点 + * @param direction 焦点移动方向 + * @param previouslyFocusedRect 之前焦点所在的矩形区域 + */ @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (mOnTextViewChangeListener != null) { + // 根据焦点变化和文本内容,通知监听器更新菜单项显示 if (!focused && TextUtils.isEmpty(getText())) { mOnTextViewChangeListener.onTextChange(mIndex, false); } else { @@ -179,8 +245,13 @@ public class NoteEditText extends EditText { super.onFocusChanged(focused, direction, previouslyFocusedRect); } + /** + * 创建上下文菜单,用于处理链接点击 + * @param menu 上下文菜单 + */ @Override protected void onCreateContextMenu(ContextMenu menu) { + // 检查选中文本是否包含URL链接 if (getText() instanceof Spanned) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -188,9 +259,11 @@ public class NoteEditText extends EditText { int min = Math.min(selStart, selEnd); int max = Math.max(selStart, selEnd); + // 获取选中区域内的URL链接 final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); if (urls.length == 1) { int defaultResId = 0; + // 根据链接协议确定菜单项文本 for(String schema: sSchemaActionResMap.keySet()) { if(urls[0].getURL().indexOf(schema) >= 0) { defaultResId = sSchemaActionResMap.get(schema); @@ -202,10 +275,11 @@ public class NoteEditText extends EditText { defaultResId = R.string.note_link_other; } + // 添加菜单项并设置点击监听器 menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { - // goto a new intent + // 点击菜单项时,调用URLSpan的onClick方法打开链接 urls[0].onClick(NoteEditText.this); return true; } diff --git a/Notes-master/src/net/micode/notes/ui/NoteItemData.java b/Notes-master/src/net/micode/notes/ui/NoteItemData.java index 0f5a878..8e61ab8 100644 --- a/Notes-master/src/net/micode/notes/ui/NoteItemData.java +++ b/Notes-master/src/net/micode/notes/ui/NoteItemData.java @@ -26,22 +26,28 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; +/** + * 笔记项数据模型类,用于存储和管理单个笔记的相关数据 + * 包含从数据库查询的笔记信息以及位置状态信息 + */ public class NoteItemData { + // 从数据库查询笔记数据时使用的投影列 static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, - NoteColumns.HAS_ATTACHMENT, - NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, - NoteColumns.PARENT_ID, - NoteColumns.SNIPPET, - NoteColumns.TYPE, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, + NoteColumns.ID, // 笔记ID + NoteColumns.ALERTED_DATE, // 提醒日期 + NoteColumns.BG_COLOR_ID, // 背景颜色ID + NoteColumns.CREATED_DATE, // 创建日期 + NoteColumns.HAS_ATTACHMENT, // 是否有附件 + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.NOTES_COUNT, // 笔记数量(文件夹类型时使用) + NoteColumns.PARENT_ID, // 父文件夹ID + NoteColumns.SNIPPET, // 摘要内容 + NoteColumns.TYPE, // 类型(笔记/文件夹) + NoteColumns.WIDGET_ID, // 桌面小部件ID + NoteColumns.WIDGET_TYPE, // 桌面小部件类型 }; + // 投影列索引常量,用于从Cursor中获取数据 private static final int ID_COLUMN = 0; private static final int ALERTED_DATE_COLUMN = 1; private static final int BG_COLOR_ID_COLUMN = 2; @@ -55,28 +61,38 @@ public class NoteItemData { private static final int WIDGET_ID_COLUMN = 10; private static final int WIDGET_TYPE_COLUMN = 11; - private long mId; - private long mAlertDate; - private int mBgColorId; - private long mCreatedDate; - private boolean mHasAttachment; - private long mModifiedDate; - private int mNotesCount; - private long mParentId; - private String mSnippet; - private int mType; - private int mWidgetId; - private int mWidgetType; - private String mName; - private String mPhoneNumber; - - private boolean mIsLastItem; - private boolean mIsFirstItem; - private boolean mIsOnlyOneItem; - private boolean mIsOneNoteFollowingFolder; - private boolean mIsMultiNotesFollowingFolder; - + // 笔记基本属性 + private long mId; // 笔记ID + private long mAlertDate; // 提醒日期 + private int mBgColorId; // 背景颜色ID + private long mCreatedDate; // 创建日期 + private boolean mHasAttachment; // 是否有附件 + private long mModifiedDate; // 修改日期 + private int mNotesCount; // 笔记数量(文件夹类型时使用) + private long mParentId; // 父文件夹ID + private String mSnippet; // 摘要内容 + private int mType; // 类型(笔记/文件夹) + private int mWidgetId; // 桌面小部件ID + private int mWidgetType; // 桌面小部件类型 + + // 通话记录相关属性 + private String mName; // 联系人名称 + private String mPhoneNumber; // 电话号码 + + // 位置状态属性,用于UI显示 + private boolean mIsLastItem; // 是否为列表最后一项 + private boolean mIsFirstItem; // 是否为列表第一项 + private boolean mIsOnlyOneItem; // 是否为列表中唯一一项 + private boolean mIsOneNoteFollowingFolder; // 是否为文件夹后的第一个笔记 + private boolean mIsMultiNotesFollowingFolder; // 是否为文件夹后的多个笔记之一 + + /** + * 构造函数,从Cursor中读取数据并初始化笔记项 + * @param context 应用上下文 + * @param cursor 包含笔记数据的Cursor对象 + */ public NoteItemData(Context context, Cursor cursor) { + // 从Cursor中读取基本笔记信息 mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); @@ -86,18 +102,26 @@ public class NoteItemData { mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); mParentId = cursor.getLong(PARENT_ID_COLUMN); mSnippet = cursor.getString(SNIPPET_COLUMN); + + // 清理摘要内容中的标记 mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); + // 初始化通话记录相关信息 mPhoneNumber = ""; + // 如果是通话记录文件夹中的笔记 if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + // 获取通话记录的电话号码 mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) { + // 获取联系人名称 mName = Contact.getContact(context, mPhoneNumber); if (mName == null) { + // 如果没有联系人名称,使用电话号码代替 mName = mPhoneNumber; } } @@ -106,27 +130,38 @@ public class NoteItemData { if (mName == null) { mName = ""; } + + // 检查并设置笔记在列表中的位置状态 checkPostion(cursor); } + /** + * 检查并设置笔记在列表中的位置状态 + * @param cursor 包含笔记数据的Cursor对象 + */ private void checkPostion(Cursor cursor) { + // 设置基本位置状态 mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false; mIsOnlyOneItem = (cursor.getCount() == 1); mIsMultiNotesFollowingFolder = false; mIsOneNoteFollowingFolder = false; + // 如果是笔记类型且不是列表中的第一项 if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { int position = cursor.getPosition(); if (cursor.moveToPrevious()) { + // 检查前一项是否为文件夹或系统类型 if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { + // 判断当前笔记是文件夹后的第一个笔记还是多个笔记之一 if (cursor.getCount() > (position + 1)) { mIsMultiNotesFollowingFolder = true; } else { mIsOneNoteFollowingFolder = true; } } + // 恢复Cursor位置 if (!cursor.moveToNext()) { throw new IllegalStateException("cursor move to previous but can't move back"); } @@ -134,90 +169,181 @@ public class NoteItemData { } } + // 以下是各种状态和属性的访问器方法 + + /** + * 判断是否为文件夹后的第一个笔记 + * @return 如果是返回true,否则返回false + */ public boolean isOneFollowingFolder() { return mIsOneNoteFollowingFolder; } + /** + * 判断是否为文件夹后的多个笔记之一 + * @return 如果是返回true,否则返回false + */ public boolean isMultiFollowingFolder() { return mIsMultiNotesFollowingFolder; } + /** + * 判断是否为列表中的最后一项 + * @return 如果是返回true,否则返回false + */ public boolean isLast() { return mIsLastItem; } + /** + * 获取通话记录的联系人名称 + * @return 联系人名称 + */ public String getCallName() { return mName; } + /** + * 判断是否为列表中的第一项 + * @return 如果是返回true,否则返回false + */ public boolean isFirst() { return mIsFirstItem; } + /** + * 判断是否为列表中唯一一项 + * @return 如果是返回true,否则返回false + */ public boolean isSingle() { return mIsOnlyOneItem; } + /** + * 获取笔记ID + * @return 笔记ID + */ public long getId() { return mId; } + /** + * 获取提醒日期 + * @return 提醒日期 + */ public long getAlertDate() { return mAlertDate; } + /** + * 获取创建日期 + * @return 创建日期 + */ public long getCreatedDate() { return mCreatedDate; } + /** + * 判断是否有附件 + * @return 如果有附件返回true,否则返回false + */ public boolean hasAttachment() { return mHasAttachment; } + /** + * 获取修改日期 + * @return 修改日期 + */ public long getModifiedDate() { return mModifiedDate; } + /** + * 获取背景颜色ID + * @return 背景颜色ID + */ public int getBgColorId() { return mBgColorId; } + /** + * 获取父文件夹ID + * @return 父文件夹ID + */ public long getParentId() { return mParentId; } + /** + * 获取笔记数量(文件夹类型时使用) + * @return 笔记数量 + */ public int getNotesCount() { return mNotesCount; } + /** + * 获取文件夹ID + * @return 文件夹ID + */ public long getFolderId () { return mParentId; } + /** + * 获取笔记类型 + * @return 笔记类型 + */ public int getType() { return mType; } + /** + * 获取桌面小部件类型 + * @return 桌面小部件类型 + */ public int getWidgetType() { return mWidgetType; } + /** + * 获取桌面小部件ID + * @return 桌面小部件ID + */ public int getWidgetId() { return mWidgetId; } + /** + * 获取笔记摘要内容 + * @return 笔记摘要内容 + */ public String getSnippet() { return mSnippet; } + /** + * 判断是否有提醒 + * @return 如果有提醒返回true,否则返回false + */ public boolean hasAlert() { return (mAlertDate > 0); } + /** + * 判断是否为通话记录 + * @return 如果是通话记录返回true,否则返回false + */ public boolean isCallRecord() { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } + /** + * 静态方法,从Cursor中获取笔记类型 + * @param cursor 包含笔记数据的Cursor对象 + * @return 笔记类型 + */ public static int getNoteType(Cursor cursor) { return cursor.getInt(TYPE_COLUMN); } diff --git a/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/Notes-master/src/net/micode/notes/ui/NotesListActivity.java index e843aec..a870aee 100644 --- a/Notes-master/src/net/micode/notes/ui/NotesListActivity.java +++ b/Notes-master/src/net/micode/notes/ui/NotesListActivity.java @@ -78,60 +78,52 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashSet; +/** + * 笔记列表活动类,负责显示笔记和文件夹列表 + * 处理笔记的创建、删除、移动等操作 + * 支持文件夹导航和上下文菜单 + */ public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; - - private static final int FOLDER_LIST_QUERY_TOKEN = 1; - - private static final int MENU_FOLDER_DELETE = 0; - - private static final int MENU_FOLDER_VIEW = 1; - - private static final int MENU_FOLDER_CHANGE_NAME = 2; - + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; // 笔记列表查询令牌 + private static final int FOLDER_LIST_QUERY_TOKEN = 1; // 文件夹列表查询令牌 + private static final int MENU_FOLDER_DELETE = 0; // 文件夹删除菜单项ID + private static final int MENU_FOLDER_VIEW = 1; // 文件夹查看菜单项ID + private static final int MENU_FOLDER_CHANGE_NAME = 2; // 文件夹重命名菜单项ID + + // 首次使用应用的偏好设置键 private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; - + + // 列表编辑状态枚举 private enum ListEditState { - NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER + NOTE_LIST, // 根笔记列表状态 + SUB_FOLDER, // 子文件夹状态 + CALL_RECORD_FOLDER // 通话记录文件夹状态 }; - - private ListEditState mState; - - private BackgroundQueryHandler mBackgroundQueryHandler; - - private NotesListAdapter mNotesListAdapter; - - private ListView mNotesListView; - - private Button mAddNewNote; - - private boolean mDispatch; - - private int mOriginY; - - private int mDispatchY; - - private TextView mTitleBar; - - private long mCurrentFolderId; - - private ContentResolver mContentResolver; - - private ModeCallback mModeCallBack; - - private static final String TAG = "NotesListActivity"; - - public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; - - private NoteItemData mFocusNoteDataItem; - + + private ListEditState mState; // 当前列表编辑状态 + private BackgroundQueryHandler mBackgroundQueryHandler;// 后台查询处理器 + private NotesListAdapter mNotesListAdapter; // 笔记列表适配器 + private ListView mNotesListView; // 笔记列表视图 + private Button mAddNewNote; // 添加新笔记按钮 + private boolean mDispatch; // 触摸事件分发标志 + private int mOriginY; // 触摸事件原始Y坐标 + private int mDispatchY; // 触摸事件分发Y坐标 + private TextView mTitleBar; // 标题栏文本 + private long mCurrentFolderId; // 当前文件夹ID + private ContentResolver mContentResolver; // 内容解析器 + private ModeCallback mModeCallBack; // 操作模式回调 + private static final String TAG = "NotesListActivity"; // 日志标签 + public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;// 列表视图滚动速率 + private NoteItemData mFocusNoteDataItem; // 聚焦的笔记数据项 + + // 查询条件常量 private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; - private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; - + + // 活动结果请求码 private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; @@ -140,15 +132,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt super.onCreate(savedInstanceState); setContentView(R.layout.note_list); initResources(); - - /** - * Insert an introduction when user firstly use this application - */ + + // 首次使用应用时插入介绍笔记 setAppInfoFromRawRes(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // 处理打开或新建笔记的结果 if (resultCode == RESULT_OK && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { mNotesListAdapter.changeCursor(null); @@ -157,6 +148,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 从原始资源文件读取应用介绍并保存为笔记 + */ private void setAppInfoFromRawRes() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { @@ -165,6 +159,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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]; @@ -184,12 +179,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt try { in.close(); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } } + // 创建并保存介绍笔记 WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, ResourceParser.RED); @@ -206,23 +201,37 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override protected void onStart() { super.onStart(); + // 启动异步笔记列表查询 startAsyncNotesListQuery(); } + /** + * 初始化资源和视图 + */ private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); mCurrentFolderId = Notes.ID_ROOT_FOLDER; mNotesListView = (ListView) findViewById(R.id.notes_list); + + // 添加列表页脚 mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), null, false); + + // 设置列表项点击和长按监听器 mNotesListView.setOnItemClickListener(new OnListItemClickListener()); mNotesListView.setOnItemLongClickListener(this); + + // 设置列表适配器 mNotesListAdapter = new NotesListAdapter(this); mNotesListView.setAdapter(mNotesListAdapter); + + // 初始化添加新笔记按钮 mAddNewNote = (Button) findViewById(R.id.btn_new_note); mAddNewNote.setOnClickListener(this); mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + + // 初始化状态变量 mDispatch = false; mDispatchY = 0; mOriginY = 0; @@ -231,14 +240,25 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mModeCallBack = new ModeCallback(); } + /** + * 操作模式回调类,处理多选模式下的操作 + */ private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { - private DropdownMenu mDropDownMenu; - private ActionMode mActionMode; + private DropdownMenu mDropDownMenu; // 下拉菜单 + private ActionMode mActionMode; // 操作模式 + + // 操作菜单项,用于移动笔记 private MenuItem mMoveMenu; + /** + * 创建操作模式时调用 + */ public boolean onCreateActionMode(ActionMode mode, Menu menu) { + // Inflate菜单资源 getMenuInflater().inflate(R.menu.note_list_options, menu); menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + + // 初始化移动菜单项 mMoveMenu = menu.findItem(R.id.move); if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER || DataUtils.getUserFolderCount(mContentResolver) == 0) { @@ -247,11 +267,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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); @@ -264,14 +286,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt updateMenu(); return true; } - }); return true; } + /** + * 更新菜单状态 + */ private void updateMenu() { int selectedCount = mNotesListAdapter.getSelectedCount(); - // Update dropdown menu + // 更新下拉菜单标题 String format = getResources().getString(R.string.menu_select_title, selectedCount); mDropDownMenu.setTitle(format); MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); @@ -287,15 +311,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - // TODO Auto-generated method stub return false; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // TODO Auto-generated method stub return false; } + /** + * 销毁操作模式时调用 + */ public void onDestroyActionMode(ActionMode mode) { mNotesListAdapter.setChoiceMode(false); mNotesListView.setLongClickable(true); @@ -306,12 +331,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mActionMode.finish(); } + /** + * 列表项选中状态改变时调用 + */ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } + /** + * 菜单项点击处理 + */ public boolean onMenuItemClick(MenuItem item) { if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), @@ -321,6 +352,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt switch (item.getItemId()) { case R.id.delete: + // 显示删除确认对话框 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(getString(R.string.alert_title_delete)); builder.setIcon(android.R.drawable.ic_dialog_alert); @@ -337,6 +369,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); break; case R.id.move: + // 启动目标文件夹查询 startQueryDestinationFolders(); break; default: @@ -346,32 +379,27 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 新笔记按钮触摸监听器,处理特殊区域的触摸事件分发 + */ 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; int eventY = start + (int) event.getY(); - /** - * Minus TitleBar's height - */ + + // 调整标题栏高度 if (mState == ListEditState.SUB_FOLDER) { eventY -= mTitleBar.getHeight(); start -= mTitleBar.getHeight(); } - /** - * HACKME:When click the transparent part of "New Note" button, dispatch - * the event to the list view behind this button. The transparent part of - * "New Note" button could be expressed by formula y=-0.12x+94(Unit:pixel) - * and the line top of the button. The coordinate based on left of the "New - * Note" button. The 94 represents maximum height of the transparent part. - * Notice that, if the background of the button changes, the formula should - * also change. This is very bad, just for the UI designer's strong requirement. - */ + + // 处理按钮透明区域的触摸事件分发 if (event.getY() < (event.getX() * (-0.12) + 94)) { View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - mNotesListView.getFooterViewsCount()); @@ -387,6 +415,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } case MotionEvent.ACTION_MOVE: { + // 处理移动事件的分发 if (mDispatch) { mDispatchY += (int) event.getY() - mOriginY; event.setLocation(event.getX(), mDispatchY); @@ -395,6 +424,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } default: { + // 处理事件结束 if (mDispatch) { event.setLocation(event.getX(), mDispatchY); mDispatch = false; @@ -405,10 +435,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } - }; + /** + * 启动异步笔记列表查询 + */ private void startAsyncNotesListQuery() { + // 根据当前文件夹ID设置查询条件 String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, @@ -417,6 +450,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * 后台查询处理器,处理异步查询结果 + */ private final class BackgroundQueryHandler extends AsyncQueryHandler { public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); @@ -424,6 +460,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + // 处理不同类型的查询结果 switch (token) { case FOLDER_NOTE_LIST_QUERY_TOKEN: mNotesListAdapter.changeCursor(cursor); @@ -441,13 +478,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 显示文件夹列表菜单,用于选择目标文件夹 + */ private void showFolderListMenu(Cursor cursor) { AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(R.string.menu_title_select_folder); 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( @@ -462,6 +502,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); } + /** + * 创建新笔记 + */ private void createNewNote() { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); @@ -469,20 +512,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); } + /** + * 批量删除选中的笔记 + */ private void batchDelete() { new AsyncTask>() { protected HashSet doInBackground(Void... unused) { + // 获取选中笔记关联的小部件 HashSet widgets = mNotesListAdapter.getSelectedWidget(); if (!isSyncMode()) { - // if not synced, delete notes directly + // 非同步模式下直接删除笔记 if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter .getSelectedItemIds())) { } else { Log.e(TAG, "Delete notes error, should not happens"); } } else { - // in sync mode, we'll move the deleted note into the trash - // folder + // 同步模式下将删除的笔记移动到废纸篓 if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { Log.e(TAG, "Move notes to trash folder error, should not happens"); @@ -493,6 +539,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override protected void onPostExecute(HashSet widgets) { + // 更新相关小部件 if (widgets != null) { for (AppWidgetAttribute widget : widgets) { if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -506,6 +553,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } + /** + * 删除文件夹 + */ private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); @@ -514,15 +564,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt HashSet ids = new HashSet(); ids.add(folderId); + // 获取文件夹中笔记关联的小部件 HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, folderId); if (!isSyncMode()) { - // if not synced, delete folder directly + // 非同步模式下直接删除文件夹 DataUtils.batchDeleteNotes(mContentResolver, ids); } else { - // in sync mode, we'll move the deleted folder into the trash folder + // 同步模式下将删除的文件夹移动到废纸篓 DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); } + // 更新相关小部件 if (widgets != null) { for (AppWidgetAttribute widget : widgets) { if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -533,6 +585,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 打开笔记详情 + */ private void openNode(NoteItemData data) { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); @@ -540,6 +595,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); } + /** + * 打开文件夹 + */ private void openFolder(NoteItemData data) { mCurrentFolderId = data.getId(); startAsyncNotesListQuery(); @@ -549,6 +607,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } else { mState = ListEditState.SUB_FOLDER; } + // 设置标题栏显示文件夹名称 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mTitleBar.setText(R.string.call_record_folder_name); } else { @@ -557,6 +616,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mTitleBar.setVisibility(View.VISIBLE); } + /** + * 视图点击事件处理 + */ public void onClick(View v) { switch (v.getId()) { case R.id.btn_new_note: @@ -567,6 +629,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 显示软键盘 + */ private void showSoftInput() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { @@ -574,16 +639,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 隐藏软键盘 + */ private void hideSoftInput(View view) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } + /** + * 显示创建或修改文件夹对话框 + */ 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()); @@ -610,12 +683,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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 values = new ContentValues(); @@ -637,17 +712,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }); + // 根据输入框内容启用/禁用确定按钮 if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } - /** - * When the name edit text is null, disable the positive button - */ etName.addTextChangedListener(new TextWatcher() { - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // TODO Auto-generated method stub - - } + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} public void onTextChanged(CharSequence s, int start, int before, int count) { if (TextUtils.isEmpty(etName.getText())) { @@ -657,15 +727,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub - - } + public void afterTextChanged(Editable s) {} }); } @Override public void onBackPressed() { + // 处理返回键,根据当前状态导航到上级目录 switch (mState) { case SUB_FOLDER: mCurrentFolderId = Notes.ID_ROOT_FOLDER; @@ -688,8 +756,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 更新小部件 + */ private void updateWidget(int appWidgetId, int appWidgetType) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + // 根据小部件类型设置目标类 if (appWidgetType == Notes.TYPE_WIDGET_2X) { intent.setClass(this, NoteWidgetProvider_2x.class); } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { @@ -707,6 +779,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt setResult(RESULT_OK, intent); } + /** + * 文件夹上下文菜单创建监听器 + */ private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mFocusNoteDataItem != null) { @@ -732,11 +807,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Log.e(TAG, "The long click data item is null"); return false; } + // 处理文件夹上下文菜单项点击 switch (item.getItemId()) { case MENU_FOLDER_VIEW: openFolder(mFocusNoteDataItem); break; case MENU_FOLDER_DELETE: + // 显示删除文件夹确认对话框 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.alert_title_delete)); builder.setIcon(android.R.drawable.ic_dialog_alert); @@ -756,16 +833,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt default: break; } - return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); + // 根据当前状态加载不同的菜单资源 if (mState == ListEditState.NOTE_LIST) { getMenuInflater().inflate(R.menu.note_list, menu); - // set sync or 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) { @@ -780,6 +857,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override public boolean onOptionsItemSelected(MenuItem item) { + // 处理选项菜单点击事件 switch (item.getItemId()) { case R.id.menu_new_folder: { showCreateOrModifyFolderDialog(true); @@ -791,6 +869,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } case R.id.menu_sync: { if (isSyncMode()) { + // 切换同步/取消同步 if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { GTaskSyncService.startSync(this); } else { @@ -824,10 +903,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * 导出笔记到文本文件 + */ private void exportNoteToText() { final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); new AsyncTask() { - @Override protected Integer doInBackground(Void... unused) { return backup.exportToText(); @@ -835,6 +916,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt @Override protected void onPostExecute(Integer result) { + // 显示导出结果 if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this @@ -862,26 +944,34 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); } } - }.execute(); } + /** + * 判断是否处于同步模式 + */ private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + /** + * 启动偏好设置活动 + */ private void startPreferenceActivity() { Activity from = getParent() != null ? getParent() : this; Intent intent = new Intent(from, NotesPreferenceActivity.class); from.startActivityIfNeeded(intent, -1); } + /** + * 列表项点击监听器 + */ private class OnListItemClickListener implements OnItemClickListener { - 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, @@ -890,6 +980,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return; } + // 根据当前状态处理不同类型的项点击 switch (mState) { case NOTE_LIST: if (item.getType() == Notes.TYPE_FOLDER @@ -914,11 +1005,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - } + /** + * 启动目标文件夹查询 + */ private void startQueryDestinationFolders() { String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + // 根据当前状态调整查询条件 selection = (mState == ListEditState.NOTE_LIST) ? selection: "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; @@ -935,10 +1029,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * 列表项长按事件处理 + */ public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData(); if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + // 启动多选操作模式 if (mNotesListView.startActionMode(mModeCallBack) != null) { mModeCallBack.onItemCheckedStateChanged(null, position, id, true); mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); @@ -946,9 +1044,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Log.e(TAG, "startActionMode fails"); } } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + // 为文件夹设置上下文菜单 mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); } } return false; } -} +} \ No newline at end of file diff --git a/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java b/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..3a6f25f 100644 --- a/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java +++ b/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java @@ -30,19 +30,28 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; - +/** + * 笔记列表适配器,用于将笔记数据绑定到ListView上 + * 支持多选模式和小部件属性管理 + */ public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; - private Context mContext; - private HashMap mSelectedIndex; - private int mNotesCount; - private boolean mChoiceMode; - + private Context mContext; // 上下文环境 + private HashMap mSelectedIndex; // 选中项的索引映射 + private int mNotesCount; // 笔记数量 + private boolean mChoiceMode; // 是否处于多选模式 + + /** + * 小部件属性类,用于存储与笔记关联的小部件信息 + */ public static class AppWidgetAttribute { - public int widgetId; - public int widgetType; + public int widgetId; // 小部件ID + public int widgetType; // 小部件类型 }; + /** + * 构造函数 + */ public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); @@ -50,11 +59,17 @@ public class NotesListAdapter extends CursorAdapter { mNotesCount = 0; } + /** + * 创建新的列表项视图 + */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } + /** + * 将数据绑定到列表项视图 + */ @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { @@ -64,24 +79,37 @@ public class NotesListAdapter extends CursorAdapter { } } + /** + * 设置指定位置的项的选中状态 + */ public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } + /** + * 判断是否处于多选模式 + */ public boolean isInChoiceMode() { return mChoiceMode; } + /** + * 设置多选模式 + */ public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } + /** + * 全选或取消全选所有笔记项 + */ public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { if (cursor.moveToPosition(i)) { + // 只处理笔记类型的项,不处理文件夹等其他类型 if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { setCheckedItem(i, checked); } @@ -89,6 +117,9 @@ public class NotesListAdapter extends CursorAdapter { } } + /** + * 获取所有选中项的ID集合 + */ public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -101,10 +132,12 @@ public class NotesListAdapter extends CursorAdapter { } } } - return itemSet; } + /** + * 获取所有选中项关联的小部件属性集合 + */ public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -116,9 +149,7 @@ public class NotesListAdapter extends CursorAdapter { widget.widgetId = item.getWidgetId(); widget.widgetType = item.getWidgetType(); itemSet.add(widget); - /** - * Don't close cursor here, only the adapter could close it - */ + // 注意:不要在这里关闭cursor,只有适配器可以关闭它 } else { Log.e(TAG, "Invalid cursor"); return null; @@ -128,6 +159,9 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + /** + * 获取选中项的数量 + */ public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -143,11 +177,17 @@ public class NotesListAdapter extends CursorAdapter { return count; } + /** + * 判断是否所有笔记项都被选中 + */ public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount != 0 && checkedCount == mNotesCount); } + /** + * 判断指定位置的项是否被选中 + */ public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; @@ -155,23 +195,33 @@ public class NotesListAdapter extends CursorAdapter { return mSelectedIndex.get(position); } + /** + * 当内容发生变化时调用 + */ @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } + /** + * 更改游标时调用 + */ @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } + /** + * 计算笔记数量 + */ private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { Cursor c = (Cursor) getItem(i); if (c != null) { + // 只计算笔记类型的项 if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { mNotesCount++; } @@ -181,4 +231,4 @@ public class NotesListAdapter extends CursorAdapter { } } } -} +} \ No newline at end of file diff --git a/Notes-master/src/net/micode/notes/ui/NotesListItem.java b/Notes-master/src/net/micode/notes/ui/NotesListItem.java index 1221e80..115df77 100644 --- a/Notes-master/src/net/micode/notes/ui/NotesListItem.java +++ b/Notes-master/src/net/micode/notes/ui/NotesListItem.java @@ -29,18 +29,27 @@ import net.micode.notes.data.Notes; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - +/** + * 笔记列表项视图类,负责渲染笔记列表中的单个项目 + * 支持显示不同类型的笔记(普通笔记、文件夹、通话记录) + * 支持多选模式下显示复选框 + */ public class NotesListItem extends LinearLayout { - private ImageView mAlert; - private TextView mTitle; - private TextView mTime; - private TextView mCallName; - private NoteItemData mItemData; - private CheckBox mCheckBox; + private ImageView mAlert; // 提醒图标 + private TextView mTitle; // 标题文本 + private TextView mTime; // 时间文本 + private TextView mCallName; // 通话记录联系人名称 + private NoteItemData mItemData; // 笔记数据 + private CheckBox mCheckBox; // 多选模式下的复选框 + /** + * 构造函数,初始化视图组件 + */ public NotesListItem(Context context) { super(context); + // 加载布局文件 inflate(context, R.layout.note_item, this); + // 获取各视图组件引用 mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTitle = (TextView) findViewById(R.id.tv_title); mTime = (TextView) findViewById(R.id.tv_time); @@ -48,7 +57,16 @@ public class NotesListItem extends LinearLayout { mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } + /** + * 绑定数据到视图 + * + * @param context 上下文 + * @param data 笔记数据 + * @param choiceMode 是否处于多选模式 + * @param checked 该项是否被选中 + */ public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { + // 处理多选模式下的复选框显示 if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(checked); @@ -57,7 +75,10 @@ public class NotesListItem extends LinearLayout { } mItemData = data; + + // 根据笔记类型设置不同的显示内容和样式 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 通话记录文件夹特殊处理 mCallName.setVisibility(View.GONE); mAlert.setVisibility(View.VISIBLE); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); @@ -65,10 +86,12 @@ public class NotesListItem extends LinearLayout { + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setImageResource(R.drawable.call_record); } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + // 通话记录笔记特殊处理 mCallName.setVisibility(View.VISIBLE); mCallName.setText(data.getCallName()); mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + // 显示提醒图标(如果有提醒) if (data.hasAlert()) { mAlert.setImageResource(R.drawable.clock); mAlert.setVisibility(View.VISIBLE); @@ -80,12 +103,15 @@ public class NotesListItem extends LinearLayout { mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); if (data.getType() == Notes.TYPE_FOLDER) { + // 文件夹显示样式 mTitle.setText(data.getSnippet() + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setVisibility(View.GONE); } else { + // 普通笔记显示样式 mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); + // 显示提醒图标(如果有提醒) if (data.hasAlert()) { mAlert.setImageResource(R.drawable.clock); mAlert.setVisibility(View.VISIBLE); @@ -94,29 +120,45 @@ public class NotesListItem extends LinearLayout { } } } + + // 设置修改时间,使用相对时间格式(如"2小时前") mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + // 设置背景样式 setBackground(data); } + /** + * 根据笔记类型和位置设置背景样式 + */ private void setBackground(NoteItemData data) { int id = data.getBgColorId(); + if (data.getType() == Notes.TYPE_NOTE) { + // 根据笔记在列表中的位置和状态设置不同的背景 if (data.isSingle() || data.isOneFollowingFolder()) { + // 单独的笔记或后面跟着文件夹的笔记 setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); } else if (data.isLast()) { + // 列表中的最后一个笔记 setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); } else if (data.isFirst() || data.isMultiFollowingFolder()) { + // 列表中的第一个笔记或后面跟着多个文件夹的笔记 setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); } else { + // 普通位置的笔记 setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } } else { + // 文件夹使用特定的背景 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } + /** + * 获取绑定的笔记数据 + */ public NoteItemData getItemData() { return mItemData; } -} +} \ No newline at end of file diff --git a/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java b/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..fef4e66 100644 --- a/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/Notes-master/src/net/micode/notes/ui/NotesPreferenceActivity.java @@ -47,53 +47,57 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; - +/** + * 笔记应用的设置界面,提供同步账户管理和手动同步功能 + */ public class NotesPreferenceActivity extends PreferenceActivity { - public static final String PREFERENCE_NAME = "notes_preferences"; - - public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - - public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - - public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; - - private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - - private static final String AUTHORITIES_FILTER_KEY = "authorities"; - - private PreferenceCategory mAccountCategory; - - private GTaskReceiver mReceiver; - - private Account[] mOriAccounts; - - private boolean mHasAddedAccount; - + public static final String PREFERENCE_NAME = "notes_preferences"; // 偏好设置文件名 + public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; // 同步账户名键值 + public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; // 上次同步时间键值 + public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; // 背景颜色设置键值 + + private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; // 同步账户设置分类键值 + private static final String AUTHORITIES_FILTER_KEY = "authorities"; // 账户类型过滤键值 + + private PreferenceCategory mAccountCategory; // 账户设置分类 + private GTaskReceiver mReceiver; // 同步状态广播接收器 + private Account[] mOriAccounts; // 原始账户列表 + private boolean mHasAddedAccount; // 是否已添加新账户标志 + + /** + * 活动创建时调用,初始化界面和数据 + */ @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - /* using the app icon for navigation */ + // 设置ActionBar支持返回导航 getActionBar().setDisplayHomeAsUpEnabled(true); + // 加载设置XML布局 addPreferencesFromResource(R.xml.preferences); mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); mReceiver = new GTaskReceiver(); + + // 注册广播接收器,监听同步服务状态变化 IntentFilter filter = new IntentFilter(); filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); registerReceiver(mReceiver, filter); mOriAccounts = null; + // 添加设置页面头部视图 View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); getListView().addHeaderView(header, null, true); } + /** + * 活动恢复时调用,刷新界面和处理账户变化 + */ @Override protected void onResume() { super.onResume(); - // need to set sync account automatically if user has added a new - // account + // 如果用户添加了新账户,自动设置为同步账户 if (mHasAddedAccount) { Account[] accounts = getGoogleAccounts(); if (mOriAccounts != null && accounts.length > mOriAccounts.length) { @@ -116,6 +120,9 @@ public class NotesPreferenceActivity extends PreferenceActivity { refreshUI(); } + /** + * 活动销毁时调用,释放资源 + */ @Override protected void onDestroy() { if (mReceiver != null) { @@ -124,6 +131,9 @@ public class NotesPreferenceActivity extends PreferenceActivity { super.onDestroy(); } + /** + * 加载账户设置选项 + */ private void loadAccountPreference() { mAccountCategory.removeAll(); @@ -135,11 +145,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { public boolean onPreferenceClick(Preference preference) { if (!GTaskSyncService.isSyncing()) { if (TextUtils.isEmpty(defaultAccount)) { - // the first time to set account + // 首次设置账户 showSelectAccountAlertDialog(); } else { - // if the account has already been set, we need to promp - // user about the risk + // 已设置账户,提示用户更改风险 showChangeAccountConfirmAlertDialog(); } } else { @@ -154,11 +163,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { mAccountCategory.addPreference(accountPref); } + /** + * 加载同步按钮和同步状态 + */ private void loadSyncButton() { Button syncButton = (Button) findViewById(R.id.preference_sync_button); TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - // set button state + // 设置同步按钮状态 if (GTaskSyncService.isSyncing()) { syncButton.setText(getString(R.string.preferences_button_sync_cancel)); syncButton.setOnClickListener(new View.OnClickListener() { @@ -176,7 +188,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); - // set last sync time + // 设置上次同步时间显示 if (GTaskSyncService.isSyncing()) { lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE); @@ -193,14 +205,21 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + /** + * 刷新设置界面 + */ private void refreshUI() { loadAccountPreference(); loadSyncButton(); } + /** + * 显示选择账户对话框 + */ private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + // 设置自定义标题 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); @@ -216,6 +235,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { mOriAccounts = accounts; mHasAddedAccount = false; + // 如果有可用账户,显示账户列表 if (accounts.length > 0) { CharSequence[] items = new CharSequence[accounts.length]; final CharSequence[] itemMapping = items; @@ -237,6 +257,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } + // 添加"添加账户"选项 View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); dialogBuilder.setView(addAccountView); @@ -254,9 +275,13 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } + /** + * 显示更改账户确认对话框 + */ private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + // 设置自定义标题 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, @@ -265,6 +290,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); dialogBuilder.setCustomTitle(titleView); + // 显示操作选项 CharSequence[] menuItemArray = new CharSequence[] { getString(R.string.preferences_menu_change_account), getString(R.string.preferences_menu_remove_account), @@ -283,11 +309,17 @@ public class NotesPreferenceActivity extends PreferenceActivity { dialogBuilder.show(); } + /** + * 获取设备上的Google账户列表 + */ private Account[] getGoogleAccounts() { AccountManager accountManager = AccountManager.get(this); return accountManager.getAccountsByType("com.google"); } + /** + * 设置同步账户 + */ private void setSyncAccount(String account) { if (!getSyncAccountName(this).equals(account)) { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -299,10 +331,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } editor.commit(); - // clean up last sync time + // 清除上次同步时间 setLastSyncTime(this, 0); - // clean up local gtask related info + // 清除本地与Google Tasks相关的信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -318,6 +350,9 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + /** + * 移除同步账户 + */ private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); @@ -329,7 +364,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } editor.commit(); - // clean up local gtask related info + // 清除本地与Google Tasks相关的信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -340,12 +375,18 @@ public class NotesPreferenceActivity extends PreferenceActivity { }).start(); } + /** + * 获取当前设置的同步账户名 + */ public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + /** + * 设置上次同步时间 + */ public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -354,12 +395,18 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.commit(); } + /** + * 获取上次同步时间 + */ public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } + /** + * 同步服务状态广播接收器 + */ private class GTaskReceiver extends BroadcastReceiver { @Override @@ -370,13 +417,16 @@ public class NotesPreferenceActivity extends PreferenceActivity { syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); } - } } + /** + * 处理选项菜单点击事件 + */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: + // 返回笔记列表 Intent intent = new Intent(this, NotesListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); @@ -385,4 +435,4 @@ public class NotesPreferenceActivity extends PreferenceActivity { return false; } } -} +} \ No newline at end of file