diff --git a/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java b/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java index 85723be..0046676 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java @@ -40,12 +40,21 @@ import net.micode.notes.tool.DataUtils; 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; - MediaPlayer mPlayer; - + private long mNoteId; // 笔记ID + private String mSnippet; // 笔记摘要内容 + private static final int SNIPPET_PREW_MAX_LEN = 60; // 摘要预览最大长度 + MediaPlayer mPlayer; // 媒体播放器 + + /** + * 活动创建时调用 + * 初始化窗口参数、获取笔记内容并显示提醒 + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -83,11 +92,19 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } + /** + * 检查屏幕是否点亮 + * @return 屏幕是否处于点亮状态 + */ private boolean isScreenOn() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); return pm.isScreenOn(); } + /** + * 播放闹钟提醒音效 + * 使用系统默认闹钟铃声并循环播放 + */ private void playAlarmSound() { Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); @@ -105,20 +122,24 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD mPlayer.setLooping(true); mPlayer.start(); } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block + // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (SecurityException e) { - // TODO Auto-generated catch block + // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IllegalStateException e) { - // TODO Auto-generated catch block + // TODO 自动生成的 catch 块 e.printStackTrace(); } catch (IOException e) { - // TODO Auto-generated catch block + // TODO 自动生成的 catch 块 e.printStackTrace(); } } + /** + * 显示提醒操作对话框 + * 显示笔记内容并提供操作按钮 + */ private void showActionDialog() { AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle(R.string.app_name); @@ -130,6 +151,10 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD dialog.show().setOnDismissListener(this); } + /** + * 对话框按钮点击事件处理 + * 处理确认和进入笔记操作 + */ public void onClick(DialogInterface dialog, int which) { switch (which) { case DialogInterface.BUTTON_NEGATIVE: @@ -143,11 +168,19 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } + /** + * 对话框关闭事件处理 + * 停止提醒音效并结束活动 + */ public void onDismiss(DialogInterface dialog) { stopAlarmSound(); finish(); } + /** + * 停止闹钟提醒音效 + * 停止并释放媒体播放器资源 + */ private void stopAlarmSound() { if (mPlayer != null) { mPlayer.stop(); diff --git a/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java b/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java index f221202..5cebadb 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java @@ -28,19 +28,31 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; +/** + * 闹钟初始化接收器类 + * 负责在系统启动或时间变更时重新设置所有笔记的提醒闹钟 + */ public class AlarmInitReceiver extends BroadcastReceiver { + /** + * 查询笔记数据库所需的列 + */ private static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE + NoteColumns.ID, // 笔记ID + NoteColumns.ALERTED_DATE // 提醒日期 }; - private static final int COLUMN_ID = 0; - private static final int COLUMN_ALERTED_DATE = 1; + private static final int COLUMN_ID = 0; // ID列索引 + private static final int COLUMN_ALERTED_DATE = 1; // 提醒日期列索引 + /** + * 接收广播事件 + * 查询所有未来的提醒并重新设置闹钟 + */ @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, @@ -51,9 +63,11 @@ public class AlarmInitReceiver extends BroadcastReceiver { if (c.moveToFirst()) { do { long alertDate = c.getLong(COLUMN_ALERTED_DATE); + // 创建用于闹钟触发的PendingIntent Intent sender = new Intent(context, AlarmReceiver.class); sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); + // 获取闹钟管理器并设置闹钟 AlarmManager alermManager = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); diff --git a/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java b/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java index 54e503b..a696b27 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java @@ -20,11 +20,22 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +/** + * 闹钟接收器类 + * 负责接收闹钟触发的广播并启动提醒活动 + */ public class AlarmReceiver extends BroadcastReceiver { + /** + * 接收闹钟广播事件 + * 启动闹钟提醒活动显示提醒内容 + */ @Override public void onReceive(Context context, Intent intent) { + // 设置意图的目标类为闹钟提醒活动 intent.setClass(context, AlarmAlertActivity.class); + // 添加新任务标志,确保活动可以从非活动状态启动 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动闹钟提醒活动 context.startActivity(intent); } } diff --git a/app/src/main/java/net/micode/notes/ui/DateTimePicker.java b/app/src/main/java/net/micode/notes/ui/DateTimePicker.java index 496b0cd..7d526c1 100644 --- a/app/src/main/java/net/micode/notes/ui/DateTimePicker.java +++ b/app/src/main/java/net/micode/notes/ui/DateTimePicker.java @@ -28,42 +28,50 @@ import android.view.View; import android.widget.FrameLayout; import android.widget.NumberPicker; +/** + * 日期时间选择器控件 + * 提供日期和时间的选择功能,支持12小时和24小时格式 + */ public class DateTimePicker extends FrameLayout { - private static final boolean DEFAULT_ENABLE_STATE = true; + private static final boolean DEFAULT_ENABLE_STATE = true; // 默认启用状态 - private static final int HOURS_IN_HALF_DAY = 12; - private static final int HOURS_IN_ALL_DAY = 24; - private static final int DAYS_IN_ALL_WEEK = 7; - private static final int DATE_SPINNER_MIN_VAL = 0; - private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; - private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; - private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; - private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; - private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; - private static final int MINUT_SPINNER_MIN_VAL = 0; - private static final int MINUT_SPINNER_MAX_VAL = 59; - private static final int AMPM_SPINNER_MIN_VAL = 0; - private static final int AMPM_SPINNER_MAX_VAL = 1; + private static final int HOURS_IN_HALF_DAY = 12; // 半天的小时数 + private static final int HOURS_IN_ALL_DAY = 24; // 全天的小时数 + private static final int DAYS_IN_ALL_WEEK = 7; // 一周的天数 + private static final int DATE_SPINNER_MIN_VAL = 0; // 日期选择器最小值 + private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; // 日期选择器最大值 + private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; // 24小时制小时选择器最小值 + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; // 24小时制小时选择器最大值 + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; // 12小时制小时选择器最小值 + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; // 12小时制小时选择器最大值 + private static final int MINUT_SPINNER_MIN_VAL = 0; // 分钟选择器最小值 + private static final int MINUT_SPINNER_MAX_VAL = 59; // 分钟选择器最大值 + 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 final NumberPicker mDateSpinner; // 日期选择器 + private final NumberPicker mHourSpinner; // 小时选择器 + private final NumberPicker mMinuteSpinner; // 分钟选择器 + private final NumberPicker mAmPmSpinner; // 上午/下午选择器 + private Calendar mDate; // 日期时间对象 - private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; // 日期显示值数组 - private boolean mIsAm; + private boolean mIsAm; // 是否为上午 - private boolean mIs24HourView; + private boolean mIs24HourView; // 是否为24小时制 - private boolean mIsEnabled = DEFAULT_ENABLE_STATE; + private boolean mIsEnabled = DEFAULT_ENABLE_STATE; // 是否启用 - private boolean mInitialising; + private boolean mInitialising; // 是否正在初始化 - private OnDateTimeChangedListener mOnDateTimeChangedListener; + private OnDateTimeChangedListener mOnDateTimeChangedListener; // 日期时间变更监听器 + /** + * 日期变更监听器 + * 处理日期选择器的值变更事件 + */ private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { @@ -73,6 +81,10 @@ public class DateTimePicker extends FrameLayout { } }; + /** + * 小时变更监听器 + * 处理小时选择器的值变更事件 + */ private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { @@ -115,6 +127,10 @@ public class DateTimePicker extends FrameLayout { } }; + /** + * 分钟变更监听器 + * 处理分钟选择器的值变更事件 + */ private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { @@ -144,6 +160,10 @@ public class DateTimePicker extends FrameLayout { } }; + /** + * 上午/下午变更监听器 + * 处理上午/下午选择器的值变更事件 + */ private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { @@ -158,19 +178,50 @@ public class DateTimePicker extends FrameLayout { } }; + /** + * 日期时间变更监听器接口 + * 用于通知日期时间的变更事件 + */ public interface OnDateTimeChangedListener { + /** + * 当日期时间变更时调用 + * @param view 日期时间选择器视图 + * @param year 年份 + * @param month 月份 + * @param dayOfMonth 日期 + * @param hourOfDay 小时(24小时制) + * @param minute 分钟 + */ void onDateTimeChanged(DateTimePicker view, int year, int month, int dayOfMonth, int hourOfDay, int minute); } + /** + * 构造函数 + * 使用当前时间初始化日期时间选择器 + * @param context 上下文 + */ public DateTimePicker(Context context) { this(context, System.currentTimeMillis()); } + /** + * 构造函数 + * 使用指定时间初始化日期时间选择器 + * @param context 上下文 + * @param date 初始日期时间(毫秒) + */ public DateTimePicker(Context context, long date) { this(context, date, DateFormat.is24HourFormat(context)); } + /** + * 构造函数 + * 使用指定时间和时间格式初始化日期时间选择器 + * @param context 上下文 + * @param date 初始日期时间(毫秒) + * @param is24HourView 是否使用24小时制 + */ public DateTimePicker(Context context, long date, boolean is24HourView) { super(context); mDate = Calendar.getInstance(); @@ -178,19 +229,24 @@ public class DateTimePicker extends FrameLayout { mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; inflate(context, R.layout.datetime_picker, this); + // 初始化日期选择器 mDateSpinner = (NumberPicker) findViewById(R.id.date); mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); + // 初始化小时选择器 mHourSpinner = (NumberPicker) findViewById(R.id.hour); mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); + + // 初始化分钟选择器 mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); mMinuteSpinner.setOnLongPressUpdateInterval(100); mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); + // 初始化上午/下午选择器 String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); @@ -198,22 +254,28 @@ public class DateTimePicker extends FrameLayout { mAmPmSpinner.setDisplayedValues(stringsForAmPm); mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); - // update controls to initial state + // 更新控件到初始状态 updateDateControl(); updateHourControl(); updateAmPmControl(); + // 设置时间显示格式 set24HourView(is24HourView); - // set to current time + // 设置为当前时间 setCurrentDate(date); + // 设置启用状态 setEnabled(isEnabled()); - // set the content descriptions + // 设置内容描述 mInitialising = false; } + /** + * 设置控件启用状态 + * @param enabled 是否启用 + */ @Override public void setEnabled(boolean enabled) { if (mIsEnabled == enabled) { @@ -227,24 +289,26 @@ public class DateTimePicker extends FrameLayout { mIsEnabled = enabled; } + /** + * 获取控件启用状态 + * @return 是否启用 + */ @Override public boolean isEnabled() { return mIsEnabled; } /** - * Get the current date in millis - * - * @return the current date in millis + * 获取当前日期时间的毫秒值 + * @return 当前日期时间的毫秒值 */ public long getCurrentDateInTimeMillis() { return mDate.getTimeInMillis(); } /** - * Set the current date - * - * @param date The current date in millis + * 设置当前日期时间 + * @param date 日期时间的毫秒值 */ public void setCurrentDate(long date) { Calendar cal = Calendar.getInstance(); @@ -254,13 +318,12 @@ public class DateTimePicker extends FrameLayout { } /** - * Set the current date - * - * @param year The current year - * @param month The current month - * @param dayOfMonth The current dayOfMonth - * @param hourOfDay The current hourOfDay - * @param minute The current minute + * 设置当前日期时间 + * @param year 当前年份 + * @param month 当前月份 + * @param dayOfMonth 当前日期 + * @param hourOfDay 当前小时 + * @param minute 当前分钟 */ public void setCurrentDate(int year, int month, int dayOfMonth, int hourOfDay, int minute) { @@ -272,18 +335,16 @@ public class DateTimePicker extends FrameLayout { } /** - * Get current year - * - * @return The current year + * 获取当前年份 + * @return 当前年份 */ public int getCurrentYear() { return mDate.get(Calendar.YEAR); } /** - * Set current year - * - * @param year The current year + * 设置当前年份 + * @param year 当前年份 */ public void setCurrentYear(int year) { if (!mInitialising && year == getCurrentYear()) { @@ -295,18 +356,16 @@ public class DateTimePicker extends FrameLayout { } /** - * Get current month in the year - * - * @return The current month in the year + * 获取当前月份 + * @return 当前月份 */ public int getCurrentMonth() { return mDate.get(Calendar.MONTH); } /** - * Set current month in the year - * - * @param month The month in the year + * 设置当前月份 + * @param month 月份 */ public void setCurrentMonth(int month) { if (!mInitialising && month == getCurrentMonth()) { @@ -318,18 +377,16 @@ public class DateTimePicker extends FrameLayout { } /** - * Get current day of the month - * - * @return The day of the month + * 获取当前日期 + * @return 当前日期 */ public int getCurrentDay() { return mDate.get(Calendar.DAY_OF_MONTH); } /** - * Set current day of the month - * - * @param dayOfMonth The day of the month + * 设置当前日期 + * @param dayOfMonth 日期 */ public void setCurrentDay(int dayOfMonth) { if (!mInitialising && dayOfMonth == getCurrentDay()) { @@ -341,8 +398,8 @@ public class DateTimePicker extends FrameLayout { } /** - * Get current hour in 24 hour mode, in the range (0~23) - * @return The current hour in 24 hour mode + * 获取当前小时(24小时制) + * @return 当前小时(24小时制) */ public int getCurrentHourOfDay() { return mDate.get(Calendar.HOUR_OF_DAY); @@ -362,9 +419,8 @@ public class DateTimePicker extends FrameLayout { } /** - * Set current hour in 24 hour mode, in the range (0~23) - * - * @param hourOfDay + * 设置当前小时(24小时制) + * @param hourOfDay 小时(24小时制) */ public void setCurrentHour(int hourOfDay) { if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { @@ -390,16 +446,16 @@ public class DateTimePicker extends FrameLayout { } /** - * Get currentMinute - * - * @return The Current Minute + * 获取当前分钟 + * @return 当前分钟 */ public int getCurrentMinute() { return mDate.get(Calendar.MINUTE); } /** - * Set current minute + * 设置当前分钟 + * @param minute 分钟 */ public void setCurrentMinute(int minute) { if (!mInitialising && minute == getCurrentMinute()) { diff --git a/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java b/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java index 2c47ba4..d498aa0 100644 --- a/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java +++ b/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java @@ -29,22 +29,45 @@ import android.content.DialogInterface.OnClickListener; import android.text.format.DateFormat; import android.text.format.DateUtils; +/** + * 日期时间选择对话框 + * 封装日期时间选择器控件,提供对话框形式的日期时间选择功能 + */ public class DateTimePickerDialog extends AlertDialog implements OnClickListener { - private Calendar mDate = Calendar.getInstance(); - private boolean mIs24HourView; - private OnDateTimeSetListener mOnDateTimeSetListener; - private DateTimePicker mDateTimePicker; + private Calendar mDate = Calendar.getInstance(); // 日期时间对象 + private boolean mIs24HourView; // 是否为24小时制 + private OnDateTimeSetListener mOnDateTimeSetListener; // 日期时间设置监听器 + private DateTimePicker mDateTimePicker; // 日期时间选择器控件 + /** + * 日期时间设置监听器接口 + * 用于通知日期时间设置完成事件 + */ public interface OnDateTimeSetListener { + /** + * 当日期时间设置完成时调用 + * @param dialog 对话框 + * @param date 设置的日期时间(毫秒) + */ void OnDateTimeSet(AlertDialog dialog, long date); } + /** + * 构造函数 + * 创建日期时间选择对话框 + * @param context 上下文 + * @param date 初始日期时间(毫秒) + */ public DateTimePickerDialog(Context context, long date) { super(context); mDateTimePicker = new DateTimePicker(context); 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); @@ -64,14 +87,27 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener updateTitle(mDate.getTimeInMillis()); } + /** + * 设置是否使用24小时制 + * @param is24HourView 是否使用24小时制 + */ public void set24HourView(boolean is24HourView) { mIs24HourView = is24HourView; } + /** + * 设置日期时间设置监听器 + * @param callBack 监听器回调 + */ public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { mOnDateTimeSetListener = callBack; } + /** + * 更新对话框标题 + * 根据当前日期时间更新对话框标题显示 + * @param date 日期时间(毫秒) + */ private void updateTitle(long date) { int flag = DateUtils.FORMAT_SHOW_YEAR | @@ -81,10 +117,13 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); } + /** + * 确认按钮点击事件处理 + * 通知监听器日期时间已设置 + */ public void onClick(DialogInterface arg0, int arg1) { if (mOnDateTimeSetListener != null) { mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); } } - } \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/ui/DropdownMenu.java b/app/src/main/java/net/micode/notes/ui/DropdownMenu.java index 613dc74..f6f6284 100644 --- a/app/src/main/java/net/micode/notes/ui/DropdownMenu.java +++ b/app/src/main/java/net/micode/notes/ui/DropdownMenu.java @@ -27,11 +27,22 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import net.micode.notes.R; +/** + * 下拉菜单类 + * 封装PopupMenu,提供简单的下拉菜单功能 + */ public class DropdownMenu { - private Button mButton; - private PopupMenu mPopupMenu; - private Menu mMenu; + private Button mButton; // 触发下拉菜单的按钮 + private PopupMenu mPopupMenu; // 弹出菜单 + private Menu mMenu; // 菜单对象 + /** + * 构造函数 + * 创建下拉菜单并关联到按钮 + * @param context 上下文 + * @param button 触发菜单的按钮 + * @param menuId 菜单资源ID + */ public DropdownMenu(Context context, Button button, int menuId) { mButton = button; mButton.setBackgroundResource(R.drawable.dropdown_icon); @@ -39,22 +50,39 @@ public class DropdownMenu { mMenu = mPopupMenu.getMenu(); mPopupMenu.getMenuInflater().inflate(menuId, mMenu); mButton.setOnClickListener(new OnClickListener() { + /** + * 按钮点击事件处理 + * 显示弹出菜单 + */ public void onClick(View v) { mPopupMenu.show(); } }); } + /** + * 设置下拉菜单项点击监听器 + * @param listener 菜单项点击监听器 + */ public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } + /** + * 查找菜单项 + * @param id 菜单项ID + * @return 菜单项对象 + */ public MenuItem findItem(int id) { return mMenu.findItem(id); } + /** + * 设置菜单标题 + * @param title 标题文本 + */ public void setTitle(CharSequence title) { mButton.setText(title); } diff --git a/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java b/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java index 96b77da..2e221a4 100644 --- a/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java +++ b/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java @@ -29,25 +29,45 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; +/** + * 文件夹列表适配器类 + * 用于显示文件夹列表,继承自CursorAdapter + */ public class FoldersListAdapter extends CursorAdapter { + /** + * 查询文件夹所需的列 + */ public static final String [] PROJECTION = { - NoteColumns.ID, - NoteColumns.SNIPPET + NoteColumns.ID, // 文件夹ID + NoteColumns.SNIPPET // 文件夹名称 }; - public static final int ID_COLUMN = 0; - public static final int NAME_COLUMN = 1; + public static final int ID_COLUMN = 0; // ID列索引 + public static final int NAME_COLUMN = 1; // 名称列索引 + /** + * 构造函数 + * @param context 上下文 + * @param c 数据游标 + */ public FoldersListAdapter(Context context, Cursor c) { super(context, c); - // TODO Auto-generated constructor stub + // TODO 自动生成的构造函数存根 } + /** + * 创建新视图 + * 当需要为游标中的行创建新视图时调用 + */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new FolderListItem(context); } + /** + * 绑定视图数据 + * 将游标中的数据绑定到已存在的视图 + */ @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof FolderListItem) { @@ -57,24 +77,43 @@ public class FoldersListAdapter extends CursorAdapter { } } + /** + * 获取文件夹名称 + * 根据位置获取对应的文件夹名称 + * @param context 上下文 + * @param position 位置 + * @return 文件夹名称 + */ public String getFolderName(Context context, int position) { Cursor cursor = (Cursor) getItem(position); 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; + private TextView mName; // 文件夹名称文本视图 + /** + * 构造函数 + * @param context 上下文 + */ public FolderListItem(Context context) { super(context); inflate(context, R.layout.folder_list_item, this); mName = (TextView) findViewById(R.id.tv_folder_name); } + /** + * 绑定数据 + * 设置文件夹名称 + * @param name 文件夹名称 + */ public void bind(String name) { mName.setText(name); } } - } diff --git a/app/src/main/java/net/micode/notes/ui/NoteEditText.java b/app/src/main/java/net/micode/notes/ui/NoteEditText.java index 2afe2a8..0cfbb5e 100644 --- a/app/src/main/java/net/micode/notes/ui/NoteEditText.java +++ b/app/src/main/java/net/micode/notes/ui/NoteEditText.java @@ -37,15 +37,22 @@ 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; - private int mSelectionStartBeforeDelete; + private int mIndex; // 文本框索引 + private int mSelectionStartBeforeDelete; // 删除前的选择起始位置 - private static final String SCHEME_TEL = "tel:" ; - private static final String SCHEME_HTTP = "http:" ; - private static final String SCHEME_EMAIL = "mailto:" ; + private static final String SCHEME_TEL = "tel:" ; // 电话链接前缀 + private static final String SCHEME_HTTP = "http:" ; // 网页链接前缀 + private static final String SCHEME_EMAIL = "mailto:" ;// 邮件链接前缀 + /** + * 链接类型与操作文本资源ID的映射 + */ private static final Map sSchemaActionResMap = new HashMap(); static { sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); @@ -54,56 +61,88 @@ public class NoteEditText extends EditText { } /** - * Call by the {@link NoteEditActivity} to delete or add edit text + * 文本视图变更监听器接口 + * 由NoteEditActivity调用,用于删除或添加编辑文本框 */ public interface OnTextViewChangeListener { /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null + * 当按下删除键且文本为空时删除当前编辑框 + * @param index 当前编辑框索引 + * @param text 当前文本内容 */ void onEditTextDelete(int index, String text); /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen + * 当按下回车键时在当前编辑框后添加新的编辑框 + * @param index 新编辑框的索引 + * @param text 新编辑框的文本内容 */ void onEditTextEnter(int index, String text); /** - * Hide or show item option when text change + * 当文本变化时隐藏或显示选项 + * @param index 当前编辑框索引 + * @param hasText 是否有文本 */ void onTextChange(int index, boolean hasText); } - private OnTextViewChangeListener mOnTextViewChangeListener; + private OnTextViewChangeListener mOnTextViewChangeListener; // 文本视图变更监听器 + /** + * 构造函数 + * @param context 上下文 + */ public NoteEditText(Context context) { super(context, null); mIndex = 0; } + /** + * 设置编辑框索引 + * @param index 索引值 + */ public void setIndex(int index) { mIndex = index; } + /** + * 设置文本视图变更监听器 + * @param listener 监听器对象 + */ public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { mOnTextViewChangeListener = listener; } + /** + * 构造函数 + * @param context 上下文 + * @param attrs 属性集 + */ public NoteEditText(Context context, AttributeSet attrs) { super(context, attrs, android.R.attr.editTextStyle); } + /** + * 构造函数 + * @param context 上下文 + * @param attrs 属性集 + * @param defStyle 默认样式 + */ public NoteEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - // TODO Auto-generated constructor stub + // TODO 自动生成的构造函数存根 } + /** + * 触摸事件处理 + * 处理文本选择 + */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - + // 计算触摸点对应的文本位置 int x = (int) event.getX(); int y = (int) event.getY(); x -= getTotalPaddingLeft(); @@ -121,6 +160,10 @@ public class NoteEditText extends EditText { return super.onTouchEvent(event); } + /** + * 按键按下事件处理 + * 处理回车键和删除键 + */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { @@ -138,6 +181,10 @@ public class NoteEditText extends EditText { return super.onKeyDown(keyCode, event); } + /** + * 按键释放事件处理 + * 处理回车键和删除键的特殊逻辑 + */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch(keyCode) { @@ -167,6 +214,10 @@ public class NoteEditText extends EditText { return super.onKeyUp(keyCode, event); } + /** + * 焦点变化事件处理 + * 当焦点变化时通知监听器文本状态 + */ @Override protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (mOnTextViewChangeListener != null) { @@ -179,6 +230,10 @@ public class NoteEditText extends EditText { super.onFocusChanged(focused, direction, previouslyFocusedRect); } + /** + * 创建上下文菜单 + * 为文本中的链接创建特定的菜单项 + */ @Override protected void onCreateContextMenu(ContextMenu menu) { if (getText() instanceof Spanned) { @@ -204,8 +259,12 @@ public class NoteEditText extends EditText { menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( new OnMenuItemClickListener() { + /** + * 菜单项点击事件处理 + * 处理链接点击 + */ public boolean onMenuItemClick(MenuItem item) { - // goto a new intent + // 跳转到新的意图 urls[0].onClick(NoteEditText.this); return true; } diff --git a/app/src/main/java/net/micode/notes/ui/NoteItemData.java b/app/src/main/java/net/micode/notes/ui/NoteItemData.java index 0f5a878..c432bb6 100644 --- a/app/src/main/java/net/micode/notes/ui/NoteItemData.java +++ b/app/src/main/java/net/micode/notes/ui/NoteItemData.java @@ -26,56 +26,69 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; +/** + * 笔记项数据类 + * 封装单个笔记或文件夹的数据,用于在列表中显示 + */ public class NoteItemData { + /** + * 查询笔记所需的列 + */ static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, - NoteColumns.HAS_ATTACHMENT, - NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, - NoteColumns.PARENT_ID, - NoteColumns.SNIPPET, - NoteColumns.TYPE, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, + NoteColumns.ID, // 笔记ID + NoteColumns.ALERTED_DATE, // 提醒日期 + NoteColumns.BG_COLOR_ID, // 背景颜色ID + NoteColumns.CREATED_DATE, // 创建日期 + NoteColumns.HAS_ATTACHMENT, // 是否有附件 + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.NOTES_COUNT, // 笔记数量 + NoteColumns.PARENT_ID, // 父文件夹ID + NoteColumns.SNIPPET, // 笔记摘要 + NoteColumns.TYPE, // 类型 + NoteColumns.WIDGET_ID, // 小部件ID + NoteColumns.WIDGET_TYPE, // 小部件类型 }; - private static final int ID_COLUMN = 0; - private static final int ALERTED_DATE_COLUMN = 1; - private static final int BG_COLOR_ID_COLUMN = 2; - private static final int CREATED_DATE_COLUMN = 3; - private static final int HAS_ATTACHMENT_COLUMN = 4; - private static final int MODIFIED_DATE_COLUMN = 5; - private static final int NOTES_COUNT_COLUMN = 6; - private static final int PARENT_ID_COLUMN = 7; - private static final int SNIPPET_COLUMN = 8; - private static final int TYPE_COLUMN = 9; - 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 static final int ID_COLUMN = 0; // ID列索引 + private static final int ALERTED_DATE_COLUMN = 1; // 提醒日期列索引 + private static final int BG_COLOR_ID_COLUMN = 2; // 背景颜色ID列索引 + private static final int CREATED_DATE_COLUMN = 3; // 创建日期列索引 + private static final int HAS_ATTACHMENT_COLUMN = 4; // 是否有附件列索引 + private static final int MODIFIED_DATE_COLUMN = 5; // 修改日期列索引 + private static final int NOTES_COUNT_COLUMN = 6; // 笔记数量列索引 + private static final int PARENT_ID_COLUMN = 7; // 父文件夹ID列索引 + private static final int SNIPPET_COLUMN = 8; // 笔记摘要列索引 + private static final int TYPE_COLUMN = 9; // 类型列索引 + private static final int WIDGET_ID_COLUMN = 10; // 小部件ID列索引 + private static final int WIDGET_TYPE_COLUMN = 11; // 小部件类型列索引 + + 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; // 是否为文件夹后的多个笔记 + + /** + * 构造函数 + * 从数据库游标创建笔记项数据 + * @param context 上下文 + * @param cursor 数据库游标 + */ public NoteItemData(Context context, Cursor cursor) { mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); @@ -109,6 +122,11 @@ public class NoteItemData { checkPostion(cursor); } + /** + * 检查项目在列表中的位置 + * 确定项目是否为第一项、最后一项、唯一一项或跟随文件夹的项目 + * @param cursor 数据库游标 + */ private void checkPostion(Cursor cursor) { mIsLastItem = cursor.isLast() ? true : false; mIsFirstItem = cursor.isFirst() ? true : false; @@ -134,90 +152,180 @@ public class NoteItemData { } } + /** + * 判断是否为文件夹后的单个笔记 + * @return 如果是文件夹后的单个笔记则返回true + */ public boolean isOneFollowingFolder() { return mIsOneNoteFollowingFolder; } + /** + * 判断是否为文件夹后的多个笔记 + * @return 如果是文件夹后的多个笔记则返回true + */ public boolean isMultiFollowingFolder() { return mIsMultiNotesFollowingFolder; } + /** + * 判断是否为最后一项 + * @return 如果是最后一项则返回true + */ public boolean isLast() { return mIsLastItem; } + /** + * 获取通话记录联系人名称 + * @return 联系人名称 + */ public String getCallName() { return mName; } + /** + * 判断是否为第一项 + * @return 如果是第一项则返回true + */ public boolean isFirst() { return mIsFirstItem; } + /** + * 判断是否为唯一一项 + * @return 如果是唯一一项则返回true + */ public boolean isSingle() { return mIsOnlyOneItem; } + /** + * 获取笔记ID + * @return 笔记ID + */ public long getId() { return mId; } + /** + * 获取提醒日期 + * @return 提醒日期(毫秒) + */ public long getAlertDate() { return mAlertDate; } + /** + * 获取创建日期 + * @return 创建日期(毫秒) + */ public long getCreatedDate() { return mCreatedDate; } + /** + * 判断是否有附件 + * @return 如果有附件则返回true + */ public boolean hasAttachment() { return mHasAttachment; } + /** + * 获取修改日期 + * @return 修改日期(毫秒) + */ public long getModifiedDate() { return mModifiedDate; } + /** + * 获取背景颜色ID + * @return 背景颜色ID + */ public int getBgColorId() { return mBgColorId; } + /** + * 获取父文件夹ID + * @return 父文件夹ID + */ public long getParentId() { return mParentId; } + /** + * 获取笔记数量 + * @return 笔记数量 + */ public int getNotesCount() { return mNotesCount; } + /** + * 获取文件夹ID + * @return 文件夹ID + */ public long getFolderId () { return mParentId; } + /** + * 获取类型 + * @return 类型(笔记、文件夹或系统) + */ public int getType() { return mType; } + /** + * 获取小部件类型 + * @return 小部件类型 + */ public int getWidgetType() { return mWidgetType; } + /** + * 获取小部件ID + * @return 小部件ID + */ public int getWidgetId() { return mWidgetId; } + /** + * 获取笔记摘要 + * @return 笔记摘要 + */ public String getSnippet() { return mSnippet; } + /** + * 判断是否有提醒 + * @return 如果有提醒则返回true + */ public boolean hasAlert() { return (mAlertDate > 0); } + /** + * 判断是否为通话记录 + * @return 如果是通话记录则返回true + */ public boolean isCallRecord() { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } + /** + * 获取笔记类型 + * 静态方法,直接从游标获取类型 + * @param cursor 数据库游标 + * @return 笔记类型 + */ public static int getNoteType(Cursor cursor) { return cursor.getInt(TYPE_COLUMN); } diff --git a/app/src/main/java/net/micode/notes/ui/NotesListActivity.java b/app/src/main/java/net/micode/notes/ui/NotesListActivity.java index 9966422..1938053 100644 --- a/app/src/main/java/net/micode/notes/ui/NotesListActivity.java +++ b/app/src/main/java/net/micode/notes/ui/NotesListActivity.java @@ -78,63 +78,76 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashSet; +/** + * 笔记列表活动类 + * 负责显示笔记列表、文件夹列表,并处理笔记的创建、删除、移动等操作 + * 实现了点击监听器和长按监听器接口 + */ public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; + private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; // 文件夹笔记列表查询标记 - private static final int FOLDER_LIST_QUERY_TOKEN = 1; + private static final int FOLDER_LIST_QUERY_TOKEN = 1; // 文件夹列表查询标记 - private static final int MENU_FOLDER_DELETE = 0; + private static final int MENU_FOLDER_DELETE = 0; // 文件夹删除菜单项 - private static final int MENU_FOLDER_VIEW = 1; + private static final int MENU_FOLDER_VIEW = 1; // 文件夹查看菜单项 - private static final int MENU_FOLDER_CHANGE_NAME = 2; + 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"; // 添加介绍笔记的偏好设置键 + /** + * 列表编辑状态枚举 + * 定义了三种不同的列表状态:笔记列表、子文件夹、通话记录文件夹 + */ private enum ListEditState { NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER }; - private ListEditState mState; + private ListEditState mState; // 当前列表编辑状态 - private BackgroundQueryHandler mBackgroundQueryHandler; + private BackgroundQueryHandler mBackgroundQueryHandler; // 后台查询处理器 - private NotesListAdapter mNotesListAdapter; + private NotesListAdapter mNotesListAdapter; // 笔记列表适配器 - private ListView mNotesListView; + private ListView mNotesListView; // 笔记列表视图 - private Button mAddNewNote; + private Button mAddNewNote; // 添加新笔记按钮 - private boolean mDispatch; + private boolean mDispatch; // 是否分发事件标志 - private int mOriginY; + private int mOriginY; // 原始Y坐标 - private int mDispatchY; + private int mDispatchY; // 分发Y坐标 - private TextView mTitleBar; + private TextView mTitleBar; // 标题栏 - private long mCurrentFolderId; + private long mCurrentFolderId; // 当前文件夹ID - private ContentResolver mContentResolver; + private ContentResolver mContentResolver; // 内容解析器 - private ModeCallback mModeCallBack; + private ModeCallback mModeCallBack; // 模式回调 private static final String TAG = "NotesListActivity"; - public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; + public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; // 笔记列表视图滚动速率 - private NoteItemData mFocusNoteDataItem; + private NoteItemData mFocusNoteDataItem; // 焦点笔记数据项 - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; + private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; // 普通选择条件 private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " - + NoteColumns.NOTES_COUNT + ">0)"; + + NoteColumns.NOTES_COUNT + ">0)"; // 根文件夹选择条件 - private final static int REQUEST_CODE_OPEN_NODE = 102; - private final static int REQUEST_CODE_NEW_NODE = 103; + private final static int REQUEST_CODE_OPEN_NODE = 102; // 打开笔记请求码 + private final static int REQUEST_CODE_NEW_NODE = 103; // 新建笔记请求码 + /** + * 活动创建时调用 + * 初始化界面和资源 + */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -142,11 +155,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt initResources(); /** - * Insert an introduction when user firstly use this application + * 当用户首次使用此应用程序时插入介绍笔记 */ setAppInfoFromRawRes(); } + /** + * 处理活动结果 + * 当打开或新建笔记活动返回时更新列表 + */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK @@ -157,6 +174,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 从原始资源设置应用信息 + * 读取介绍文件并创建介绍笔记 + */ private void setAppInfoFromRawRes() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { @@ -184,7 +205,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt try { in.close(); } catch (IOException e) { - // TODO Auto-generated catch block + // TODO 自动生成的 catch 块 e.printStackTrace(); } } @@ -203,12 +224,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 活动开始时调用 + * 开始异步查询笔记列表 + */ @Override protected void onStart() { super.onStart(); startAsyncNotesListQuery(); } + /** + * 初始化资源 + * 设置列表视图、适配器和按钮等界面元素 + */ private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); @@ -231,11 +260,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mModeCallBack = new ModeCallback(); } + /** + * 模式回调类 + * 处理多选模式下的菜单和操作 + */ private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; private ActionMode mActionMode; private MenuItem mMoveMenu; + /** + * 创建操作模式 + * 初始化菜单项 + */ public boolean onCreateActionMode(ActionMode mode, Menu menu) { getMenuInflater().inflate(R.menu.note_list_options, menu); menu.findItem(R.id.delete).setOnMenuItemClickListener(this); @@ -259,6 +296,10 @@ 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) { mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); updateMenu(); @@ -269,9 +310,13 @@ 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); MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); @@ -286,32 +331,56 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 准备操作模式 + * 在操作模式准备好时调用 + */ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - // TODO Auto-generated method stub + // TODO 自动生成的方法存根 return false; } + /** + * 操作项点击事件处理 + * 处理操作模式下的菜单项点击 + */ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // TODO Auto-generated method stub + // TODO 自动生成的方法存根 return false; } + /** + * 销毁操作模式 + * 退出多选模式时恢复界面状态 + */ public void onDestroyActionMode(ActionMode mode) { mNotesListAdapter.setChoiceMode(false); mNotesListView.setLongClickable(true); mAddNewNote.setVisibility(View.VISIBLE); } + /** + * 结束操作模式 + * 完成当前的操作模式 + */ public void finishActionMode() { mActionMode.finish(); } + /** + * 项目选中状态改变处理 + * 当列表项的选中状态发生变化时更新菜单 + */ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } + /** + * 菜单项点击事件处理 + * 处理删除和移动操作 + */ public boolean onMenuItemClick(MenuItem item) { if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), @@ -328,6 +397,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mNotesListAdapter.getSelectedCount())); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + /** + * 确认删除对话框点击事件处理 + * 执行批量删除操作 + */ public void onClick(DialogInterface dialog, int which) { batchDelete(); @@ -344,8 +417,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 新建笔记触摸监听器类 + * 处理新建笔记按钮的触摸事件 + */ private class NewNoteOnTouchListener implements OnTouchListener { + /** + * 触摸事件处理 + * 处理新建笔记按钮的触摸事件分发 + */ public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { @@ -357,20 +438,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt int start = screenHeight - newNoteViewHeight; int eventY = start + (int) event.getY(); /** - * Minus TitleBar's height + * 减去标题栏的高度 */ if (mState == ListEditState.SUB_FOLDER) { eventY -= mTitleBar.getHeight(); start -= mTitleBar.getHeight(); } /** - * HACKME:When click the transparent part of "New Note" button, dispatch - * the event to the list view behind this button. The transparent part of - * "New Note" button could be expressed by formula y=-0.12x+94(Unit:pixel) - * and the line top of the button. The coordinate based on left of the "New - * Note" button. The 94 represents maximum height of the transparent part. - * Notice that, if the background of the button changes, the formula should - * also change. This is very bad, just for the UI designer's strong requirement. + * 技巧:当点击"新建笔记"按钮的透明部分时,将事件分发到 + * 按钮后面的列表视图。"新建笔记"按钮的透明部分可以用公式 + * y=-0.12x+94(单位:像素)和按钮顶部的线表示。坐标基于 + * "新建笔记"按钮的左侧。94表示透明部分的最大高度。 + * 注意,如果按钮的背景发生变化,公式也应该改变。这很不好, + * 只是为了满足UI设计师的强烈要求。 */ if (event.getY() < (event.getX() * (-0.12) + 94)) { View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 @@ -408,6 +488,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }; + /** + * 开始异步查询笔记列表 + * 根据当前文件夹ID查询笔记列表 + */ private void startAsyncNotesListQuery() { String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; @@ -417,11 +501,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * 后台查询处理器类 + * 处理异步查询操作 + */ private final class BackgroundQueryHandler extends AsyncQueryHandler { + /** + * 构造函数 + * @param contentResolver 内容解析器 + */ public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); } + /** + * 查询完成回调 + * 处理查询结果 + */ @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { @@ -441,12 +537,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 显示文件夹列表菜单 + * 用于选择目标文件夹进行移动操作 + */ private void showFolderListMenu(Cursor cursor) { AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(R.string.menu_title_select_folder); final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + /** + * 文件夹选择对话框点击事件处理 + * 将选中的笔记移动到目标文件夹 + */ public void onClick(DialogInterface dialog, int which) { DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); @@ -462,6 +566,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); } + /** + * 创建新笔记 + * 启动笔记编辑活动创建新笔记 + */ private void createNewNote() { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); @@ -469,20 +577,27 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); } + /** + * 批量删除笔记 + * 异步执行笔记删除操作 + */ private void batchDelete() { new AsyncTask>() { + /** + * 后台执行删除操作 + * 根据同步模式决定是直接删除还是移动到垃圾箱 + */ protected HashSet doInBackground(Void... unused) { HashSet widgets = mNotesListAdapter.getSelectedWidget(); if (!isSyncMode()) { - // if not synced, delete notes directly + // 如果未同步,直接删除笔记 if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter .getSelectedItemIds())) { } else { Log.e(TAG, "Delete notes error, should not happens"); } } else { - // in sync mode, we'll move the deleted note into the trash - // folder + // 在同步模式下,我们将删除的笔记移动到垃圾箱文件夹 if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { Log.e(TAG, "Move notes to trash folder error, should not happens"); @@ -491,6 +606,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return widgets; } + /** + * 删除操作完成后的处理 + * 更新小部件并结束操作模式 + */ @Override protected void onPostExecute(HashSet widgets) { if (widgets != null) { @@ -506,6 +625,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } + /** + * 删除文件夹 + * 删除指定ID的文件夹及其内容 + * @param folderId 要删除的文件夹ID + */ private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); @@ -517,10 +641,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, folderId); if (!isSyncMode()) { - // if not synced, delete folder directly + // 如果未同步,直接删除文件夹 DataUtils.batchDeleteNotes(mContentResolver, ids); } else { - // in sync mode, we'll move the deleted folder into the trash folder + // 在同步模式下,我们将删除的文件夹移动到垃圾箱文件夹 DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); } if (widgets != null) { @@ -533,6 +657,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 打开笔记 + * 启动笔记编辑活动查看笔记内容 + * @param data 笔记数据项 + */ private void openNode(NoteItemData data) { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); @@ -540,6 +669,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); } + /** + * 打开文件夹 + * 显示文件夹内的笔记列表 + * @param data 文件夹数据项 + */ private void openFolder(NoteItemData data) { mCurrentFolderId = data.getId(); startAsyncNotesListQuery(); @@ -557,6 +691,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mTitleBar.setVisibility(View.VISIBLE); } + /** + * 点击事件处理 + * 处理界面上的按钮点击事件 + */ public void onClick(View v) { int viewId = v.getId(); if (viewId == R.id.btn_new_note) { @@ -564,6 +702,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 显示软键盘 + * 强制显示输入法键盘 + */ private void showSoftInput() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { @@ -571,11 +713,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 隐藏软键盘 + * 从指定视图隐藏输入法键盘 + * @param view 关联的视图 + */ private void hideSoftInput(View view) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } + /** + * 显示创建或修改文件夹对话框 + * 用于创建新文件夹或重命名现有文件夹 + * @param create 是否为创建操作,true表示创建,false表示重命名 + */ private void showCreateOrModifyFolderDialog(final boolean create) { final AlertDialog.Builder builder = new AlertDialog.Builder(this); View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); @@ -596,6 +748,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setPositiveButton(android.R.string.ok, null); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + /** + * 取消按钮点击事件处理 + * 隐藏软键盘 + */ public void onClick(DialogInterface dialog, int which) { hideSoftInput(etName); } @@ -604,6 +760,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt final Dialog dialog = builder.setView(view).show(); final Button positive = (Button)dialog.findViewById(android.R.id.button1); positive.setOnClickListener(new OnClickListener() { + /** + * 确认按钮点击事件处理 + * 创建或重命名文件夹 + */ public void onClick(View v) { hideSoftInput(etName); String name = etName.getText().toString(); @@ -638,14 +798,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt positive.setEnabled(false); } /** - * When the name edit text is null, disable the positive button + * 当名称编辑文本为空时,禁用确认按钮 */ etName.addTextChangedListener(new TextWatcher() { + /** + * 文本变化前的回调 + */ public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // TODO Auto-generated method stub + // TODO 自动生成的方法存根 } + /** + * 文本变化时的回调 + * 根据文本是否为空启用或禁用确认按钮 + */ public void onTextChanged(CharSequence s, int start, int before, int count) { if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); @@ -654,13 +821,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 文本变化后的回调 + */ public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub + // TODO 自动生成的方法存根 } }); } + /** + * 处理返回按钮事件 + * 根据当前状态决定返回行为 + */ @Override public void onBackPressed() { switch (mState) { @@ -685,6 +859,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + /** + * 更新桌面小部件 + * 发送广播通知小部件更新 + * @param appWidgetId 小部件ID + * @param appWidgetType 小部件类型 + */ private void updateWidget(int appWidgetId, int appWidgetType) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (appWidgetType == Notes.TYPE_WIDGET_2X) { @@ -704,7 +884,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt setResult(RESULT_OK, intent); } + /** + * 文件夹上下文菜单创建监听器 + * 为文件夹项创建上下文菜单 + */ private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + /** + * 创建上下文菜单 + * 添加查看、删除和重命名菜单项 + */ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mFocusNoteDataItem != null) { menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); @@ -715,6 +903,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }; + /** + * 上下文菜单关闭回调 + * 清除上下文菜单创建监听器 + */ @Override public void onContextMenuClosed(Menu menu) { if (mNotesListView != null) { @@ -723,6 +915,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt super.onContextMenuClosed(menu); } + /** + * 上下文菜单项选择处理 + * 处理文件夹的查看、删除和重命名操作 + */ @Override public boolean onContextItemSelected(MenuItem item) { if (mFocusNoteDataItem == null) { @@ -740,6 +936,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setMessage(getString(R.string.alert_message_delete_folder)); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + /** + * 确认删除文件夹对话框点击事件处理 + * 执行文件夹删除操作 + */ public void onClick(DialogInterface dialog, int which) { deleteFolder(mFocusNoteDataItem.getId()); } @@ -757,13 +957,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * 准备选项菜单 + * 根据当前状态显示不同的菜单 + */ @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); if (mState == ListEditState.NOTE_LIST) { getMenuInflater().inflate(R.menu.note_list, menu); - // set sync or sync_cancel - // Temporarily disabled sync functionality + // 设置同步或取消同步 + // 临时禁用同步功能 menu.findItem(R.id.menu_sync).setTitle(R.string.menu_sync); } else if (mState == ListEditState.SUB_FOLDER) { getMenuInflater().inflate(R.menu.sub_folder, menu); @@ -775,6 +979,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * 选项菜单项选择处理 + * 处理新建文件夹、导出文本、同步等操作 + */ @Override public boolean onOptionsItemSelected(MenuItem item) { int itemId = item.getItemId(); @@ -783,7 +991,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } else if (itemId == R.id.menu_export_text) { exportNoteToText(); } else if (itemId == R.id.menu_sync) { - // Temporarily disabled sync functionality + // 临时禁用同步功能 Toast.makeText(this, "Sync functionality temporarily disabled", Toast.LENGTH_SHORT).show(); startPreferenceActivity(); } else if (itemId == R.id.menu_setting) { @@ -796,21 +1004,36 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } + /** + * 搜索请求处理 + * 启动搜索界面 + */ @Override public boolean onSearchRequested() { startSearch(null, false, null /* appData */, false); return true; } + /** + * 导出笔记到文本文件 + * 异步执行导出操作 + */ private void exportNoteToText() { final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); new AsyncTask() { + /** + * 后台执行导出操作 + */ @Override protected Integer doInBackground(Void... unused) { return backup.exportToText(); } + /** + * 导出操作完成后的处理 + * 显示导出结果对话框 + */ @Override protected void onPostExecute(Integer result) { if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { @@ -844,20 +1067,36 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } + /** + * 判断是否为同步模式 + * @return 是否启用了同步 + */ private boolean isSyncMode() { - // Temporarily disabled sync functionality + // 临时禁用同步功能 return false; // return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + /** + * 启动偏好设置活动 + * 打开应用程序设置界面 + */ private void startPreferenceActivity() { Activity from = getParent() != null ? getParent() : this; Intent intent = new Intent(from, NotesPreferenceActivity.class); from.startActivityIfNeeded(intent, -1); } + /** + * 列表项点击监听器类 + * 处理笔记列表项的点击事件 + */ private class OnListItemClickListener implements OnItemClickListener { + /** + * 列表项点击事件处理 + * 根据当前状态和项目类型执行相应操作 + */ public void onItemClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { NoteItemData item = ((NotesListItem) view).getItemData(); @@ -897,6 +1136,10 @@ 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: @@ -915,6 +1158,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NoteColumns.MODIFIED_DATE + " DESC"); } + /** + * 列表项长按事件处理 + * 处理笔记和文件夹的长按操作 + */ public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData(); diff --git a/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java b/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..95142eb 100644 --- a/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java +++ b/app/src/main/java/net/micode/notes/ui/NotesListAdapter.java @@ -31,18 +31,30 @@ import java.util.HashSet; import java.util.Iterator; +/** + * 笔记列表适配器类 + * 用于显示笔记列表,支持多选模式,继承自CursorAdapter + */ public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; - private Context mContext; - private HashMap mSelectedIndex; - private int mNotesCount; - private boolean mChoiceMode; - + private Context mContext; // 上下文 + private HashMap mSelectedIndex; // 选中项索引 + private int mNotesCount; // 笔记数量 + private boolean mChoiceMode; // 是否为选择模式 + + /** + * 应用小部件属性类 + * 存储小部件的ID和类型 + */ public static class AppWidgetAttribute { - public int widgetId; - public int widgetType; + public int widgetId; // 小部件ID + public int widgetType; // 小部件类型 }; + /** + * 构造函数 + * @param context 上下文 + */ public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); @@ -50,11 +62,19 @@ public class NotesListAdapter extends CursorAdapter { mNotesCount = 0; } + /** + * 创建新视图 + * 当需要为游标中的行创建新视图时调用 + */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } + /** + * 绑定视图数据 + * 将游标中的数据绑定到已存在的视图 + */ @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { @@ -64,20 +84,37 @@ public class NotesListAdapter extends CursorAdapter { } } + /** + * 设置选中项 + * @param position 位置 + * @param checked 是否选中 + */ public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } + /** + * 判断是否处于选择模式 + * @return 如果是选择模式则返回true + */ public boolean isInChoiceMode() { return mChoiceMode; } + /** + * 设置选择模式 + * @param mode 是否为选择模式 + */ public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } + /** + * 全选或取消全选 + * @param checked 是否选中 + */ public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { @@ -89,6 +126,10 @@ public class NotesListAdapter extends CursorAdapter { } } + /** + * 获取所有选中项的ID + * @return 选中项ID的集合 + */ public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -105,6 +146,10 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + /** + * 获取所有选中项的小部件属性 + * @return 选中项小部件属性的集合 + */ public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -117,7 +162,7 @@ public class NotesListAdapter extends CursorAdapter { widget.widgetType = item.getWidgetType(); itemSet.add(widget); /** - * Don't close cursor here, only the adapter could close it + * 不要在这里关闭游标,只有适配器才能关闭它 */ } else { Log.e(TAG, "Invalid cursor"); @@ -128,6 +173,10 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } + /** + * 获取选中项数量 + * @return 选中项数量 + */ public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -143,11 +192,20 @@ public class NotesListAdapter extends CursorAdapter { return count; } + /** + * 判断是否全部选中 + * @return 如果全部选中则返回true + */ public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount != 0 && checkedCount == mNotesCount); } + /** + * 判断项目是否被选中 + * @param position 位置 + * @return 如果项目被选中则返回true + */ public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; @@ -155,18 +213,30 @@ 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/app/src/main/java/net/micode/notes/ui/NotesListItem.java b/app/src/main/java/net/micode/notes/ui/NotesListItem.java index 1221e80..6b6c9c8 100644 --- a/app/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/app/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -30,14 +30,22 @@ import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; +/** + * 笔记列表项类 + * 自定义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; // 复选框 + /** + * 构造函数 + * @param context 上下文 + */ public NotesListItem(Context context) { super(context); inflate(context, R.layout.note_item, this); @@ -48,6 +56,13 @@ public class NotesListItem extends LinearLayout { mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } + /** + * 绑定数据到视图 + * @param context 上下文 + * @param data 笔记项数据 + * @param choiceMode 是否处于选择模式 + * @param checked 是否被选中 + */ public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setVisibility(View.VISIBLE); @@ -58,6 +73,7 @@ public class NotesListItem extends LinearLayout { mItemData = data; if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 通话记录文件夹 mCallName.setVisibility(View.GONE); mAlert.setVisibility(View.VISIBLE); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); @@ -65,6 +81,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 +93,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); @@ -99,23 +119,37 @@ public class NotesListItem extends LinearLayout { setBackground(data); } + /** + * 设置项目背景 + * 根据项目类型和位置设置不同的背景资源 + * @param data 笔记项数据 + */ private void setBackground(NoteItemData data) { int id = data.getBgColorId(); if (data.getType() == Notes.TYPE_NOTE) { if (data.isSingle() || data.isOneFollowingFolder()) { + // 单个笔记或文件夹后的单个笔记 setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); } else if (data.isLast()) { + // 最后一个笔记 setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); } else if (data.isFirst() || data.isMultiFollowingFolder()) { + // 第一个笔记或文件夹后的多个笔记中的第一个 setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); } else { + // 中间的笔记 setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } } else { + // 文件夹背景 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } + /** + * 获取项目数据 + * @return 笔记项数据 + */ public NoteItemData getItemData() { return mItemData; } diff --git a/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java b/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java index 07c5f7e..db788ea 100644 --- a/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/app/src/main/java/net/micode/notes/ui/NotesPreferenceActivity.java @@ -48,32 +48,70 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; +/** + * 笔记偏好设置活动类 + * 管理应用的设置,包括账号同步和背景颜色等 + */ public class NotesPreferenceActivity extends PreferenceActivity { + /** + * 偏好设置文件名 + */ public static final String PREFERENCE_NAME = "notes_preferences"; + /** + * 同步账号名称的键 + */ public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; + /** + * 最后同步时间的键 + */ public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; + /** + * 设置背景颜色的键 + */ public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; + /** + * 同步账号类别的键 + */ private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; + /** + * 账号权限过滤键 + */ private static final String AUTHORITIES_FILTER_KEY = "authorities"; + /** + * 账号类别 + */ private PreferenceCategory mAccountCategory; + /** + * Google任务广播接收器 + */ private GTaskReceiver mReceiver; + /** + * 原始账号列表 + */ private Account[] mOriAccounts; + /** + * 是否已添加账号 + */ private boolean mHasAddedAccount; + /** + * 创建活动时调用 + * 初始化界面和注册广播接收器 + */ @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - /* using the app icon for navigation */ + /* 使用应用图标进行导航 */ getActionBar().setDisplayHomeAsUpEnabled(true); addPreferencesFromResource(R.xml.preferences); @@ -88,12 +126,15 @@ public class NotesPreferenceActivity extends PreferenceActivity { getListView().addHeaderView(header, null, true); } + /** + * 恢复活动时调用 + * 自动设置同步账号(如果用户添加了新账号) + */ @Override protected void onResume() { super.onResume(); - // need to set sync account automatically if user has added a new - // account + // 如果用户添加了新账号,需要自动设置同步账号 if (mHasAddedAccount) { Account[] accounts = getGoogleAccounts(); if (mOriAccounts != null && accounts.length > mOriAccounts.length) { @@ -116,6 +157,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { refreshUI(); } + /** + * 销毁活动时调用 + * 注销广播接收器 + */ @Override protected void onDestroy() { if (mReceiver != null) { @@ -124,6 +169,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { super.onDestroy(); } + /** + * 加载账号偏好设置 + * 创建账号设置项并设置点击监听器 + */ private void loadAccountPreference() { mAccountCategory.removeAll(); @@ -132,14 +181,17 @@ public class NotesPreferenceActivity extends PreferenceActivity { 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 { @@ -154,14 +206,21 @@ public class NotesPreferenceActivity extends PreferenceActivity { mAccountCategory.addPreference(accountPref); } + /** + * 加载同步按钮 + * 根据同步状态设置按钮文本和点击事件 + */ private void loadSyncButton() { Button syncButton = (Button) findViewById(R.id.preference_sync_button); TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - // set button state + // 设置按钮状态 if (GTaskSyncService.isSyncing()) { syncButton.setText(getString(R.string.preferences_button_sync_cancel)); syncButton.setOnClickListener(new View.OnClickListener() { + /** + * 取消同步按钮点击事件 + */ public void onClick(View v) { GTaskSyncService.cancelSync(NotesPreferenceActivity.this); } @@ -169,6 +228,9 @@ public class NotesPreferenceActivity extends PreferenceActivity { } else { syncButton.setText(getString(R.string.preferences_button_sync_immediately)); syncButton.setOnClickListener(new View.OnClickListener() { + /** + * 立即同步按钮点击事件 + */ public void onClick(View v) { GTaskSyncService.startSync(NotesPreferenceActivity.this); } @@ -176,7 +238,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); - // set last sync time + // 设置最后同步时间 if (GTaskSyncService.isSyncing()) { lastSyncTimeView.setText(GTaskSyncService.getProgressString()); lastSyncTimeView.setVisibility(View.VISIBLE); @@ -193,11 +255,19 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + /** + * 刷新用户界面 + * 重新加载账号偏好和同步按钮 + */ private void refreshUI() { loadAccountPreference(); loadSyncButton(); } + /** + * 显示选择账号对话框 + * 列出可用的Google账号供用户选择 + */ private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); @@ -229,6 +299,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } dialogBuilder.setSingleChoiceItems(items, checkedItem, new DialogInterface.OnClickListener() { + /** + * 账号选择点击事件 + * 设置选中的账号为同步账号 + */ public void onClick(DialogInterface dialog, int which) { setSyncAccount(itemMapping[which].toString()); dialog.dismiss(); @@ -242,6 +316,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { 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"); @@ -254,6 +332,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } + /** + * 显示更改账号确认对话框 + * 提示用户更改账号的风险 + */ private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); @@ -271,6 +353,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { getString(R.string.preferences_menu_cancel) }; dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { + /** + * 菜单项点击事件 + * 处理更改账号、移除账号或取消操作 + */ public void onClick(DialogInterface dialog, int which) { if (which == 0) { showSelectAccountAlertDialog(); @@ -283,11 +369,20 @@ public class NotesPreferenceActivity extends PreferenceActivity { dialogBuilder.show(); } + /** + * 获取Google账号列表 + * @return Google账号数组 + */ private Account[] getGoogleAccounts() { AccountManager accountManager = AccountManager.get(this); return accountManager.getAccountsByType("com.google"); } + /** + * 设置同步账号 + * 更新偏好设置并清理同步相关数据 + * @param account 账号名称 + */ private void setSyncAccount(String account) { if (!getSyncAccountName(this).equals(account)) { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -299,11 +394,14 @@ public class NotesPreferenceActivity extends PreferenceActivity { } editor.commit(); - // clean up last sync time + // 清理最后同步时间 setLastSyncTime(this, 0); - // clean up local gtask related info + // 清理本地Google任务相关信息 new Thread(new Runnable() { + /** + * 清理同步ID和Google任务ID + */ public void run() { ContentValues values = new ContentValues(); values.put(NoteColumns.GTASK_ID, ""); @@ -318,6 +416,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + /** + * 移除同步账号 + * 清除偏好设置中的账号信息和同步时间 + */ private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); @@ -329,8 +431,11 @@ public class NotesPreferenceActivity extends PreferenceActivity { } editor.commit(); - // clean up local gtask related info + // 清理本地Google任务相关信息 new Thread(new Runnable() { + /** + * 清理同步ID和Google任务ID + */ public void run() { ContentValues values = new ContentValues(); values.put(NoteColumns.GTASK_ID, ""); @@ -340,12 +445,22 @@ public class NotesPreferenceActivity extends PreferenceActivity { }).start(); } + /** + * 获取同步账号名称 + * @param context 上下文 + * @return 同步账号名称 + */ public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + /** + * 设置最后同步时间 + * @param context 上下文 + * @param time 时间(毫秒) + */ public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -354,14 +469,27 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.commit(); } + /** + * 获取最后同步时间 + * @param context 上下文 + * @return 最后同步时间(毫秒) + */ public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } + /** + * Google任务广播接收器类 + * 接收同步状态变化的广播并更新界面 + */ private class GTaskReceiver extends BroadcastReceiver { + /** + * 接收广播 + * 刷新界面并更新同步状态 + */ @Override public void onReceive(Context context, Intent intent) { refreshUI(); @@ -374,6 +502,10 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } + /** + * 选项菜单项点击事件处理 + * 处理返回主页等操作 + */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: