From b9a6976510574b368d488e20136cb680b6eb500e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AB=98=E6=BA=90?= <3056440880@qq.com> Date: Thu, 6 Apr 2023 20:48:11 +0800 Subject: [PATCH 2/2] gaoyuan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 高源 <3056440880@qq.com> --- src/ui/AlarmAlertActivity.java | 63 ++++++++++-- src/ui/AlarmInitReceiver.java | 9 ++ src/ui/AlarmReceiver.java | 2 + src/ui/DateTimePicker.java | 63 +++++++++++- src/ui/DateTimePickerDialog.java | 38 ++++++-- src/ui/DropdownMenu.java | 11 +++ src/ui/FoldersListAdapter.java | 14 +++ src/ui/NoteEditActivity.java | 142 +++++++++++++++++++++++++--- src/ui/NoteEditText.java | 19 +++- src/ui/NoteItemData.java | 6 ++ src/ui/NotesListAdapter.java | 14 +++ src/ui/NotesListItem.java | 9 ++ src/ui/NotesPreferenceActivity.java | 28 +++++- 13 files changed, 378 insertions(+), 40 deletions(-) diff --git a/src/ui/AlarmAlertActivity.java b/src/ui/AlarmAlertActivity.java index 85723be..75de484 100644 --- a/src/ui/AlarmAlertActivity.java +++ b/src/ui/AlarmAlertActivity.java @@ -41,30 +41,42 @@ import java.io.IOException; public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { - private long mNoteId; - private String mSnippet; - private static final int SNIPPET_PREW_MAX_LEN = 60; + private long mNoteId; //用于存储笔记Id + private String mSnippet; //用于存储笔记内容摘要 + private static final int SNIPPET_PREW_MAX_LEN = 60;//限制笔记长度 MediaPlayer mPlayer; + + //onCreate() 函数在 Activity 初始化时调用,用于初始化 + //savedInstanceState 该参数用于恢复 Activity 状态。 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + //界面显示:无标题 requestWindowFeature(Window.FEATURE_NO_TITLE); + //获取 Window 对象,添加 FLAG_SHOW_WHEN_LOCKED 标记 final Window win = getWindow(); win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); + //如果屏幕没亮,添加一系列 屏幕唤醒标记 if (!isScreenOn()) { - win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON - | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON //保持窗口常亮 + | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON //点亮窗口 + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON //允许在窗口点亮时锁屏 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); } + //获取Intent对象 Intent intent = getIntent(); + //从Intent对象中获取笔记的 Id 和内容 try { mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); + + //根据便签Id从数据库中获取便签内容 + //getContentResolver() 实现数据共享,实例存储 mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) @@ -74,6 +86,7 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD return; } + //创建 MediaPlayer 对象,根据笔记是否存在数据库中来展示操作对话框或者直接退出 mPlayer = new MediaPlayer(); if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { showActionDialog(); @@ -83,29 +96,38 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } + //用于判断屏幕是否被唤醒(亮着) private boolean isScreenOn() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); return pm.isScreenOn(); } + //用于实现报警铃声的播放 private void playAlarmSound() { + //获取系统默认的铃声 Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); + //获取静音模式下允许响铃的类型 int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); + //判断闹钟铃声是否被静音模式影响 + //若被影响则设置播放音频类型为允许响铃的类型 + //若违背影响则设置音频为默认类型 if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { mPlayer.setAudioStreamType(silentModeStreams); } else { mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } try { - mPlayer.setDataSource(this, url); - mPlayer.prepare(); - mPlayer.setLooping(true); - mPlayer.start(); + mPlayer.setDataSource(this, url); //设置报警铃声 + mPlayer.prepare(); //准备同步 + mPlayer.setLooping(true); //设置循环播放 + mPlayer.start(); //开始播放 } catch (IllegalArgumentException e) { // TODO Auto-generated catch block + + //以下的 e.printStackTrace() 函数功能是抛出异常,还将显示出更深的调用信息 e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block @@ -119,32 +141,52 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } + + //创建一个对话框,标题为app的名字,内容为mSnippet,确定按钮为notealert_ok, + // 当屏幕开启时,取消按钮为notealert_enter,点击弹出框外部可以隐藏弹出框 private void showActionDialog() { AlertDialog.Builder dialog = new AlertDialog.Builder(this); + //为对话框设置标题 dialog.setTitle(R.string.app_name); + //为对话框设置内容 dialog.setMessage(mSnippet); + //给对话框添加“OK”按钮 dialog.setPositiveButton(R.string.notealert_ok, this); + if (isScreenOn()) { + //给对话框添加“CANCEL”按钮 dialog.setNegativeButton(R.string.notealert_enter, this); } + //给对话框设置监听器 dialog.show().setOnDismissListener(this); } + //当对话框界面内某个按钮被点击时调用以下方法 + // 接收 对话框界面 和 点击的按钮位置which 为参数 public void onClick(DialogInterface dialog, int which) { + //switch语句,根据按钮位置的不同,执行不同的操作 switch (which) { + //当点击点击的是返回按键 case DialogInterface.BUTTON_NEGATIVE: + //将编辑的便签内容传输到特定类中 Intent intent = new Intent(this, NoteEditActivity.class); + //设置操作属性 intent.setAction(Intent.ACTION_VIEW); + //创建一个特定的Note Id传输给便签 intent.putExtra(Intent.EXTRA_UID, mNoteId); + //开始操作 startActivity(intent); break; + //如果是其他按钮被点击则不执行任何操作 default: break; } } public void onDismiss(DialogInterface dialog) { + //终止报警声音 stopAlarmSound(); + //完成动作 finish(); } @@ -152,6 +194,7 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD if (mPlayer != null) { mPlayer.stop(); mPlayer.release(); + //释放 MediaPlayer(媒体播放器)对象 mPlayer = null; } } diff --git a/src/ui/AlarmInitReceiver.java b/src/ui/AlarmInitReceiver.java index f221202..edd2b85 100644 --- a/src/ui/AlarmInitReceiver.java +++ b/src/ui/AlarmInitReceiver.java @@ -28,8 +28,10 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; +//定时向用户推送闹钟提醒 public class AlarmInitReceiver extends BroadcastReceiver { + //对数据库的操作,调用笔记Id和时钟时间 private static final String [] PROJECTION = new String [] { NoteColumns.ID, NoteColumns.ALERTED_DATE @@ -38,16 +40,23 @@ public class AlarmInitReceiver extends BroadcastReceiver { private static final int COLUMN_ID = 0; private static final int COLUMN_ALERTED_DATE = 1; + //重写 BroadcastReceiver 的 onReceive 方法 @Override public void onReceive(Context context, Intent intent) { + + //获取当前系统时间 long currentDate = System.currentTimeMillis(); + + //查询数据库,找出大于当前系统时间、类型相等的笔记的记录 Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, new String[] { String.valueOf(currentDate) }, null); + //当查询结果不为空时 if (c != null) { + // 如果有符合要求的记录,就遍历每一条记录 if (c.moveToFirst()) { do { long alertDate = c.getLong(COLUMN_ALERTED_DATE); diff --git a/src/ui/AlarmReceiver.java b/src/ui/AlarmReceiver.java index 54e503b..1b4d096 100644 --- a/src/ui/AlarmReceiver.java +++ b/src/ui/AlarmReceiver.java @@ -24,6 +24,8 @@ public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { intent.setClass(context, AlarmAlertActivity.class); + + //为intent对象添加一个标志,表示它将启动一个新的任务栈 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } diff --git a/src/ui/DateTimePicker.java b/src/ui/DateTimePicker.java index 496b0cd..ab88e69 100644 --- a/src/ui/DateTimePicker.java +++ b/src/ui/DateTimePicker.java @@ -30,6 +30,7 @@ import android.widget.NumberPicker; public class DateTimePicker extends FrameLayout { + //初始化控件 private static final boolean DEFAULT_ENABLE_STATE = true; private static final int HOURS_IN_HALF_DAY = 12; @@ -46,11 +47,12 @@ public class DateTimePicker extends FrameLayout { private static final int AMPM_SPINNER_MIN_VAL = 0; private static final int AMPM_SPINNER_MAX_VAL = 1; + //设置闹钟的变量 private final NumberPicker mDateSpinner; private final NumberPicker mHourSpinner; private final NumberPicker mMinuteSpinner; private final NumberPicker mAmPmSpinner; - private Calendar mDate; + private Calendar mDate; //用于操作时间 private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; @@ -64,7 +66,20 @@ public class DateTimePicker extends FrameLayout { private OnDateTimeChangedListener mOnDateTimeChangedListener; + /* + 设置时间选择器的监听器, + 包括了日期选择器的监听器 mOnDateChangedListener, + 小时选择器的监听器 mOnHourChangedListener, + 分钟选择器的监听器mOnMinuteChangedListener, + 上午和下午选择器的监听器 mOnAmPmChangedListener + + */ + + + //日期选择器的监听器 private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { + + //将目前的日期传给 mDate,updateDateControl是同步操作 @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); @@ -73,39 +88,58 @@ public class DateTimePicker extends FrameLayout { } }; + //选择器的监听器 private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { boolean isDateChanged = false; Calendar cal = Calendar.getInstance(); if (!mIs24HourView) { + + //这里是对于12小时制时,晚上11点和12点交替时对日期的更改 if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, 1); isDateChanged = true; - } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { + } + + //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 + else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, -1); isDateChanged = true; } + + //这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改 if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { mIsAm = !mIsAm; updateAmPmControl(); } } else { + + //这里是对于24小时制时,晚上11点和12点交替时对日期的更改 if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, 1); isDateChanged = true; - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + } + + //这里是对于24小时制时,凌晨11点和12点交替时对日期的更改 + else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, -1); isDateChanged = true; } } + + //用数字选择器给 newHour 赋值 int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + + //将新的Hour值传给mDate mDate.set(Calendar.HOUR_OF_DAY, newHour); + onDateTimeChanged(); if (isDateChanged) { setCurrentYear(cal.get(Calendar.YEAR)); @@ -115,12 +149,13 @@ public class DateTimePicker extends FrameLayout { } }; + //分钟选择器的监听器 private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { int minValue = mMinuteSpinner.getMinValue(); int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; + int offset = 0; //设置一个小时改变的记录数据 if (oldVal == maxValue && newVal == minValue) { offset += 1; } else if (oldVal == minValue && newVal == maxValue) { @@ -144,6 +179,7 @@ public class DateTimePicker extends FrameLayout { } }; + // 上午和下午选择器的监听器 private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { @@ -163,34 +199,43 @@ public class DateTimePicker extends FrameLayout { int dayOfMonth, int hourOfDay, int minute); } + // 通过对数据库的访问,获取当前系统时间 public DateTimePicker(Context context) { this(context, System.currentTimeMillis()); } + // 获得一个天文数字(1970年至今的秒数),需要DateFormat将其变得有意义 public DateTimePicker(Context context, long date) { this(context, date, DateFormat.is24HourFormat(context)); } public DateTimePicker(Context context, long date, boolean is24HourView) { - super(context); + super(context);//获取系统时间 mDate = Calendar.getInstance(); mInitialising = true; mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + + //在控件中填充R.layout.datetime_picker布局 inflate(context, R.layout.datetime_picker, this); + //获取年月日Spinner,设置最小值和最大值,设置值改变监听 mDateSpinner = (NumberPicker) findViewById(R.id.date); mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + // 获取时Spinner,设置值改变监听 mHourSpinner = (NumberPicker) findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + + // 获取分Spinner,设置最小值、最大值、长按更新间隔和值改变监听 mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); mMinuteSpinner.setOnLongPressUpdateInterval(100); mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + // 获取上午/下午Spinner,设置最小值、最大值、显示值和值改变监听 String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); @@ -214,8 +259,12 @@ public class DateTimePicker extends FrameLayout { mInitialising = false; } + // 设置控件及其子控件的可用状态 @Override public void setEnabled(boolean enabled) { + + // 如果mIsEnabled变量与传入的enabled相同,则不进行操作, + // 否则将控件及其子控件的可用状态更新为enabled并更新mIsEnabled变量。 if (mIsEnabled == enabled) { return; } @@ -434,6 +483,7 @@ public class DateTimePicker extends FrameLayout { updateAmPmControl(); } + // 更新日期控件,根据当前日期更新展示的日期列表 private void updateDateControl() { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(mDate.getTimeInMillis()); @@ -448,6 +498,8 @@ public class DateTimePicker extends FrameLayout { mDateSpinner.invalidate(); } + // 更新上午/下午控件,如果是24小时制,则隐藏此控件; + // 否则根据当前选择的时间更新展示的上午/下午选项 private void updateAmPmControl() { if (mIs24HourView) { mAmPmSpinner.setVisibility(View.GONE); @@ -458,6 +510,7 @@ public class DateTimePicker extends FrameLayout { } } + // 更新小时控件,根据当前选择的时间更新展示的小时选项 private void updateHourControl() { if (mIs24HourView) { mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); diff --git a/src/ui/DateTimePickerDialog.java b/src/ui/DateTimePickerDialog.java index 2c47ba4..be8d4ae 100644 --- a/src/ui/DateTimePickerDialog.java +++ b/src/ui/DateTimePickerDialog.java @@ -31,37 +31,52 @@ import android.text.format.DateUtils; public class DateTimePickerDialog extends AlertDialog implements OnClickListener { - private Calendar mDate = Calendar.getInstance(); + private Calendar mDate = Calendar.getInstance();//便于时间操作 private boolean mIs24HourView; - private OnDateTimeSetListener mOnDateTimeSetListener; - private DateTimePicker mDateTimePicker; + private OnDateTimeSetListener mOnDateTimeSetListener;//时间日期滚动控件 + private DateTimePicker mDateTimePicker;//用于设置或获取日期和时间 public interface OnDateTimeSetListener { + + //在选择日期后返回结果 void OnDateTimeSet(AlertDialog dialog, long date); } public DateTimePickerDialog(Context context, long date) { super(context); mDateTimePicker = new DateTimePicker(context); - setView(mDateTimePicker); + setView(mDateTimePicker); //设置一个子视图 + + //设置一个监听器 mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { public void onDateTimeChanged(DateTimePicker view, int year, int month, int dayOfMonth, int hourOfDay, int minute) { + + //设置日期时间为用户选择的日期和时间 mDate.set(Calendar.YEAR, year); mDate.set(Calendar.MONTH, month); mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); mDate.set(Calendar.MINUTE, minute); + + // 更新标题为选择的日期和时间 updateTitle(mDate.getTimeInMillis()); } }); - mDate.setTimeInMillis(date); - mDate.set(Calendar.SECOND, 0); + + mDate.setTimeInMillis(date);// 初始化mDate对象 + mDate.set(Calendar.SECOND, 0);// 将秒数设为0 mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); + + // 为对话框设置一个确定按钮 setButton(context.getString(R.string.datetime_dialog_ok), this); + + // 为对话框设置一个取消按钮,传入null为点击事件处理器 setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); + + //根据当前的时间的时间格式设置是否使用24小时制显示时间 set24HourView(DateFormat.is24HourFormat(this.getContext())); - updateTitle(mDate.getTimeInMillis()); + updateTitle(mDate.getTimeInMillis());// 更新对话框的标题为初始日期和时间 } public void set24HourView(boolean is24HourView) { @@ -73,15 +88,24 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener } private void updateTitle(long date) { + + // 定义一个标识变量,用于根据给定的日期时间更新标题栏的显示 int flag = DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_TIME; + + // 根据用户选择的,决定是否使用24小时制 flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; + + // 将日期转化为字符串,并设置标题 setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); } + // 用于处理对话框的点击事件 public void onClick(DialogInterface arg0, int arg1) { + + // 如果有日期时间设置监听器,则传递当前日期时间 if (mOnDateTimeSetListener != null) { mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); } diff --git a/src/ui/DropdownMenu.java b/src/ui/DropdownMenu.java index 613dc74..0cdbfbc 100644 --- a/src/ui/DropdownMenu.java +++ b/src/ui/DropdownMenu.java @@ -28,16 +28,23 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import net.micode.notes.R; public class DropdownMenu { + // 定义一个按钮,用于显示和触发下拉菜单 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.setOnClickListener(new OnClickListener() { public void onClick(View v) { mPopupMenu.show(); @@ -45,16 +52,20 @@ public class DropdownMenu { }); } + // 用于设置下拉菜单点击监听器 public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { + // 如果弹出菜单不为空则为其设置监听器 if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } + // 可根据id查找菜单项 public MenuItem findItem(int id) { return mMenu.findItem(id); } + // 用于设置按钮标题 public void setTitle(CharSequence title) { mButton.setText(title); } diff --git a/src/ui/FoldersListAdapter.java b/src/ui/FoldersListAdapter.java index 96b77da..930f65d 100644 --- a/src/ui/FoldersListAdapter.java +++ b/src/ui/FoldersListAdapter.java @@ -29,27 +29,35 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; +// 指定每个列表项的布局模板,指定Cursor中的每个字段应该绑定到那些视图上 +// 以文件夹的形式展现给用户 public class FoldersListAdapter extends CursorAdapter { + // 指定查询时返回的列,NoteColumns是一个接口 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 } + // 创建一个新视图,并初始化子标签 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new FolderListItem(context); } + // 用于绑定视图和数据 @Override public void bindView(View view, Context context, Cursor cursor) { + // 判断视图对象是否为 FolderListItem 的实例 if (view instanceof FolderListItem) { String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); @@ -57,21 +65,27 @@ public class FoldersListAdapter extends CursorAdapter { } } + // 获取对应文件夹名称 public String getFolderName(Context context, int 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); } + // 用于显示文件夹名称 private class FolderListItem extends LinearLayout { private TextView mName; public FolderListItem(Context context) { super(context); + // 将布局文件加载到当前视图 inflate(context, R.layout.folder_list_item, this); + // 将获取到的TextView对象赋值给mName mName = (TextView) findViewById(R.id.tv_folder_name); } + // 将给定的名称设置到mName中 public void bind(String name) { mName.setText(name); } diff --git a/src/ui/NoteEditActivity.java b/src/ui/NoteEditActivity.java index 96a9ff8..689fb1f 100644 --- a/src/ui/NoteEditActivity.java +++ b/src/ui/NoteEditActivity.java @@ -74,16 +74,19 @@ import java.util.regex.Pattern; public class NoteEditActivity extends Activity implements OnClickListener, NoteSettingChangedListener, OnTextViewChangeListener { + // 头部视图空间 private class HeadViewHolder { + // 文本视图,显示修改的日期 public TextView tvModified; - + // 图像视图,显示警告图标 public ImageView ivAlertIcon; - + // 文本视图,显示警告日期 public TextView tvAlertDate; - + // 图像按钮,用于设置背景颜色 public ImageView ibSetBgColor; } + // 用于存储不同背景选择按钮的id和对应颜色 private static final Map sBgSelectorBtnsMap = new HashMap(); static { sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); @@ -93,6 +96,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); } + // 用于存储不同颜色值和对应的背景选择器的id private static final Map sBgSelectorSelectionMap = new HashMap(); static { sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); @@ -102,6 +106,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); } + // 用于存储不同字体大小按钮id和对应的字体大小 private static final Map sFontSizeBtnsMap = new HashMap(); static { sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); @@ -110,6 +115,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); } + // 用于存储不同字体大小和对应的字体选择器的id private static final Map sFontSelectorSelectionMap = new HashMap(); static { sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); @@ -119,45 +125,53 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private static final String TAG = "NoteEditActivity"; - + // 存储笔记的头部视图 private HeadViewHolder mNoteHeaderHolder; - + // 存储头部视图图面板 private View mHeadViewPanel; private View mNoteBgColorSelector; private View mFontSizeSelector; - + // 用于存储笔记编辑器 private EditText mNoteEditor; - + // 用于存储笔记编辑面板 private View mNoteEditorPanel; - + // 用于存储正在编辑的笔记 private WorkingNote mWorkingNote; - + // 用于存储用户的偏好设置 private SharedPreferences mSharedPrefs; - private int mFontSizeId; + private int mFontSizeId; + // 用于表示字体大小偏好设置键 private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; - + // 用于限制快捷方式图标标题的最大长度 private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; - + // 表示已被选中的标签符号 public static final String TAG_CHECKED = String.valueOf('\u221A'); + // 表示未被选中的标签符号 public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); - + // 用于存储文本编辑列表 private LinearLayout mEditTextList; + // 用于存储用户的输入内容 private String mUserQuery; + private Pattern mPattern; + // 在活动创建时进行一些初始化操作 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.note_edit); + // 如果传入的参数为空,且调用initActivityState方法传入getIntent方法返回的Intent对象返回false, + // 表示活动状态初始化失败,结束活动 if (savedInstanceState == null && !initActivityState(getIntent())) { finish(); return; } + // 初始化一些资源 initResources(); } @@ -165,20 +179,27 @@ public class NoteEditActivity extends Activity implements OnClickListener, * Current activity may be killed when the memory is low. Once it is killed, for another time * user load this activity, we should restore the former state */ + // 为防止内存不足时程序的终止,在这里有一个保存现场的函数 + // 用于在活动恢复时执行一些操作 @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { + // 创建一个新的Intent对象,设置其动作为Intent.ACTION_VIEW Intent intent = new Intent(Intent.ACTION_VIEW); + // 将Intent.EXTRA_UID作为额外数据放入intent对象中 intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); + // 如果初始化活动失败则结束活动 if (!initActivityState(intent)) { finish(); return; } + // 打印一条调试信息,表示从被杀死的活动中恢复 Log.d(TAG, "Restoring from killed activity"); } } + // 用于返回活动初始化状态 private boolean initActivityState(Intent intent) { /** * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, @@ -192,25 +213,30 @@ public class NoteEditActivity extends Activity implements OnClickListener, /** * Starting from the searched result */ + // 根据键值查找ID if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); } - + // 如果在内容提供者中,noteId对应的笔记不可见或者不是笔记类型 if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { Intent jump = new Intent(this, NotesListActivity.class); + //程序将跳转到上面声明的intent——jump startActivity(jump); showToast(R.string.error_note_not_exist); finish(); return false; } else { + // 否则向 load函数 传入当前活动和noteId作为参数,返回一个WorkingNote对象 mWorkingNote = WorkingNote.load(this, noteId); + // 如果正在编辑的笔记为空 if (mWorkingNote == null) { Log.e(TAG, "load note failed with note id" + noteId); finish(); return false; } } + // 获取当前活动的窗口对象,设置软输入状态为隐藏状态并调整大小 getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); @@ -243,6 +269,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } else { mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); + // 将空笔记转换为通话笔记 mWorkingNote.convertToCallNote(phoneNumber, callDate); } } else { @@ -258,6 +285,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, finish(); return false; } + // 设置其设置状态为改变监听器 mWorkingNote.setOnSettingStatusChangedListener(this); return true; } @@ -268,9 +296,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, initNoteScreen(); } + // 用于初始化笔记的界面和功能 private void initNoteScreen() { + // 设置笔记编辑器的文字外观,根据字体大小的id选择合适的资源 mNoteEditor.setTextAppearance(this, TextAppearanceResources .getTexAppearanceResource(mFontSizeId)); + // 设置外观 if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { switchToListMode(mWorkingNote.getContent()); } else { @@ -295,18 +326,23 @@ public class NoteEditActivity extends Activity implements OnClickListener, showAlertHeader(); } + // 设置闹钟显示 private void showAlertHeader() { + // 判断当前笔记是否有闹钟提醒 if (mWorkingNote.hasClockAlert()) { long time = System.currentTimeMillis(); + // 如果系统时间大于闹钟设置的时间,则闹钟时间设置为在系统时间的基础上加一个时间跨度 if (time > mWorkingNote.getAlertDate()) { mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); } else { mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); } + // 显示闹钟的开始图标 mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); } else { + // 如果当前的笔记没有闹钟提醒,就将警告日期文本和警告图标设置为不可见 mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); }; @@ -326,6 +362,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, * generate a id. If the editing note is not worth saving, there * is no id which is equivalent to create new note */ + //在创建一个新的标签时,先在数据库中匹配 + //如果不存在,那么先在数据库中存储 if (!mWorkingNote.existInDatabase()) { saveNote(); } @@ -333,27 +371,32 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); } + //用于分发触摸事件到合适的视图中 @Override public boolean dispatchTouchEvent(MotionEvent ev) { + // 判断当前笔记的颜色选择器在屏幕上是否可见,且在可触控范围内 if (mNoteBgColorSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mNoteBgColorSelector, ev)) { mNoteBgColorSelector.setVisibility(View.GONE); return true; } - + // 判断当前笔记的字体大小选择器是否可见,且在触控范围内 if (mFontSizeSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mFontSizeSelector, ev)) { mFontSizeSelector.setVisibility(View.GONE); return true; } + // 如果两个选择器在屏幕都不可见,则重新分配触发事件 return super.dispatchTouchEvent(ev); } + // 对屏幕触控的坐标进行操作 private boolean inRangeOfView(View view, MotionEvent ev) { int []location = new int[2]; view.getLocationOnScreen(location); int x = location[0]; int y = location[1]; + // 如果触控的位置超出了给定的范围 if (ev.getX() < x || ev.getX() > (x + view.getWidth()) || ev.getY() < y @@ -363,6 +406,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return true; } + //对标签各项属性内容的初始化 private void initResources() { mHeadViewPanel = findViewById(R.id.note_title); mNoteHeaderHolder = new HeadViewHolder(); @@ -379,6 +423,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, iv.setOnClickListener(this); } + //对字体大小的选择 mFontSizeSelector = findViewById(R.id.font_size_selector); for (int id : sFontSizeBtnsMap.keySet()) { View view = findViewById(id); @@ -400,12 +445,15 @@ public class NoteEditActivity extends Activity implements OnClickListener, @Override protected void onPause() { super.onPause(); + // 尝试保存当前笔记数据,并判断是否保持成功 if(saveNote()) { Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); } + // 清除一些设置状态 clearSettingState(); } + // 与桌面小部件的内容和外观同步 private void updateWidget() { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { @@ -425,8 +473,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, setResult(RESULT_OK, intent); } + // 在一个视图被点击时调用 public void onClick(View v) { + // 获取被点击的视图的id int id = v.getId(); + // 如果是背景颜色的按钮,则将背景颜色选择器和当前笔记背景颜色对应的视图设为可见 if (id == R.id.btn_set_bg_color) { mNoteBgColorSelector.setVisibility(View.VISIBLE); findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( @@ -452,6 +503,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + // 当按下返回键时执行操作 @Override public void onBackPressed() { if(clearSettingState()) { @@ -462,6 +514,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, super.onBackPressed(); } + // 用于清除一些设置状态 private boolean clearSettingState() { if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { mNoteBgColorSelector.setVisibility(View.GONE); @@ -473,6 +526,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return false; } + // 在改变背景颜色时执行的操作 public void onBackgroundColorChanged() { findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( View.VISIBLE); @@ -480,38 +534,53 @@ public class NoteEditActivity extends Activity implements OnClickListener, mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); } + // 在准备选择菜单执行的操作 @Override public boolean onPrepareOptionsMenu(Menu menu) { + // 判断Activity是否正在结束 if (isFinishing()) { return true; } clearSettingState(); + // 清空菜单所有项目 menu.clear(); + // 判断当前笔记的文件夹id是否是通话记录文件夹的id if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { + // 加载通话记录编辑菜单的资源 getMenuInflater().inflate(R.menu.call_note_edit, menu); } else { + // 加载普通笔记编辑菜单的资源 getMenuInflater().inflate(R.menu.note_edit, menu); } + // 笔记是否处于清单模式 if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + // 将菜单中的列表模式项目的标题设置为普通模式 menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); } else { + // 将菜单中的列表模式项目的标题设置为列表模式 menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); } + // 判断当前笔记是否有闹钟提醒 if (mWorkingNote.hasClockAlert()) { + // 将菜单中的提醒项目设置为不可见 menu.findItem(R.id.menu_alert).setVisible(false); } else { + // 将菜单中的删除提醒项目设置为不可见 menu.findItem(R.id.menu_delete_remind).setVisible(false); } return true; } + // 当选择菜单中的某个项目被点击调用时的操作 @Override public boolean onOptionsItemSelected(MenuItem item) { + // 根据被点击项目的id,进行不同的处理 switch (item.getItemId()) { case R.id.menu_new_note: createNewNote(); break; case R.id.menu_delete: + // 创建对话框,询问用户是否删除 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.alert_title_delete)); builder.setIcon(android.R.drawable.ic_dialog_alert); @@ -527,24 +596,30 @@ public class NoteEditActivity extends Activity implements OnClickListener, builder.show(); break; case R.id.menu_font_size: + // 将字体大小选择器和当前字体大小对应的视图设置为可见 mFontSizeSelector.setVisibility(View.VISIBLE); findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); break; case R.id.menu_list_mode: + // 根据当前笔记是否是清单模式,切换到相反的模式,并更新笔记的清单模式属性 mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? TextNote.MODE_CHECK_LIST : 0); break; case R.id.menu_share: getWorkingText(); + // 将文本发送给其他应用 sendTo(this, mWorkingNote.getContent()); break; case R.id.menu_send_to_desktop: + // 将当前笔记作为一个快捷方式发送到桌面 sendToDesktop(); break; case R.id.menu_alert: + // 设置一个闹钟提醒 setReminder(); break; case R.id.menu_delete_remind: + // 将当前笔记的提醒日期设置为0,并更新数据库 mWorkingNote.setAlertDate(0, false); break; default: @@ -553,13 +628,17 @@ public class NoteEditActivity extends Activity implements OnClickListener, return true; } + // 设置闹钟提醒 private void setReminder() { + // 用于显示日期时间选择器对话框 DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); d.setOnDateTimeSetListener(new OnDateTimeSetListener() { + // 根据用户选择的日期时间,设置当前笔记的提醒日期,并更新数据库 public void OnDateTimeSet(AlertDialog dialog, long date) { mWorkingNote.setAlertDate(date , true); } }); + // 显示对话框,让用户选择一个日期时间 d.show(); } @@ -567,13 +646,17 @@ public class NoteEditActivity extends Activity implements OnClickListener, * Share note to apps that support {@link Intent#ACTION_SEND} action * and {@text/plain} type */ + // 与其他应用共享文本信息 private void sendTo(Context context, String info) { Intent intent = new Intent(Intent.ACTION_SEND); + // 将要共享的文本信息作为额外数据放入意图中 intent.putExtra(Intent.EXTRA_TEXT, info); intent.setType("text/plain"); + // 启动一个活动,让用户选择一个可以接收这个意图的应用 context.startActivity(intent); } + // 创建一个新便签 private void createNewNote() { // Firstly, save current editing notes saveNote(); @@ -586,15 +669,19 @@ public class NoteEditActivity extends Activity implements OnClickListener, startActivity(intent); } + // 删除当前便签 private void deleteCurrentNote() { + // 如果正在编辑的笔记存有数据 if (mWorkingNote.existInDatabase()) { HashSet ids = new HashSet(); long id = mWorkingNote.getNoteId(); + // 判断是否为根文件夹id if (id != Notes.ID_ROOT_FOLDER) { ids.add(id); } else { Log.d(TAG, "Wrong note id, should not happen"); } + // 判断当前是否是同步模式 if (!isSyncMode()) { if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { Log.e(TAG, "Delete Note error"); @@ -608,10 +695,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, mWorkingNote.markDeleted(true); } + // 判断是否是同步模式 private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + // 笔记闹钟提醒发生变化时调用, set 是是否设置提醒的标志 public void onClockAlertChanged(long date, boolean set) { /** * User could set clock to an unsaved note, so before setting the @@ -620,15 +709,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, if (!mWorkingNote.existInDatabase()) { saveNote(); } + // 大于0,即保存到了数据库中,否则就是未保存到数据库 if (mWorkingNote.getNoteId() > 0) { + // 用于发送一个闹钟接收的广播,将当前笔记的uri作为数据放入意图中 Intent intent = new Intent(this, AlarmReceiver.class); intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); + // 用于包装上面的意图对象,以便在闹钟触发时发送广播 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); + // 获取一个闹钟管理器对象,用于设置或取消闹钟 AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); showAlertHeader(); + // 判断是否设置提醒 if(!set) { + // 取消之前设置的闹钟 alarmManager.cancel(pendingIntent); } else { + // 设置一个在指定日期时间触发的闹钟,并发送待定意图 alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); } } else { @@ -646,6 +742,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, updateWidget(); } + // 在用户删除某个文本编辑器时,将被删除的文本内容追加到前面或者后面的文本编辑器中,并更新列表的索引值 public void onEditTextDelete(int index, String text) { int childCount = mEditTextList.getChildCount(); if (childCount == 1) { @@ -672,6 +769,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, edit.setSelection(length); } + /* 在用户在某个文本编辑器中按下回车键时,创建一个新的文本编辑器, + * 并将当前的文本内容分割到两个文本编辑器中。 + * 实现在笔记应用中输入多行文本。 + */ public void onEditTextEnter(int index, String text) { /** * Should not happen, check for debug @@ -691,6 +792,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + // 将用户输入的文本转换成多个文本编辑器,以便用户在列表模式下编辑多行文本。 private void switchToListMode(String text) { mEditTextList.removeAllViews(); String[] items = text.split("\n"); @@ -708,6 +810,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, mEditTextList.setVisibility(View.VISIBLE); } + // 将用户输入的搜索关键词在完整文本中高亮显示,以便用户快速定位到相关内容 private Spannable getHighlightQueryResult(String fullText, String userQuery) { SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); if (!TextUtils.isEmpty(userQuery)) { @@ -725,6 +828,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return spannable; } + // 根据用户输入的文本内容和搜索关键词创建一个带有复选框和高亮效果的文本编辑器视图 private View getListItem(String item, int index) { View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); @@ -756,6 +860,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return view; } + // 根据用户在文本编辑器中输入或删除文本时,动态显示或隐藏复选框。 public void onTextChange(int index, boolean hasText) { if (index >= mEditTextList.getChildCount()) { Log.e(TAG, "Wrong index, should not happen"); @@ -768,6 +873,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + // 根据用户选择的模式,将文本编辑器从普通模式切换到列表模式,或者从列表模式切换到普通模式。 public void onCheckListModeChanged(int oldMode, int newMode) { if (newMode == TextNote.MODE_CHECK_LIST) { switchToListMode(mNoteEditor.getText().toString()); @@ -782,6 +888,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + // 根据笔记的模式,从文本编辑器或者文本编辑器列表中获取文本内容 private boolean getWorkingText() { boolean hasChecked = false; if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { @@ -805,6 +912,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return hasChecked; } + // 用于保存一个笔记应用的文本内容 private boolean saveNote() { getWorkingText(); boolean saved = mWorkingNote.saveNote(); @@ -821,6 +929,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, return saved; } + // 将笔记内容发送到桌面 private void sendToDesktop() { /** * Before send message to home, we should make sure that current @@ -856,6 +965,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } } + // 用于编辑小图标的标题 private String makeShortcutIconTitle(String content) { content = content.replace(TAG_CHECKED, ""); content = content.replace(TAG_UNCHECKED, ""); @@ -863,10 +973,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, SHORTCUT_ICON_TITLE_MAX_LEN) : content; } + // 显示提示的视图 private void showToast(int resId) { showToast(resId, Toast.LENGTH_SHORT); } + // 持续显示提示视图 private void showToast(int resId, int duration) { Toast.makeText(this, resId, duration).show(); } diff --git a/src/ui/NoteEditText.java b/src/ui/NoteEditText.java index 2afe2a8..1f7235e 100644 --- a/src/ui/NoteEditText.java +++ b/src/ui/NoteEditText.java @@ -37,6 +37,7 @@ import net.micode.notes.R; import java.util.HashMap; import java.util.Map; +// 继承EditText,设置标签设置文本框 public class NoteEditText extends EditText { private static final String TAG = "NoteEditText"; private int mIndex; @@ -46,6 +47,7 @@ public class NoteEditText extends EditText { private static final String SCHEME_HTTP = "http:" ; private static final String SCHEME_EMAIL = "mailto:" ; + // 建立一个字符和整数的hash表,用于链接电话,网站,还有邮箱 private static final Map sSchemaActionResMap = new HashMap(); static { sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); @@ -56,6 +58,7 @@ public class NoteEditText extends EditText { /** * Call by the {@link NoteEditActivity} to delete or add edit text */ + //在NoteEditActivity中删除或添加文本的操作,可以看做是一个文本是否被变的标记 public interface OnTextViewChangeListener { /** * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens @@ -77,19 +80,22 @@ public class NoteEditText extends EditText { private OnTextViewChangeListener mOnTextViewChangeListener; + // 根据context设置文本 public NoteEditText(Context context) { super(context, null); mIndex = 0; } - + // 设置当前光标 public void setIndex(int index) { mIndex = index; } + // 初始化文本修改标记 public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { mOnTextViewChangeListener = listener; } + // 初始化便签 public NoteEditText(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.editTextStyle); } @@ -99,6 +105,7 @@ public class NoteEditText extends EditText { // TODO Auto-generated constructor stub } + // 根据用户触摸的位置,获取对应的文本偏移量,并设置为选择状态。 @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { @@ -121,6 +128,7 @@ public class NoteEditText extends EditText { return super.onTouchEvent(event); } + // 处理用户按下一个键盘按键时会触发 的事件 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { @@ -138,11 +146,14 @@ public class NoteEditText extends EditText { return super.onKeyDown(keyCode, event); } + // 处理用户松开一个键盘按键时会触发 的事件 @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode) { case KeyEvent.KEYCODE_DEL: + // 如果被修改过 if (mOnTextViewChangeListener != null) { + // 如果被修改过且文档不为空 if (0 == mSelectionStartBeforeDelete && mIndex != 0) { mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); return true; @@ -167,9 +178,11 @@ public class NoteEditText extends EditText { return super.onKeyUp(keyCode, event); } + // 当焦点发生变化时,会自动调用该方法来处理焦点改变的事件 @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (mOnTextViewChangeListener != null) { + // 获取到焦点并且文本不为空 if (!focused && TextUtils.isEmpty(getText())) { mOnTextViewChangeListener.onTextChange(mIndex, false); } else { @@ -179,8 +192,10 @@ public class NoteEditText extends EditText { super.onFocusChanged(focused, direction, previouslyFocusedRect); } + // 生成上下文菜单 @Override protected void onCreateContextMenu(ContextMenu menu) { + // 如果有文本存在 if (getText() instanceof Spanned) { int selStart = getSelectionStart(); int selEnd = getSelectionEnd(); @@ -201,7 +216,7 @@ public class NoteEditText extends EditText { if (defaultResId == 0) { defaultResId = R.string.note_link_other; } - + // 建立菜单 menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( new OnMenuItemClickListener() { public boolean onMenuItemClick(MenuItem item) { diff --git a/src/ui/NoteItemData.java b/src/ui/NoteItemData.java index 0f5a878..fe94192 100644 --- a/src/ui/NoteItemData.java +++ b/src/ui/NoteItemData.java @@ -26,6 +26,9 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; +/*用于存储和操作一个笔记的id、标题、内容、创建时间、 +修改时间、背景颜色、字体大小、模式、文件夹id等信息。 + */ public class NoteItemData { static final String [] PROJECTION = new String [] { NoteColumns.ID, @@ -76,6 +79,7 @@ public class NoteItemData { private boolean mIsOneNoteFollowingFolder; private boolean mIsMultiNotesFollowingFolder; + // 初始化NoteItemData,主要利用光标cursor获取的东西 public NoteItemData(Context context, Cursor cursor) { mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); @@ -109,6 +113,7 @@ public class NoteItemData { checkPostion(cursor); } + // 根据鼠标的位置设置标记和位置 private void checkPostion(Cursor cursor) { mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false; @@ -214,6 +219,7 @@ public class NoteItemData { return (mAlertDate > 0); } + // 判断一个笔记是否是一个通话记录 public boolean isCallRecord() { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } diff --git a/src/ui/NotesListAdapter.java b/src/ui/NotesListAdapter.java index 51c9cb9..af6f2fd 100644 --- a/src/ui/NotesListAdapter.java +++ b/src/ui/NotesListAdapter.java @@ -31,6 +31,7 @@ import java.util.HashSet; import java.util.Iterator; +// 用于创建笔记应用的列表适配器 public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; private Context mContext; @@ -55,6 +56,7 @@ public class NotesListAdapter extends CursorAdapter { return new NotesListItem(context); } + // 用于绑定视图和数据 @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { @@ -64,20 +66,24 @@ public class NotesListAdapter extends CursorAdapter { } } + // 设置勾选框,并更新视图 public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } + // 判断单选按钮是否勾选 public boolean isInChoiceMode() { return mChoiceMode; } + // 设置单项选项框 public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } + // 根据参数checked,选择或取消选择所有的便签项 public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { @@ -89,6 +95,7 @@ public class NotesListAdapter extends CursorAdapter { } } + // 获取所有选中的项的id public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -105,6 +112,7 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + // 获取所有选中的项的小部件属性 public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -128,6 +136,7 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + // 获取选中的项的数量,并返回一个整数。 public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -143,11 +152,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 +166,21 @@ public class NotesListAdapter extends CursorAdapter { return mSelectedIndex.get(position); } + // 内容发生变化时,重新计算便签数量 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } + // 光标发生变动时,回调该函数计算便签数量 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } + // 计算便签数量 private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { diff --git a/src/ui/NotesListItem.java b/src/ui/NotesListItem.java index 1221e80..dad78e7 100644 --- a/src/ui/NotesListItem.java +++ b/src/ui/NotesListItem.java @@ -30,6 +30,7 @@ import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; +//创建便签列表项目选项 public class NotesListItem extends LinearLayout { private ImageView mAlert; private TextView mTitle; @@ -38,6 +39,7 @@ public class NotesListItem extends LinearLayout { private NoteItemData mItemData; private CheckBox mCheckBox; + public NotesListItem(Context context) { super(context); inflate(context, R.layout.note_item, this); @@ -48,15 +50,20 @@ 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); } else { mCheckBox.setVisibility(View.GONE); } mItemData = data; + // 根据数据的id和父id,设置不同的标题,时间,图标和呼叫名 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.GONE); mAlert.setVisibility(View.VISIBLE); @@ -99,6 +106,7 @@ public class NotesListItem extends LinearLayout { setBackground(data); } + // 根据传入的数据设置背景颜色 private void setBackground(NoteItemData data) { int id = data.getBgColorId(); if (data.getType() == Notes.TYPE_NOTE) { @@ -116,6 +124,7 @@ public class NotesListItem extends LinearLayout { } } + // 获取视图板顶的数据对象 public NoteItemData getItemData() { return mItemData; } diff --git a/src/ui/NotesPreferenceActivity.java b/src/ui/NotesPreferenceActivity.java index 07c5f7e..5134c10 100644 --- a/src/ui/NotesPreferenceActivity.java +++ b/src/ui/NotesPreferenceActivity.java @@ -69,6 +69,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { private boolean mHasAddedAccount; + // 在活动创建时执行一些初始化操作 @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -76,6 +77,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { /* using the app icon for navigation */ getActionBar().setDisplayHomeAsUpEnabled(true); + // 从xml文件中加载活动的偏好设置 addPreferencesFromResource(R.xml.preferences); mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); mReceiver = new GTaskReceiver(); @@ -84,7 +86,9 @@ public class NotesPreferenceActivity extends PreferenceActivity { registerReceiver(mReceiver, filter); mOriAccounts = null; + // 从xml文件中加载了一个视图,并赋值给header这个局部变量 View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); + // 将这个视图添加到列表的头部 getListView().addHeaderView(header, null, true); } @@ -112,10 +116,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } } - + // 刷新用户界面 refreshUI(); } + // 在活动销毁时执行一些清理操作 @Override protected void onDestroy() { if (mReceiver != null) { @@ -124,6 +129,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { super.onDestroy(); } + // 加载账户相关的偏好设置 private void loadAccountPreference() { mAccountCategory.removeAll(); @@ -154,6 +160,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { mAccountCategory.addPreference(accountPref); } + // 加载同步按钮和同步状态的显示 private void loadSyncButton() { Button syncButton = (Button) findViewById(R.id.preference_sync_button); TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); @@ -193,29 +200,35 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + // 刷新界面 private void refreshUI() { loadAccountPreference(); loadSyncButton(); } + // 显示账户选择的对话框并进行账户的设置 private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + // 设置标题以及子标题内容 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); 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); + // 获取同步账户信息 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; @@ -227,8 +240,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } items[index++] = account.name; } + // 在对话框建立一个单选的复选框 dialogBuilder.setSingleChoiceItems(items, checkedItem, new DialogInterface.OnClickListener() { + //设置点击后执行的事件,包括检录新同步账户和刷新标签界面 public void onClick(DialogInterface dialog, int which) { setSyncAccount(itemMapping[which].toString()); dialog.dismiss(); @@ -254,15 +269,18 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } + // 显示一个更改账户的确认对话框 private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + // 根据同步修改的账户信息设置标题以及子标题的内容 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, 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[] { @@ -283,11 +301,13 @@ public class NotesPreferenceActivity extends PreferenceActivity { 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); @@ -318,6 +338,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + // 删除同步账户 private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); @@ -340,12 +361,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { }).start(); } + // 获取同步账户名称 public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + // 设置最终同步时间 public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -354,12 +377,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.commit(); } + // 获取最终同步时间 public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } + // 接收同步状态和信息 private class GTaskReceiver extends BroadcastReceiver { @Override @@ -374,6 +399,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + // 处理菜单选项 public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: