diff --git a/doc/小米便签泛读报告.docx b/doc/小米便签泛读报告.docx index bf7e8a5..fbd5ed6 100644 Binary files a/doc/小米便签泛读报告.docx and b/doc/小米便签泛读报告.docx differ diff --git a/src/net/micode/notes/data/NotesProvider.java b/src/net/micode/notes/data/NotesProvider.java index edb0a60..475fbed 100644 --- a/src/net/micode/notes/data/NotesProvider.java +++ b/src/net/micode/notes/data/NotesProvider.java @@ -1,22 +1,6 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package net.micode.notes.data; - - + + import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; @@ -28,70 +12,125 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.text.TextUtils; import android.util.Log; - + import net.micode.notes.R; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - - + + public class NotesProvider extends ContentProvider { +// Android 应用程序中的一部分:内容提供者(ContentProvider)。 +// 内容提供者是 Android 四大组件之一,它允许应用程序之间共享数据。 + + //概述: + //NotesProvider的主要功能是作为一个内容提供者,为其他应用程序或组件提供对“Notes”数据的访问。 + //它允许其他应用程序查询、插入、更新或删除标签数据。 + //通过URI匹配,NotesProvider能够区分对哪种数据类型的请求(例如,单独的标签、标签的数据、文件夹操作等),并执行相应的操作。 + + //用于匹配不同URI的UriMatcher对象,通常用于解析传入的URI,并确定应该执行哪种操作。 private static final UriMatcher mMatcher; - + + //NotesDatabaseHelper实类,用来操作SQLite数据库,负责创建、更新和查询数据库。 private NotesDatabaseHelper mHelper; - + + //标签,输出日志时用来表示是该类发出的消息 private static final String TAG = "NotesProvider"; - + + //6个URI的匹配码,用于区分不同的URI类型 private static final int URI_NOTE = 1; private static final int URI_NOTE_ITEM = 2; private static final int URI_DATA = 3; private static final int URI_DATA_ITEM = 4; - + private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; - + + //进一步定义了URI匹配规则和搜索查询的投影 + //功能概述: + //初始化了一个UriMatcher对象mMatcher,并添加了一系列的URI匹配规则。 + //解读: static { + //创建了一个UriMatcher实例,并设置默认匹配码为NO_MATCH,表示如果没有任何URI匹配,则返回这个码。 mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + //添加规则,当URI的authority为Notes.AUTHORITY,路径为note时,返回匹配码URI_NOTE。 mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); + //添加规则,当URI的authority为Notes.AUTHORITY,路径为note/后跟一个数字(#代表数字)时,返回匹配码URI_NOTE_ITEM。 mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); + //和上面两句同理,但用于匹配数据相关的URI mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); + //用于匹配搜索相关的URI mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); + //这两行用于匹配搜索建议相关的URI mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); } - + /** * x'0A' represents the '\n' character in sqlite. For title and content in the search result, * we will trim '\n' and white space in order to show more information. */ - private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + //功能概述: + //一个 SQL 查询的投影部分,用于定义查询返回的结果集中应该包含哪些列。 + //解读:(每行对应) + //返回笔记的 ID。 + //笔记的 ID 也被重命名为 SUGGEST_COLUMN_INTENT_EXTRA_DATA,这通常用于 Android 的搜索建议中,作为传递给相关 Intent 的额外数据。 + //对 SNIPPET 列的处理:首先使用 REPLACE 函数将 x'0A'(即换行符 \n)替换为空字符串,然后使用 TRIM 函数删除前后的空白字符,处理后的结果分别重命名为 SUGGEST_COLUMN_TEXT_1 + //对 SNIPPET 列的处理:首先使用 REPLACE 函数将 x'0A'(即换行符 \n)替换为空字符串,然后使用 TRIM 函数删除前后的空白字符,处理后的结果分别重命名为 SUGGEST_COLUMN_TEXT_2 + //返回一个用于搜索建议图标的资源 ID,并命名为 SUGGEST_COLUMN_ICON_1。 + //返回一个固定的 Intent 动作 ACTION_VIEW,并命名为 SUGGEST_COLUMN_INTENT_ACTION。 + //返回一个内容类型,并命名为 SUGGEST_COLUMN_INTENT_DATA。 + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," //返回笔记的 ID + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; - + + //功能概述: + //完整的 SQL 查询语句,用于从 TABLE.NOTE 表中检索信息 + //解读: + // 使用上面定义的投影来选择数据。 + // 并指定从哪个表中选择数据。 + //WHERE子句包含三个条件: + // ①搜索 SNIPPET 列中包含特定模式的行(? 是一个占位符,实际查询时会用具体的值替换)。 + // ②父ID不为回收站的ID:排除那些父 ID 为回收站的行。 + // ③只选择类型为note(标签)的行。 private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; - + + //重写onCreate方法: + //getContext() 方法被调用以获取当前组件的上下文(Context),以便 NotesDatabaseHelper 能够访问应用程序的资源和其他功能 + //mHelper用于存储从 NotesDatabaseHelper.getInstance 方法返回的实例。这样,该实例就可以在整个组件的其他方法中被访问和使用。 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - + + //功能:查询数据 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + //初始化变量: + //Cursor对象 c,用来存储查询结果 + //使用 NotesDatabaseHelper 的实例 mHelper来获取一个可读的数据库实例 + //定义一个字符串id,用来存储从URI中解析出的ID Cursor c = null; SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + + //根据匹配不同的URI来进行不同的查询 switch (mMatcher.match(uri)) { + // URI_NOTE:查询整个 NOTE 表。 + // URI_NOTE_ITEM:查询 NOTE 表中的特定项。ID 从 URI 的路径段中获取,并添加到查询条件中。 + // URI_DATA:查询整个 DATA 表。 + // URI_DATA_ITEM:查询 DATA 表中的特定项。ID 的获取和处理方式与 URI_NOTE_ITEM 相同。 case URI_NOTE: c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); @@ -110,13 +149,19 @@ public class NotesProvider extends ContentProvider { c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; + + //URI_SEARCH 和 URI_SEARCH_SUGGEST:处理搜索查询。 + // 代码首先检查是否提供了不应与搜索查询一起使用的参数(如 sortOrder, selection, selectionArgs, 或 projection)。 + // 如果提供了这些参数,则抛出一个 IllegalArgumentException。 + // 根据 URI 类型,从 URI 的路径段或查询参数中获取搜索字符串 searchString。 + // 如果 searchString 为空或无效,则返回 null,表示没有搜索结果。 case URI_SEARCH: case URI_SEARCH_SUGGEST: if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } - + String searchString = null; if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { if (uri.getPathSegments().size() > 1) { @@ -125,11 +170,13 @@ public class NotesProvider extends ContentProvider { } else { searchString = uri.getQueryParameter("pattern"); } - + if (TextUtils.isEmpty(searchString)) { return null; } - + + //字符串格式化:格式化后的字符串就会是 "%s%",即包含s是任何文本 + //然后执行原始SQL查询 try { searchString = String.format("%%%s%%", searchString); c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, @@ -138,19 +185,31 @@ public class NotesProvider extends ContentProvider { Log.e(TAG, "got exception: " + ex.toString()); } break; + + //未知URI处理: default: throw new IllegalArgumentException("Unknown URI " + uri); } + //如果查询结果不为空(即 Cursor 对象 c 不是 null),则为其设置一个通知 URI。 + //这意味着当与这个 URI 关联的数据发生变化时,任何注册了监听这个 URI 的 ContentObserver 都会被通知。 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } - + + //功能:插入数据 + //参数:Uri 用来标识要插入数据的表,ContentValues对象包含要插入的键值对 @Override public Uri insert(Uri uri, ContentValues values) { + //获取数据库 + //三个长整型变量,分别用来存储数据项ID、便签ID 和插入行的ID SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; + + //对于 URI_NOTE,将values插入到 TABLE.NOTE 表中,并返回插入行的 ID。 + //对于 URI_DATA,首先检查values是否包含 DataColumns.NOTE_ID,如果包含,则获取其值。如果不包含,记录一条日志信息。然后,将 values 插入到 TABLE.DATA 表中,并返回插入行的 ID。 + //如果 uri 不是已知的 URI 类型,则抛出一个 IllegalArgumentException。 switch (mMatcher.match(uri)) { case URI_NOTE: insertedId = noteId = db.insert(TABLE.NOTE, null, values); @@ -166,28 +225,44 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + + //功能:通知变化 + //如果noteId 或 dataId 大于 0(即成功插入了数据),则使用 ContentResolver 的 notifyChange 方法通知监听这些 URI 的观察者,告知数据已经改变。 + //ContentUris.withAppendedId 方法用于在基本 URI 后面追加一个 ID,形成完整的 URI。 // Notify the note uri if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - + // Notify the data uri if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } - + + //返回包含新插入数据项ID 的 Uri。允许调用者知道新插入的数据项的位置 return ContentUris.withAppendedId(uri, insertedId); } - + + //功能:删除数据项 + //参数:uri:标识要删除数据的表或数据项。 selection:一个可选的 WHERE 子句,用于指定删除条件。 selectionArgs:一个可选的字符串数组,用于替换 selection 中的占位符 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { + //count:记录被删除的行数。 + //id:用于存储从 URI 中解析出的数据项 ID。 + //db:可写的数据库对象,用于执行删除操作。 + //deleteData:一个布尔值,用于标记是否删除了 DATA 表中的数据。 int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; + switch (mMatcher.match(uri)) { + //URI_NOTE: 修改 selection 语句:确保只删除 ID 大于 0 的笔记。然后执行删除操作并返回被删除的行数。 + //URI_NOTE_ITEM: 从 URI 中解析出 ID。检查 ID 是否小于等于 0,如果是,则不执行删除操作;否则执行删除操作并返回被删除的行数 + //URI_DATA: 执行删除操作并返回被删除的行数。设置 deleteData 为 true,表示删除了 DATA 表中的数据。 + //URI_DATA_ITEM: 先从 URI 中解析出 ID,然后执行删除操作并返回被删除的行数,并设置 deleteData 为 true,表示删除了 DATA 表中的数据。 case URI_NOTE: selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); @@ -218,22 +293,39 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + + //如果 count 大于 0,说明有数据被删除。 + //如果 deleteData 为 true,则通知监听 Notes.CONTENT_NOTE_URI 的观察者,数据已改变。 + //通知监听传入 uri 的观察者数据已改变。 if (count > 0) { if (deleteData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } getContext().getContentResolver().notifyChange(uri, null); } + return count; } - + + //功能:更新数据库的数据 + //参数:uri:标识要更新数据的表或数据项。 values:一个包含新值的键值对集合。 + // selection:一个可选的 WHERE 子句,用于指定更新条件。 selectionArgs:一个可选的字符串数组,用于替换 selection 中的占位符。 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + //count:记录被更新的行数。 + //id:用于存储从 URI 中解析出的数据项 ID。 + //db:可写的 SQLite 数据库对象,用于执行更新操作。 + //updateData:用于标记是否更新了 data 表中的数据。 int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean updateData = false; + switch (mMatcher.match(uri)) { + //URI_NOTE:调用 increaseNoteVersion 方法(用于增加便签版本),然后在note表执行更新操作并返回被更新的行数。 + //URI_NOTE_ITEM:从 URI 中解析出 ID,并调用 increaseNoteVersion 方法,传入解析出的 ID,最后在note表执行更新操作并返回被更新的行数。 + //URI_DATA:在data表执行更新操作并返回被更新的行数。设置 updateData 为 true,表示更新了 DATA 表中的数据。 + //URI_DATA_ITEM:从 URI 中解析出 ID。执行更新操作并返回被更新的行数。置 updateData 为 true,表示更新了 DATA 表中的数据。 case URI_NOTE: increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); @@ -257,7 +349,10 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } - + + //如果 count 大于 0,说明有数据被更新。 + //如果 updateData 为 true,则通知监听 Notes.CONTENT_NOTE_URI 的观察者数据已改变。 + //通知监听传入 uri 的观察者数据已改变。 if (count > 0) { if (updateData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); @@ -266,11 +361,13 @@ public class NotesProvider extends ContentProvider { } return count; } - + + //解析传入的条件语句:一个 SQL WHERE 子句的一部分 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - + + //更新note表的version列,将其值增加 1。 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -278,7 +375,7 @@ public class NotesProvider extends ContentProvider { sql.append(" SET "); sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); - + if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } @@ -292,14 +389,14 @@ public class NotesProvider extends ContentProvider { } sql.append(selectString); } - + mHelper.getWritableDatabase().execSQL(sql.toString()); } - + @Override public String getType(Uri uri) { // TODO Auto-generated method stub return null; } - -} + +} \ No newline at end of file diff --git a/src/net/micode/notes/ui/NotesListActivity.java b/src/net/micode/notes/ui/NotesListActivity.java index e843aec..1e6b10d 100644 --- a/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/net/micode/notes/ui/NotesListActivity.java @@ -1,21 +1,5 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package net.micode.notes.ui; - + import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -59,7 +43,7 @@ import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView; import android.widget.Toast; - + import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; @@ -71,103 +55,118 @@ import net.micode.notes.tool.ResourceParser; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; - + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashSet; - -public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { +//主界面,一进入就是这个界面 +/** + * @author k + * + */ +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 String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; - + + private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; //单行超过80个字符 + private enum ListEditState { 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 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; - + @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + // 创建类 + protected void onCreate(final Bundle savedInstanceState) { //需要是final类型 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义,它可以修饰非抽象类、非抽象类成员方法和变量。你可能出于两种理解而需要阻止改变:设计或效率。 + // final类不能被继承,没有子类,final类中的方法默认是final的。 + //final方法不能被子类的方法覆盖,但可以被继承。 + //final成员变量表示常量,只能被赋值一次,赋值后值不再改变。 + //final不能用于修饰构造方法。 + super.onCreate(savedInstanceState); // 调用父类的onCreate函数 setContentView(R.layout.note_list); initResources(); - + /** * Insert an introduction when user firstly use this application */ setAppInfoFromRawRes(); } - + @Override + // 返回一些子模块完成的数据交给主Activity处理 protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == RESULT_OK + // 结果值 和 要求值 符合要求 + if (resultCode == RESULT_OK && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { mNotesListAdapter.changeCursor(null); } else { super.onActivityResult(requestCode, resultCode, data); + // 调用 Activity 的onActivityResult() } } - + private void setAppInfoFromRawRes() { + // Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。 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); + // 把资源文件放到应用程序的/raw/raw下,那么就可以在应用中使用getResources获取资源后, + // 以openRawResource方法(不带后缀的资源文件名)打开这个文件。 + in = getResources().openRawResource(R.raw.introduction); if (in != null) { InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); - char [] buf = new char[1024]; + char [] buf = new char[1024]; // 自行定义的数值,使用者不知道有什么意义 int len = 0; while ((len = br.read(buf)) > 0) { sb.append(buf, 0, len); @@ -180,7 +179,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt e.printStackTrace(); return; } finally { - if(in != null) { + if (in != null) { try { in.close(); } catch (IOException e) { @@ -189,12 +188,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + + // 创建空的WorkingNote WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, ResourceParser.RED); note.setWorkingText(sb.toString()); if (note.saveNote()) { + // 更新保存note的信息 sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { Log.e(TAG, "Save introduction note error"); @@ -202,25 +203,28 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + @Override protected void onStart() { super.onStart(); startAsyncNotesListQuery(); } - + + // 初始化资源 private void initResources() { - mContentResolver = this.getContentResolver(); + mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西 mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mNotesListView = (ListView) findViewById(R.id.notes_list); + + // findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名 + mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView,作为Item的容器 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 = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮 mAddNewNote.setOnClickListener(this); mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); mDispatch = false; @@ -230,12 +234,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mState = ListEditState.NOTE_LIST; mModeCallBack = new ModeCallback(); } - + + // 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; private ActionMode mActionMode; private MenuItem mMoveMenu; - + public boolean onCreateActionMode(ActionMode mode, Menu menu) { getMenuInflater().inflate(R.menu.note_list_options, menu); menu.findItem(R.id.delete).setOnMenuItemClickListener(this); @@ -251,7 +256,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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); @@ -259,21 +264,22 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt (Button) customView.findViewById(R.id.selection_menu), R.menu.note_list_dropdown); mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ - public boolean onMenuItemClick(MenuItem item) { + public boolean onMenuItemClick(final MenuItem item) { mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); 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); + mDropDownMenu.setTitle(format); // 更改标题 MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); if (item != null) { if (mNotesListAdapter.isAllSelected()) { @@ -285,40 +291,40 @@ 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); mAddNewNote.setVisibility(View.VISIBLE); } - + public void finishActionMode() { 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), Toast.LENGTH_SHORT).show(); return true; } - + switch (item.getItemId()) { case R.id.delete: AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); @@ -345,9 +351,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } } - + private class NewNoteOnTouchListener implements OnTouchListener { - + public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { @@ -366,7 +372,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt /** * 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) + * "New Note" button could be expressed by formula y=-0.12x+94锛圲nit: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 @@ -405,9 +411,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } - + }; - + private void startAsyncNotesListQuery() { String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; @@ -416,12 +422,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt String.valueOf(mCurrentFolderId) }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } - + private final class BackgroundQueryHandler extends AsyncQueryHandler { public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); } - + @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { @@ -440,13 +446,13 @@ 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)); @@ -461,14 +467,14 @@ 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); intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); } - + private void batchDelete() { new AsyncTask>() { protected HashSet doInBackground(Void... unused) { @@ -490,7 +496,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return widgets; } - + @Override protected void onPostExecute(HashSet widgets) { if (widgets != null) { @@ -505,13 +511,13 @@ 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); return; } - + HashSet ids = new HashSet(); ids.add(folderId); HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, @@ -532,14 +538,14 @@ 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); intent.putExtra(Intent.EXTRA_UID, data.getId()); this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); } - + private void openFolder(NoteItemData data) { mCurrentFolderId = data.getId(); startAsyncNotesListQuery(); @@ -556,7 +562,7 @@ 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: @@ -566,19 +572,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } } - + private void showSoftInput() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } } - + 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); @@ -596,14 +602,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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() { @@ -636,7 +642,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt dialog.dismiss(); } }); - + if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } @@ -646,9 +652,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt etName.addTextChangedListener(new TextWatcher() { public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub - + } - + public void onTextChanged(CharSequence s, int start, int before, int count) { if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); @@ -656,17 +662,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt positive.setEnabled(true); } } - + public void afterTextChanged(Editable s) { // TODO Auto-generated method stub - + } }); } - + + /* (non-Javadoc) + * @see android.app.Activity#onBackPressed() + * 按返回键时根据情况更改类中的数据 + */ @Override - public void onBackPressed() { - switch (mState) { + public void onBackPressed() { switch (mState) { case SUB_FOLDER: mCurrentFolderId = Notes.ID_ROOT_FOLDER; mState = ListEditState.NOTE_LIST; @@ -687,7 +696,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } } - + + /** + * @param appWidgetId + * @param appWidgetType + * 根据不同类型的widget更新插件,通过intent传送数据 + */ private void updateWidget(int appWidgetId, int appWidgetType) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (appWidgetType == Notes.TYPE_WIDGET_2X) { @@ -698,15 +712,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Log.e(TAG, "Unspported widget type"); return; } - + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId }); - + sendBroadcast(intent); setResult(RESULT_OK, intent); } - + + /** + * 声明监听器,建立菜单,包括名称,视图,删除操作,更改名称操作; + */ private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mFocusNoteDataItem != null) { @@ -717,7 +734,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } }; - + @Override public void onContextMenuClosed(Menu menu) { if (mNotesListView != null) { @@ -725,7 +742,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } super.onContextMenuClosed(menu); } - + + /* (non-Javadoc) + * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) + * 针对menu中不同的选择进行不同的处理,里面详细注释 + */ @Override public boolean onContextItemSelected(MenuItem item) { if (mFocusNoteDataItem == null) { @@ -734,10 +755,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } switch (item.getItemId()) { case MENU_FOLDER_VIEW: - openFolder(mFocusNoteDataItem); + openFolder(mFocusNoteDataItem);//打开对应文件 break; case MENU_FOLDER_DELETE: - AlertDialog.Builder builder = new AlertDialog.Builder(this); + 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)); @@ -748,7 +769,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }); builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); + builder.show();//显示对话框 break; case MENU_FOLDER_CHANGE_NAME: showCreateOrModifyFolderDialog(false); @@ -756,10 +777,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt default: break; } - + return true; } - + @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); @@ -777,7 +798,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return true; } - + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -817,22 +838,29 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return true; } - + + /* (non-Javadoc) + * @see android.app.Activity#onSearchRequested() + * 直接调用startSearch函数 + */ @Override public boolean onSearchRequested() { startSearch(null, false, null /* appData */, false); return true; } - + + /** + * 函数功能:实现将便签导出到文本功能 + */ private void exportNoteToText() { final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); new AsyncTask() { - + @Override protected Integer doInBackground(Void... unused) { return backup.exportToText(); } - + @Override protected void onPostExecute(Integer result) { if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { @@ -862,22 +890,33 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); } } - + }.execute(); } - + + /** + * @return + * 功能:判断是否正在同步 + */ private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } - + + /** + * 功能:跳转到PreferenceActivity界面 + */ private void startPreferenceActivity() { Activity from = getParent() != null ? getParent() : this; Intent intent = new Intent(from, NotesPreferenceActivity.class); from.startActivityIfNeeded(intent, -1); } - + + /** + * @author k + * 函数功能:实现对便签列表项的点击事件(短按) + */ 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(); @@ -889,7 +928,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return; } - + switch (mState) { case NOTE_LIST: if (item.getType() == Notes.TYPE_FOLDER @@ -914,14 +953,17 @@ 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 + ")"; - + mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, @@ -934,7 +976,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.MODIFIED_DATE + " DESC"); } - + + /* (non-Javadoc) + * @see android.widget.AdapterView.OnItemLongClickListener#onItemLongClick(android.widget.AdapterView, android.view.View, int, long) + * 长按某一项时进行的操作 + * 如果长按的是便签,则通过ActionMode菜单实现;如果长按的是文件夹,则通过ContextMenu菜单实现; + * 具体ActionMOde菜单和ContextMenu菜单的详细见精度笔记 + */ public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData(); @@ -951,4 +999,4 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } -} +} \ No newline at end of file diff --git a/小米便签泛读报告.docx b/小米便签泛读报告.docx deleted file mode 100644 index bf7e8a5..0000000 Binary files a/小米便签泛读报告.docx and /dev/null differ