diff --git a/src/net/micode/notes/ui/DropdownMenu.java b/src/net/micode/notes/ui/DropdownMenu.java index 613dc74..6751211 100644 --- a/src/net/micode/notes/ui/DropdownMenu.java +++ b/src/net/micode/notes/ui/DropdownMenu.java @@ -27,34 +27,39 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import net.micode.notes.R; +//DropdownMenu类,用来创建和显示一个下拉菜单 public class DropdownMenu { - private Button mButton; - private PopupMenu mPopupMenu; - private Menu mMenu; + private Button mButton;//按钮成员变量,用于触发下拉菜单 + private PopupMenu mPopupMenu;//弹出菜单的成员变量,用于显示菜单项 + private Menu mMenu;//菜单成员变量,包含了具体的菜单项 + public DropdownMenu(Context context, Button button, int menuId) { - mButton = button; - mButton.setBackgroundResource(R.drawable.dropdown_icon); - mPopupMenu = new PopupMenu(context, mButton); - mMenu = mPopupMenu.getMenu(); - mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + mButton = button;//将传入的按钮引用赋值给mButton + mButton.setBackgroundResource(R.drawable.dropdown_icon);//设置这个按钮的背景为下拉图标 + mPopupMenu = new PopupMenu(context, mButton);//初始化PopupMenu + mMenu = mPopupMenu.getMenu();//获取PopupMenu关联的Menu对象 + mPopupMenu.getMenuInflater().inflate(menuId, mMenu);//使用传入的menuId资源填充Menu mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mPopupMenu.show(); - } + }//显示菜单 }); } + //设置菜单项点击监听器 public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } + //查找具体的菜单项 public MenuItem findItem(int id) { return mMenu.findItem(id); } + //设置按钮的标题文字 public void setTitle(CharSequence title) { mButton.setText(title); } diff --git a/src/net/micode/notes/ui/FoldersListAdapter.java b/src/net/micode/notes/ui/FoldersListAdapter.java index 96b77da..62132b7 100644 --- a/src/net/micode/notes/ui/FoldersListAdapter.java +++ b/src/net/micode/notes/ui/FoldersListAdapter.java @@ -24,57 +24,66 @@ import android.widget.CursorAdapter; import android.widget.LinearLayout; import android.widget.TextView; -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; - +import net.micode.notes.R;//引入资源文件 +import net.micode.notes.data.Notes;//引入与Notes相关的数据处理类 +import net.micode.notes.data.Notes.NoteColumns;//引入NOte数据表中的字段 +//FoldersListAdapter 类是CursorAdapter的一个子类,用于提供文件夹数据的视图 public class FoldersListAdapter extends CursorAdapter { + //定义查询结果的列,这里只用到ID和SNIPET列 public static final String [] PROJECTION = { NoteColumns.ID, NoteColumns.SNIPPET }; + //定义列的索引,方便读取数据时使用 public static final int ID_COLUMN = 0; public static final int NAME_COLUMN = 1; + //该类的构造函数 public FoldersListAdapter(Context context, Cursor c) { super(context, c); // TODO Auto-generated constructor stub } + //必须重写的newView方法,用于创建新的列表项视图 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return new FolderListItem(context); + return new FolderListItem(context);//创建一个新的FoldersListItem实例 } + //用于将数据绑定到视图上 @Override public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof FolderListItem) { + if (view instanceof FolderListItem) {//检查视图类型是否正确 + //根据ID列判断是否根文件夹,是则使用预定义字符串,否则使用数据库中的名字 String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); - ((FolderListItem) view).bind(folderName); + ((FolderListItem) view).bind(folderName);//调用FolderListItem的bind方法绑定文件夹名称 } } + //辅助方法,根据位置获取文件夹名称 public String getFolderName(Context context, int position) { - Cursor cursor = (Cursor) getItem(position); + Cursor cursor = (Cursor) getItem(position);//获取对应位置的游标 + //同样根据ID列判断文件夹名称 return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); } + //FolderListItem是LinearLayout的一个内部子类,用于表示每一项文件夹 private class FolderListItem extends LinearLayout { - private TextView mName; + private TextView mName;//文件夹名称的Textview public FolderListItem(Context context) { - super(context); - inflate(context, R.layout.folder_list_item, this); - mName = (TextView) findViewById(R.id.tv_folder_name); + super(context);//调用父类构造函数 + inflate(context, R.layout.folder_list_item, this);//填充布局 + mName = (TextView) findViewById(R.id.tv_folder_name);//获取布局中的TextView } public void bind(String name) { mName.setText(name); - } + }//设置文件夹名称到Textview } } diff --git a/src/net/micode/notes/ui/LoginActivity.java b/src/net/micode/notes/ui/LoginActivity.java index c41a970..9f9dc5b 100644 --- a/src/net/micode/notes/ui/LoginActivity.java +++ b/src/net/micode/notes/ui/LoginActivity.java @@ -24,7 +24,7 @@ public class LoginActivity extends Activity{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - //获取名为 "user management" 的SharedPreferences 对象,用于读取或存储轻量级的键值对数据 + //获取名为 "user management" 的 SharedPreferences 对象,用于读取或存储轻量级的键值对数据 SharedPreferences pref = getSharedPreferences("user management", MODE_PRIVATE); //从 SharedPreferences 对象中获取名为 "user" 的布尔值,检查用户是否设置了密码 boolean User_boolean = pref.getBoolean("user", false); //检查用户是否设置了密码 diff --git a/src/net/micode/notes/ui/NoteEditActivity.java b/src/net/micode/notes/ui/NoteEditActivity.java index 696262f..35bc45b 100644 --- a/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/net/micode/notes/ui/NoteEditActivity.java @@ -78,7 +78,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener { private class HeadViewHolder { - public TextView tvModified; // + public TextView tvModified; public ImageView ivAlertIcon; diff --git a/src/net/micode/notes/ui/NoteItemData.java b/src/net/micode/notes/ui/NoteItemData.java index 0f5a878..4bdb638 100644 --- a/src/net/micode/notes/ui/NoteItemData.java +++ b/src/net/micode/notes/ui/NoteItemData.java @@ -25,8 +25,9 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; - +//NoteItemData 类的定义,用于封装便签应用中的便签项的数据。 public class NoteItemData { + //用于查询数据库时所需的列的数组,声明了查询笔记项需要的所有数据库列 static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.ALERTED_DATE, @@ -42,6 +43,7 @@ public class NoteItemData { NoteColumns.WIDGET_TYPE, }; + //下面一堆常量定义分别用于定位游标中每一列的位置 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 +57,31 @@ 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; //联系人电话 + + private boolean mIsLastItem; //是否为最后一项 + private boolean mIsFirstItem; //是否为第一项 + private boolean mIsOnlyOneItem; //是否只有一项 + private boolean mIsOneNoteFollowingFolder; //是否仅有一个笔记跟随文件夹 + private boolean mIsMultiNotesFollowingFolder; //是否有多个笔记跟随文件夹 + + //构造器:从提供的有表数据创建NoteItemData 对象 public NoteItemData(Context context, Cursor cursor) { + //从游标中读取每列的数据并赋值给相应的成员变量 mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); @@ -94,6 +99,7 @@ public class NoteItemData { mPhoneNumber = ""; if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + //如果父项是通话记录文件夹,获取电话号码并查找联系人的名字 mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) { mName = Contact.getContact(context, mPhoneNumber); @@ -106,9 +112,11 @@ public class NoteItemData { if (mName == null) { mName = ""; } + //调用该方法来判断笔记项在游标中的位置 checkPostion(cursor); } + //判断当前位于游标中的位置情况,如是否是第一个,最后一个等,并相应地设置布尔标记位 private void checkPostion(Cursor cursor) { mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false; @@ -116,6 +124,7 @@ public class NoteItemData { mIsMultiNotesFollowingFolder = false; mIsOneNoteFollowingFolder = false; + //如果当前项是笔记并且不是第一项,检查它的前一个项是否是文件夹或系统项 if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { int position = cursor.getPosition(); if (cursor.moveToPrevious()) { @@ -128,12 +137,14 @@ public class NoteItemData { } } if (!cursor.moveToNext()) { + //如果无法返回到之前的位置,抛出异常 throw new IllegalStateException("cursor move to previous but can't move back"); } } } } + //为了获取NoteItemData对象内容的公共访问方法 public boolean isOneFollowingFolder() { return mIsOneNoteFollowingFolder; } @@ -218,6 +229,7 @@ public class NoteItemData { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } + //一个静态方法用来从游标中直接获取类型 public static int getNoteType(Cursor cursor) { return cursor.getInt(TYPE_COLUMN); } diff --git a/src/net/micode/notes/ui/NotesListActivity.java b/src/net/micode/notes/ui/NotesListActivity.java index 6db3761..8976c5c 100644 --- a/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/net/micode/notes/ui/NotesListActivity.java @@ -795,6 +795,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt createNewNote(); } else if (itemId == R.id.menu_search) { onSearchRequested(); + } else if (itemId == R.id.menu_weather) { + startWeatherActivity(); } return true; } @@ -857,6 +859,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt from.startActivityIfNeeded(intent, -1); } + private void startWeatherActivity() { + Activity from = getParent() != null ? getParent() : this; + Intent intent = new Intent(from, WeatherActivity.class); + from.startActivity(intent); + + } + private class OnListItemClickListener implements OnItemClickListener { public void onItemClick(AdapterView parent, View view, int position, long id) { diff --git a/src/net/micode/notes/ui/NotesListAdapter.java b/src/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..ad96bdd 100644 --- a/src/net/micode/notes/ui/NotesListAdapter.java +++ b/src/net/micode/notes/ui/NotesListAdapter.java @@ -30,19 +30,22 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; - +//NoteListAdapter 类,用于将数据从Cursor绑定到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 static final String TAG = "NotesListAdapter";//用于日志记录的TAG + private Context mContext;//用于保存上下文的变量 + private HashMap mSelectedIndex;//保存被选中项的索引 + private int mNotesCount;//便签数量 + private boolean mChoiceMode;//是否选择模式 + //AppEidgetAttribute 是一个静态内部类,用于存储小部件属性 public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; + //构造函数,初始化上下文和被选中项的哈希表 public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); @@ -50,38 +53,47 @@ public class NotesListAdapter extends CursorAdapter { mNotesCount = 0; } + //为给定数据创建新的ListsItem视图 @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) { + //创建一个NoteItemData对象,用于从Cursor提取数据 NoteItemData itemData = new NoteItemData(context, cursor); + //绑定视图 ((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); } } + //设置指定位置的项是否被选中 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,8 +101,10 @@ public class NotesListAdapter extends CursorAdapter { } } + //获取选中项的ID集合 public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); + //遍历mSelectedIndex查找选中的项目 for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Long id = getItemId(position); @@ -105,8 +119,10 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + //获取选中的小部件集合 public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); + //同样遍历选中的项,但是这一次获取widgetId和widgetType for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Cursor c = (Cursor) getItem(position); @@ -128,6 +144,7 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + //获取选中项的数量 public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -143,11 +160,13 @@ 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,18 +174,21 @@ public class NotesListAdapter extends CursorAdapter { return mSelectedIndex.get(position); } + //当内容改变时更新便签数量 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } + //更改cursor时,同样更新便签数量 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } + //计算便签的数量,只计算类型为便签的项 private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { diff --git a/src/net/micode/notes/ui/NotesListItem.java b/src/net/micode/notes/ui/NotesListItem.java index 1221e80..5c7b680 100644 --- a/src/net/micode/notes/ui/NotesListItem.java +++ b/src/net/micode/notes/ui/NotesListItem.java @@ -29,18 +29,21 @@ import net.micode.notes.data.Notes; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - +//定义NotesListItem 类,扩展了LinearLayout 类,用于表示一个笔记的布局 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); + //从布局文件 note_item.xml中加载布局 inflate(context, R.layout.note_item, this); + //通过findViewById 方法获取对应的控件对象 mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTitle = (TextView) findViewById(R.id.tv_title); mTime = (TextView) findViewById(R.id.tv_time); @@ -48,7 +51,9 @@ public class NotesListItem extends LinearLayout { mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } + //绑定数据到当前的笔记项,设置显示内容和样式 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); @@ -56,8 +61,11 @@ public class NotesListItem extends LinearLayout { mCheckBox.setVisibility(View.GONE); } + //保存数据到成员变量中 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,6 +73,7 @@ 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); @@ -76,15 +85,18 @@ public class NotesListItem extends LinearLayout { mAlert.setVisibility(View.GONE); } } else { + //如果笔记既不是通话记录也不是通话记录文件夹中的一项 mCallName.setVisibility(View.GONE); 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); @@ -94,13 +106,17 @@ public class NotesListItem extends LinearLayout { } } } + //设置显示笔记最后修改时间,格式化为相对时间(如"5分钟前") 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)); @@ -112,10 +128,12 @@ public class NotesListItem extends LinearLayout { setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } } else { + //如果是文件夹,则使用文件夹的背景资源 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } + //获取当前笔记项的数据 public NoteItemData getItemData() { return mItemData; } diff --git a/src/net/micode/notes/ui/NotesPreferenceActivity.java b/src/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..6a303cf 100644 --- a/src/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/src/net/micode/notes/ui/NotesPreferenceActivity.java @@ -49,6 +49,7 @@ 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"; @@ -61,29 +62,33 @@ public class NotesPreferenceActivity extends PreferenceActivity { private static final String AUTHORITIES_FILTER_KEY = "authorities"; + //成员变量,用于管理偏好设置项 private PreferenceCategory mAccountCategory; - + //广播接收器,用于接收相关相同操作广播通知 private GTaskReceiver mReceiver; - + //存储原始的Google账号数组,用于比较之后是否有变化 private Account[] mOriAccounts; - + //标志变量,记录是否添加了新账户 private boolean mHasAddedAccount; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - /* using the app icon for navigation */ + //启用向上导航使用应用程序图标 getActionBar().setDisplayHomeAsUpEnabled(true); - + //从Preferences.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); - + //初始化账户数组为null mOriAccounts = null; + //为设置列表添加表头View View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); getListView().addHeaderView(header, null, true); } @@ -92,8 +97,8 @@ public class NotesPreferenceActivity extends PreferenceActivity { 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) { @@ -112,12 +117,13 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } } - + //刷新UI界面,可能涉及到帐号显示的更新 refreshUI(); } @Override protected void onDestroy() { + //注销广播接收器 if (mReceiver != null) { unregisterReceiver(mReceiver); } @@ -125,8 +131,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } private void loadAccountPreference() { + //清空账户分类列表,然后重新添加账户偏好设置项 mAccountCategory.removeAll(); - + //创建一个账户偏好项,配置标题、概要、并设置点击监听 + //如果正在同步过程中则无法更改账户 Preference accountPref = new Preference(this); final String defaultAccount = getSyncAccountName(this); accountPref.setTitle(getString(R.string.preferences_account_title)); @@ -154,117 +162,150 @@ public class NotesPreferenceActivity extends PreferenceActivity { mAccountCategory.addPreference(accountPref); } + //用于初始化并设置同步按钮的状态和功能的私有方法 private void loadSyncButton() { + // 通过 ID 查找同步按钮,它是一个 Button 对象 Button syncButton = (Button) findViewById(R.id.preference_sync_button); + // 通过 ID 查找表示上次同步时间的 TextView 对象 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() { public void onClick(View v) { + //当按钮被点击时,启动同步 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); - + //获取当前设备上的 Google 账户列表 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; int index = 0; 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) { + // 当用户选择一个账户时,设置该账户为同步账户,并刷新UI 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; Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { "gmail-ls" }); + // 启动活动,请求码设置为-1,可能表示这个启动是特殊的或者不需要处理返回结果 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), @@ -272,100 +313,138 @@ public class NotesPreferenceActivity extends PreferenceActivity { }; dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + // 当用户在列表中选择选项时,根据选择执行相应操作 if (which == 0) { + // 如果选择“更改账户”,显示选择账户的对话框 showSelectAccountAlertDialog(); } else if (which == 1) { + //如果选择“移除账户”,移除同步账户并刷新UI removeSyncAccount(); refreshUI(); } + // 没有必要处理取消选项,因为点击会自动关闭对话框 } }); + // 显示对话框 dialogBuilder.show(); } + // 获取设备中所有Google账户的数组 private Account[] getGoogleAccounts() { + // 通过AccountManager获取账户管理实例 AccountManager accountManager = AccountManager.get(this); + //调用getAccountsByType方法获取类型为"com.google"的所有账户,即Google账户 return accountManager.getAccountsByType("com.google"); } - + // 设定或改变同步账号 private void setSyncAccount(String account) { + // 如果当前同步账号不是用户选定的账号 if (!getSyncAccountName(this).equals(account)) { + // 获取SharedPreferences的实例来存储偏好设置 SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + //创建一个SharedPreferences.Editor进行编辑 SharedPreferences.Editor editor = settings.edit(); + //如果传入账号不是null,将其存储到SharedPreferences if (account != null) { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); } else { + // 如果是null则存储空字符串"" editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + // 将更改提交到SharedPreferences editor.commit(); - // clean up last sync time + // 将最后同步时间设置为0(清空) setLastSyncTime(this, 0); - // clean up local gtask related info + // 在新线程中清空与同步相关的本地信息 new Thread(new Runnable() { public void run() { + //创建ContentValues以存储需要更新的值 ContentValues values = new ContentValues(); - values.put(NoteColumns.GTASK_ID, ""); - values.put(NoteColumns.SYNC_ID, 0); + values.put(NoteColumns.GTASK_ID, ""); // 清空GTASK_ID + values.put(NoteColumns.SYNC_ID, 0); // 设置SYNC_ID为0 + // 调用ContentResolver以更新数据库中的信息 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(); } } + // 移除同步账号 private void removeSyncAccount() { + // 获取SharedPreferences的实例来存储偏好设置 SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 创建一个SharedPreferences.Editor进行编辑 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); } + // 将更改提交到SharedPreferences editor.commit(); - // clean up local gtask related info + // 在新线程中清空与同步相关的本地信息 new Thread(new Runnable() { public void run() { + // 创建ContentValues以存储需要更新的值 ContentValues values = new ContentValues(); - values.put(NoteColumns.GTASK_ID, ""); - values.put(NoteColumns.SYNC_ID, 0); + values.put(NoteColumns.GTASK_ID, ""); // 清空GTASK_ID + values.put(NoteColumns.SYNC_ID, 0); // 设置SYNC_ID为0 getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); } }).start(); } + // 获取当前设定的同步账号 public static String getSyncAccountName(Context context) { + // 获取SharedPreferences的实例来存储偏好设置 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的实例来存储偏好设置 SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 创建一个SharedPreferences.Editor进行编辑 SharedPreferences.Editor editor = settings.edit(); + // 将同步时间设置为传入的time editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + // 将更改提交到SharedPreferences editor.commit(); } + // 获取最后同步时间 public static long getLastSyncTime(Context context) { + // 获取SharedPreferences的实例来存储偏好设置 SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 返回存储的最后同步时间,如果没有则返回0 return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } + // 内部类,广播接收器,用于接收同步状态变更的广播 private class GTaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + // 刷新界面显示 refreshUI(); if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { + // 找到同步状态的TextView,并设置显示文本 TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); @@ -374,9 +453,12 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + // 处理选项菜单的项被选择的事件 public boolean onOptionsItemSelected(MenuItem item) { + // 根据菜单项的ID进行不同的操作 switch (item.getItemId()) { case android.R.id.home: + // 如果是home项,创建意图,启动NotesListActivity Intent intent = new Intent(this, NotesListActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); diff --git a/src/net/micode/notes/ui/WeatherActivity.java b/src/net/micode/notes/ui/WeatherActivity.java new file mode 100644 index 0000000..016c0dc --- /dev/null +++ b/src/net/micode/notes/ui/WeatherActivity.java @@ -0,0 +1,127 @@ +package net.micode.notes.ui; + +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.os.AsyncTask; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; + +import net.micode.notes.R; +import com.google.gson.Gson; + +import java.io.IOException; +import java.util.HashMap; + + +public class WeatherActivity extends Activity +{ + private String responseData; + private TextView temperature; // 声明为成员变量 + private TextView location; + private TextView wea; + private TextView date; + private TextView humidity; + private TextView win; + private TextView pressure; + private TextView pm25; + private TextView airlevel; // 声明为成员变量 + OkHttpClient client = new OkHttpClient();//新建一个OkHttpClient对象 + //api地址http://tianqiapi.com/index/doc?version=day +//注册后获取参数拼接在url上 + private static String weatherUrl = "https://v1.yiketianqi.com/free/day?appid=94339557&version=v61&appsecret=9bIWWwDL&unescape=1"; + @SuppressLint("StaticFieldLeak") + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getActionBar().setDisplayHomeAsUpEnabled(true); + + setContentView(R.layout.activity_weather); + temperature = this.findViewById(R.id.temperature_tv); + location = this.findViewById(R.id.location_tv); + wea= this.findViewById(R.id.weather_tv); + date= this.findViewById(R.id.date_tv); + humidity= this.findViewById(R.id.humidity_tv); + win= this.findViewById(R.id.win_tv); + pressure= this.findViewById(R.id.pressure_tv); + pm25= this.findViewById(R.id.pm2_5_tv); + airlevel= this.findViewById(R.id.airlevel_tv); + + + new AsyncTask() { + @Override + protected String doInBackground(Void... voids) { + OkHttpClient client = new OkHttpClient(); + Request request = new Request.Builder() + .url(weatherUrl) + .build(); + + try { + Response response = client.newCall(request).execute(); + String responseData = response.body().string(); + Log.d("NetworkData", responseData); + return responseData; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected void onPostExecute(String responseData) { +// if (responseData != null) { +// Gson gson = new Gson(); +// HashMap map = gson.fromJson(responseData, HashMap.class); +// temperature.setText("温度:"+(CharSequence) map.get("tem")); +// location.setText("城市:"+(CharSequence) map.get("city")); +// wea.setText("天气:"+map.get("wea").toString()); +// date.setText("日期:"+map.get("date").toString()); +// humidity.setText("湿度:"+map.get("humidity").toString()); +// win.setText("风向:"+map.get("win").toString()); +// pressure.setText("气压:"+map.get("pressure").toString()); +// pm25.setText("PM2.5:"+map.get("air_pm25").toString()); +// airlevel.setText("空气等级:"+map.get("air_level").toString()); +// } + + if (responseData != null) { + Gson gson = new Gson(); + HashMap map = gson.fromJson(responseData, HashMap.class); + // 在此之后,检查 map 中每个键对应的值是否为 null + if (map.get("tem") != null) { + temperature.setText("温度: " + map.get("tem").toString()); + } + if (map.get("city") != null) { + location.setText("城市: " + map.get("city").toString()); + } + // 对其他字段也做相同的检查 + if (map.get("wea") != null) { + wea.setText("天气: " + map.get("wea").toString()); + } + if (map.get("date") != null) { + date.setText("日期: " + map.get("date").toString()); + } + if (map.get("humidity") != null) { + humidity.setText("湿度: " + map.get("humidity").toString()); + } + if (map.get("win") != null) { + win.setText("风向: " + map.get("win").toString()); + } + if (map.get("pressure") != null) { + pressure.setText("气压: " + map.get("pressure").toString()); + } + if (map.get("air_pm25") != null) { + pm25.setText("PM2.5: " + map.get("air_pm25").toString()); + } + if (map.get("air_level") != null) { + airlevel.setText("空气等级: " + map.get("air_level").toString()); + } + } + + } + }.execute(); + } +}