From 714f824d370668e8314d4c9e64e092ac72d48bcb Mon Sep 17 00:00:00 2001 From: eazzy <1044745821@qq.com> Date: Sun, 10 Dec 2023 00:06:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B3=A8=E9=87=8A=E4=BA=86ui=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/小米便签泛读报告.docx | Bin 196554 -> 196502 bytes .../micode/notes/ui/NotesListActivity.java | 254 +++++++++++------- .../net/micode/notes/ui/NotesListAdapter.java | 115 ++++++-- .../net/micode/notes/ui/NotesListItem.java | 68 +++-- .../notes/ui/NotesPreferenceActivity.java | 202 +++++++++++--- 5 files changed, 443 insertions(+), 196 deletions(-) diff --git a/doc/小米便签泛读报告.docx b/doc/小米便签泛读报告.docx index 2f01857b14fa329228db9ec6e25fd9837cd5f107..d18d5df810a86d3464a298fd0f6a79279a99d45e 100644 GIT binary patch delta 16868 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 UJ|ZRvCde=Z2uSDi7bnR71HURBIRF3v delta 16876 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 diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java index edd9d4c..83d6548 100644 --- a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -1,19 +1,3 @@ -/* - * 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; @@ -77,8 +61,12 @@ 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; @@ -89,7 +77,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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 @@ -136,8 +124,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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(); @@ -148,26 +141,32 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override + // 返回一些子模块完成的数据交给主Activity处理 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); } 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) { @@ -190,11 +189,13 @@ 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"); @@ -209,18 +210,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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; @@ -231,6 +235,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mModeCallBack = new ModeCallback(); } + // 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; private ActionMode mActionMode; @@ -259,7 +264,7 @@ 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; @@ -269,11 +274,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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()) { @@ -307,7 +313,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { + boolean checked) { mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } @@ -347,52 +353,58 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private class NewNoteOnTouchListener implements OnTouchListener { public boolean onTouch(View v, MotionEvent event) { - int action = event.getAction(); - if (action == 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(); + 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锛圲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 + * 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()); + if (view != null && view.getBottom() > start + && (view.getTop() < (start + 94))) { + mOriginY = (int) event.getY(); + mDispatchY = eventY; + event.setLocation(event.getX(), mDispatchY); + mDispatch = true; + return mNotesListView.dispatchTouchEvent(event); + } + } + break; } - /** - * 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()); - if (view != null && view.getBottom() > start - && (view.getTop() < (start + 94))) { - mOriginY = (int) event.getY(); - mDispatchY = eventY; + case MotionEvent.ACTION_MOVE: { + if (mDispatch) { + mDispatchY += (int) event.getY() - mOriginY; event.setLocation(event.getX(), mDispatchY); - mDispatch = true; return mNotesListView.dispatchTouchEvent(event); } + break; } - } else if (action == MotionEvent.ACTION_MOVE) { - if (mDispatch) { - mDispatchY += (int) event.getY() - mOriginY; - event.setLocation(event.getX(), mDispatchY); - return mNotesListView.dispatchTouchEvent(event); - } - } else { - if (mDispatch) { - event.setLocation(event.getX(), mDispatchY); - mDispatch = false; - return mNotesListView.dispatchTouchEvent(event); + default: { + if (mDispatch) { + event.setLocation(event.getX(), mDispatchY); + mDispatch = false; + return mNotesListView.dispatchTouchEvent(event); + } + break; } } return false; @@ -405,7 +417,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt : NORMAL_SELECTION; mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { - String.valueOf(mCurrentFolderId) + String.valueOf(mCurrentFolderId) }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } @@ -612,7 +624,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt values.put(NoteColumns.LOCAL_MODIFIED, 1); mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?", new String[] { - String.valueOf(mFocusNoteDataItem.getId()) + String.valueOf(mFocusNoteDataItem.getId()) }); } } else if (!TextUtils.isEmpty(name)) { @@ -652,30 +664,38 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); } + /* (non-Javadoc) + * @see android.app.Activity#onBackPressed() + * 按返回键时根据情况更改类中的数据 + */ @Override - public void onBackPressed() { - switch (mState) { - case SUB_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - startAsyncNotesListQuery(); - mTitleBar.setVisibility(View.GONE); - break; - case CALL_RECORD_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - mAddNewNote.setVisibility(View.VISIBLE); - mTitleBar.setVisibility(View.GONE); - startAsyncNotesListQuery(); - break; - case NOTE_LIST: - super.onBackPressed(); - break; - default: - break; - } + public void onBackPressed() { switch (mState) { + case SUB_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + break; + case CALL_RECORD_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + mAddNewNote.setVisibility(View.VISIBLE); + mTitleBar.setVisibility(View.GONE); + startAsyncNotesListQuery(); + break; + case NOTE_LIST: + super.onBackPressed(); + break; + default: + 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) { @@ -688,13 +708,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - appWidgetId + 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) { @@ -714,6 +737,10 @@ 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) { @@ -722,10 +749,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)); @@ -736,7 +763,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); @@ -793,12 +820,19 @@ 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() { @@ -841,16 +875,27 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.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) { @@ -892,10 +937,13 @@ 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 + ")"; + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null, @@ -910,6 +958,12 @@ 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(); @@ -926,4 +980,4 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } -} +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..b165769 100644 --- a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java @@ -1,19 +1,3 @@ -/* - * 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.content.Context; @@ -23,6 +7,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; + import net.micode.notes.data.Notes; import java.util.Collection; @@ -31,55 +16,94 @@ import java.util.HashSet; import java.util.Iterator; +/* + * 功能:直译为便签表连接器,继承了CursorAdapter,它为cursor和ListView提供了连接的桥梁。 + * 所以NotesListAdapter实现的是鼠标和编辑便签链接的桥梁 + */ public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; private Context mContext; private HashMap mSelectedIndex; - private int mNotesCount; - private boolean mChoiceMode; + private int mNotesCount; //便签数 + private boolean mChoiceMode; //选择模式标记 + /* + * 桌面widget的属性,包括编号和类型 + */ public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; + /* + * 函数功能:初始化便签链接器 + * 函数实现:根据传进来的内容设置相关变量 + */ public NotesListAdapter(Context context) { - super(context, null); - mSelectedIndex = new HashMap(); + super(context, null); //父类对象置空 + mSelectedIndex = new HashMap(); //新建选项下标的hash表 mContext = context; mNotesCount = 0; } @Override + /* + * 函数功能:新建一个视图来存储光标所指向的数据 + * 函数实现:使用兄弟类NotesListItem新建一个项目选项 + */ 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) { + //若view是NotesListItem的一个实例 NoteItemData itemData = new NoteItemData(context, cursor); ((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); + //则新建一个项目选项并且用bind跟将view和鼠标,内容,便签数据捆绑在一起 } } + /* + * 函数功能:设置勾选框 + * 函数实现:如下注释 + */ public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); + //根据定位和是否勾选设置下标 notifyDataSetChanged(); + //在修改后刷新activity } + /* + * 函数功能:判断单选按钮是否勾选 + */ public boolean isInChoiceMode() { return mChoiceMode; } + /* + * 函数功能:设置单项选项框 + * 函数实现:重置下标并且根据参数mode设置选项 + */ 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) { @@ -87,30 +111,47 @@ public class NotesListAdapter extends CursorAdapter { } } } + //遍历所有光标可用的位置在判断为便签类型之后勾选单项框 } + /* + * 函数功能:建立选择项的下标列表 + * 函数实现:如下注释 + */ public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); + //建立hash表 for (Integer position : mSelectedIndex.keySet()) { + //遍历所有的关键 if (mSelectedIndex.get(position) == true) { + //若光标位置可用 Long id = getItemId(position); if (id == Notes.ID_ROOT_FOLDER) { + //原文件不需要添加 Log.d(TAG, "Wrong item id, should not happen"); } else { itemSet.add(id); } + //则将id该下标假如选项集合中 + } } return itemSet; } + /* + * 函数功能:建立桌面Widget的选项表 + * 函数实现:如下注释 + */ public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Cursor c = (Cursor) getItem(position); + //以上4句和getSelectedItemIds一样,不再重复 if (c != null) { + //光标位置可用的话就建立新的Widget属性并编辑下标和类型,最后添加到选项集中 AppWidgetAttribute widget = new AppWidgetAttribute(); NoteItemData item = new NoteItemData(mContext, c); widget.widgetId = item.getWidgetId(); @@ -128,26 +169,42 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + /* + * 函数功能:获取选项个数 + * 函数实现:如下注释 + */ public int getSelectedCount() { Collection values = mSelectedIndex.values(); + //首先获取选项下标的值 if (null == values) { return 0; } Iterator iter = values.iterator(); + //初始化叠加器 int count = 0; while (iter.hasNext()) { if (true == iter.next()) { + //若value值为真计数+1 count++; } } 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; @@ -156,29 +213,45 @@ public class NotesListAdapter extends CursorAdapter { } @Override + /* + * 函数功能:在activity内容发生局部变动的时候回调该函数计算便签的数量 + * 函数实现:如下注释 + */ protected void onContentChanged() { super.onContentChanged(); + //执行基类函数 calcNotesCount(); } @Override + /* + * 函数功能:在activity光标发生局部变动的时候回调该函数计算便签的数量 + */ 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++; + //若该位置不为空并且文本类型为便签就+1 } } else { Log.e(TAG, "Invalid cursor"); return; } + //否则报错 } } -} +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java index 1221e80..fa8b3d2 100644 --- a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -1,19 +1,3 @@ -/* - * 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.content.Context; @@ -30,37 +14,43 @@ 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); + super(context); //super()它的主要作用是调整调用父类构造函数的顺序 + inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout + //findViewById用于从contentView中查找指定ID的View,转换出来的形式根据需要而定; mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTitle = (TextView) findViewById(R.id.tv_title); mTime = (TextView) findViewById(R.id.tv_time); mCallName = (TextView) findViewById(R.id.tv_name); mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } - + ///根据data的属性对各个控件的属性的控制,主要是可见性Visibility,内容setText,格式setTextAppearance 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); + mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见 + mCheckBox.setChecked(checked); ///格子打钩 } else { mCheckBox.setVisibility(View.GONE); } mItemData = data; + ///设置控件属性,一共三种情况,由data的id和父id是否与保存到文件夹的id一致来决定 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.GONE); mAlert.setVisibility(View.VISIBLE); + //设置该textview的style mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + //settext为设置内容 mTitle.setText(context.getString(R.string.call_record_folder_name) + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setImageResource(R.drawable.call_record); @@ -69,8 +59,9 @@ public class NotesListItem extends LinearLayout { 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.setImageResource(R.drawable.clock);//图片来源的设置 mAlert.setVisibility(View.VISIBLE); } else { mAlert.setVisibility(View.GONE); @@ -78,45 +69,48 @@ public class NotesListItem extends LinearLayout { } else { mCallName.setVisibility(View.GONE); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - + ///设置title格式 if (data.getType() == Notes.TYPE_FOLDER) { mTitle.setText(data.getSnippet() + context.getString(R.string.format_folder_files_count, - data.getNotesCount())); + data.getNotesCount())); mAlert.setVisibility(View.GONE); } else { mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); if (data.hasAlert()) { - mAlert.setImageResource(R.drawable.clock); + mAlert.setImageResource(R.drawable.clock);///设置图片来源 mAlert.setVisibility(View.VISIBLE); } else { mAlert.setVisibility(View.GONE); } } } - mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + ///设置内容,获取相关时间,从data里编辑的日期中获取 + mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); setBackground(data); } - + //根据data的文件属性来设置背景 private void setBackground(NoteItemData data) { int id = data.getBgColorId(); + //,若是note型文件,则4种情况,对于4种不同情况的背景来源 if (data.getType() == Notes.TYPE_NOTE) { + //单个数据并且只有一个子文件夹 if (data.isSingle() || data.isOneFollowingFolder()) { setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); - } else if (data.isLast()) { + } else if (data.isLast()) {//是最后一个数据 setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); - } else if (data.isFirst() || data.isMultiFollowingFolder()) { + } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹 setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); } else { setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } } else { + //若不是note直接调用文件夹的背景来源 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } - public NoteItemData getItemData() { return mItemData; } -} +} \ No newline at end of file diff --git a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..6f8a89f 100644 --- a/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/src/Notes/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java @@ -1,19 +1,3 @@ -/* - * 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.accounts.Account; @@ -47,66 +31,92 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; - +/* + *该类功能:NotesPreferenceActivity,在小米便签中主要实现的是对背景颜色和字体大小的数据储存。 + * 继承了PreferenceActivity主要功能为对系统信息和配置进行自动保存的Activity + */ 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; + //账户的hash标记 @Override + /* + *函数功能:创建一个activity,在函数里要完成所有的正常静态设置 + *参数:Bundle icicle:存放了 activity 当前的状态 + *函数实现:如下注释 + */ protected void onCreate(Bundle icicle) { + //先执行父类的创建函数 super.onCreate(icicle); /* using the app icon for navigation */ getActionBar().setDisplayHomeAsUpEnabled(true); + //给左上角图标的左边加上一个返回的图标 addPreferencesFromResource(R.xml.preferences); + //添加xml来源并显示 xml 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); + //获取listvivew,ListView的作用:用于列出所有选择 getListView().addHeaderView(header, null, true); + //在listview组件上方添加其他组件 } @Override + /* + * 函数功能:activity交互功能的实现,用于接受用户的输入 + * 函数实现:如下注释 + */ protected void onResume() { + //先执行父类 的交互实现 super.onResume(); // need to set sync account automatically if user has added a new // account if (mHasAddedAccount) { + //若用户新加了账户则自动设置同步账户 Account[] accounts = getGoogleAccounts(); + //获取google同步账户 if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + //若原账户不为空且当前账户有增加 for (Account accountNew : accounts) { boolean found = false; for (Account accountOld : mOriAccounts) { if (TextUtils.equals(accountOld.name, accountNew.name)) { + //更新账户 found = true; break; } } if (!found) { setSyncAccount(accountNew.name); + //若是没有找到旧的账户,那么同步账号中就只添加新账户 break; } } @@ -114,58 +124,83 @@ public class NotesPreferenceActivity extends PreferenceActivity { } refreshUI(); + //刷新标签界面 } @Override + /* + * 函数功能:销毁一个activity + * 函数实现:如下注释 + */ protected void onDestroy() { if (mReceiver != null) { unregisterReceiver(mReceiver); + //注销接收器 } super.onDestroy(); + //执行父类的销毁动作 } + /* + * 函数功能:重新设置账户信息 + * 函数实现:如下注释 + */ private void loadAccountPreference() { mAccountCategory.removeAll(); - + //销毁所有的分组 Preference accountPref = new Preference(this); + //建立首选项 final String defaultAccount = getSyncAccountName(this); accountPref.setTitle(getString(R.string.preferences_account_title)); accountPref.setSummary(getString(R.string.preferences_account_summary)); + //设置首选项的大标题和小标题 accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 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 { + //若在没有同步的情况下,则在toast中显示不能修改 Toast.makeText(NotesPreferenceActivity.this, - R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) .show(); } return true; } }); + //根据新建首选项编辑新的账户分组 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() { public void onClick(View v) { GTaskSyncService.cancelSync(NotesPreferenceActivity.this); } }); + //设置按钮显示的文本为“取消同步”以及监听器 } else { syncButton.setText(getString(R.string.preferences_button_sync_immediately)); syncButton.setOnClickListener(new View.OnClickListener() { @@ -173,50 +208,67 @@ public class NotesPreferenceActivity extends PreferenceActivity { GTaskSyncService.startSync(NotesPreferenceActivity.this); } }); + //若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器 } syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); + //设置按键可用还是不可用 // set last sync time + // 设置最终同步时间 if (GTaskSyncService.isSyncing()) { + //若是在同步的情况下 lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE); + // 根据当前同步服务器设置时间显示框的文本以及可见性 } else { + //若是非同步情况 long lastSyncTime = getLastSyncTime(this); if (lastSyncTime != 0) { lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, DateFormat.format(getString(R.string.preferences_last_sync_time_format), lastSyncTime))); lastSyncTimeView.setVisibility(View.VISIBLE); + //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性 } else { + //若时间为空直接设置为不可见状态 lastSyncTimeView.setVisibility(View.GONE); } } } - + /* + *函数功能:刷新标签界面 + *函数实现:调用上文设置账号和设置按键两个函数来实现 + */ 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)); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - + //设置标题以及子标题的内容 dialogBuilder.setCustomTitle(titleView); dialogBuilder.setPositiveButton(null, null); - + //设置对话框的自定义标题,建立一个YES的按钮 Account[] accounts = getGoogleAccounts(); String defAccount = getSyncAccountName(this); - + //获取同步账户信息 mOriAccounts = accounts; mHasAddedAccount = false; if (accounts.length > 0) { + //若账户不为空 CharSequence[] items = new CharSequence[accounts.length]; final CharSequence[] itemMapping = items; int checkedItem = -1; @@ -224,83 +276,119 @@ public class NotesPreferenceActivity extends PreferenceActivity { for (Account account : accounts) { if (TextUtils.equals(account.name, defAccount)) { checkedItem = index; + //在账户列表中查询到所需账户 } items[index++] = account.name; } dialogBuilder.setSingleChoiceItems(items, checkedItem, + //在对话框建立一个单选的复选框 new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { setSyncAccount(itemMapping[which].toString()); dialog.dismiss(); + //取消对话框 refreshUI(); } + //设置点击后执行的事件,包括检录新同步账户和刷新标签界面 }); + //建立对话框网络版的监听器 } View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); dialogBuilder.setView(addAccountView); + //给新加账户对话框设置自定义样式 final AlertDialog dialog = dialogBuilder.show(); + //显示对话框 addAccountView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { mHasAddedAccount = true; + //将新加账户的hash置true Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); + //建立网络建立组件 intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { - "gmail-ls" + "gmail-ls" }); startActivityForResult(intent, -1); + //跳回上一个选项 dialog.dismiss(); } }); + //建立新加账户对话框的监听器 } + /* + * 函数功能:显示账户选择对话框和相关账户操作 + * 函数实现:如下注释 + */ 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, getSyncAccountName(this))); TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); 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), getString(R.string.preferences_menu_cancel) }; + //定义一些标记字符串 dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel public void onClick(DialogInterface dialog, int which) { + //按键功能,由which来决定 if (which == 0) { + //进入账户选择对话框 showSelectAccountAlertDialog(); } else if (which == 1) { + //删除账户并且跟新便签界面 removeSyncAccount(); refreshUI(); } } }); dialogBuilder.show(); + //显示对话框 } + /* + *函数功能:获取谷歌账户 + *函数实现:通过账户管理器直接获取 + */ 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); SharedPreferences.Editor editor = settings.edit(); + //编辑共享的首选项 if (account != null) { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); } else { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + //将该账号加入到首选项中 + editor.commit(); + //提交修改的数据 + - // clean up last sync time setLastSyncTime(this, 0); + //将最后同步时间清零 // clean up local gtask related info new Thread(new Runnable() { @@ -311,23 +399,33 @@ public class NotesPreferenceActivity extends PreferenceActivity { getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).start(); + //重置当地同步任务的信息 Toast.makeText(NotesPreferenceActivity.this, getString(R.string.preferences_toast_success_set_accout, account), Toast.LENGTH_SHORT).show(); + //将toast的文本信息置为“设置账户成功”并显示出来 } } - + /* + * 函数功能:删除同步账户 + * 函数实现:如下注释: + */ private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); + //设置共享首选项 + if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); + //假如当前首选项中有账户就删除 } if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { editor.remove(PREFERENCE_LAST_SYNC_TIME); + //删除当前首选项中有账户时间 } editor.commit(); + //提交更新后的数据 // clean up local gtask related info new Thread(new Runnable() { @@ -338,51 +436,79 @@ public class NotesPreferenceActivity extends PreferenceActivity { getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).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); SharedPreferences.Editor editor = settings.edit(); + // 从共享首选项中找到相关账户并获取其编辑器 editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); 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); } + /* + * 函数功能:接受同步信息 + * 函数实现:继承BroadcastReceiver + */ private class GTaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { refreshUI(); if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + //获取随广播而来的Intent中的同步服务的数据 TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); + //通过获取的数据在设置系统的状态 } } } + /* + * 函数功能:处理菜单的选项 + * 函数实现:如下注释 + * 参数:MenuItem菜单选项 + */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + //根据选项的id选择,这里只有一个主页 case android.R.id.home: Intent intent = new Intent(this, NotesListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); return true; + //在主页情况下在创建连接组件intent,发出清空的信号并开始一个相应的activity default: return false; } } } + \ No newline at end of file