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 2a060d1..2f1305c 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java @@ -39,21 +39,26 @@ 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); + // 不显示标题栏 requestWindowFeature(Window.FEATURE_NO_TITLE); 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 @@ -61,77 +66,109 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); } + // 从Intent中获取便签ID和内容 Intent intent = getIntent(); try { + // 从URI路径中获取便签ID mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); + // 获取便签内容摘要 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) : mSnippet; } catch (IllegalArgumentException e) { e.printStackTrace(); + // 解析失败则关闭活动 return; } + // 初始化媒体播放器 mPlayer = new MediaPlayer(); + // 检查便签是否存在且有效 if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { + // 显示提醒对话框并播放提醒音 showActionDialog(); playAlarmSound(); } else { + // 便签不存在则关闭活动 finish(); } } + /** + * 检查屏幕是否处于点亮状态 + * @return 屏幕点亮返回true,否则返回false + */ private boolean isScreenOn() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); + // 判断屏幕是否处于交互状态(点亮) return pm.isInteractive(); } + /** + * 播放闹钟提醒音 + */ private void playAlarmSound() { + // 获取系统默认闹钟铃声的URI 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(); } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { - // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { - // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } + /** + * 显示提醒操作对话框 + */ private void showActionDialog() { AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(R.string.app_name); - dialog.setMessage(mSnippet); - dialog.setPositiveButton(R.string.notealert_ok, this); + dialog.setTitle(R.string.app_name); // 设置对话框标题为应用名称 + dialog.setMessage(mSnippet); // 设置对话框内容为便签摘要 + dialog.setPositiveButton(R.string.notealert_ok, this); // 添加"确定"按钮 + + // 如果屏幕已点亮,添加"进入"按钮用于查看和编辑便签 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); } + + // 显示对话框并设置关闭监听器 dialog.show().setOnDismissListener(this); } + /** + * 对话框按钮点击事件处理 + */ public void onClick(DialogInterface dialog, int which) { + // 处理"进入"按钮点击事件 if (which == DialogInterface.BUTTON_NEGATIVE) { + // 启动便签编辑界面,查看该便签详情 Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, mNoteId); @@ -139,16 +176,25 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } + /** + * 对话框关闭事件处理 + */ public void onDismiss(DialogInterface dialog) { + // 停止播放提醒音并关闭活动 stopAlarmSound(); finish(); } + /** + * 停止播放提醒音并释放资源 + */ private void stopAlarmSound() { if (mPlayer != null) { + // 停止播放 mPlayer.stop(); + // 释放媒体播放器资源 mPlayer.release(); mPlayer = null; } } -} +} \ No newline at end of file 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 43c91a0..dd4bf9d 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java @@ -2,16 +2,14 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 除非符合许可证要求,否则不得使用此文件。 + * 您可以从以下地址获取许可证副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,软件 + * 按“原样”分发,不附带任何明示或暗示的保证或条件。 + * 请参阅许可证,了解管理权限和限制的具体语言。 */ package net.micode.notes.ui; @@ -27,39 +25,68 @@ import android.database.Cursor; 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 + // 数据库查询投影字段:便签ID和提醒时间 + private static final String[] PROJECTION = new String[]{ + 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, + + // 查询待提醒的便签数据 + // 查询条件:提醒时间 > 当前时间 且 便签类型为普通便签 + Cursor c = context.getContentResolver().query( + Notes.CONTENT_NOTE_URI, // 便签内容URI + PROJECTION, // 查询字段 NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, - new String[] { String.valueOf(currentDate) }, - null); + new String[]{String.valueOf(currentDate)}, // 查询参数 + null); // 排序方式 + // 处理查询结果 if (c != null) { + // 移动到第一条记录 if (c.moveToFirst()) { do { + // 获取当前便签的提醒时间 long alertDate = c.getLong(COLUMN_ALERTED_DATE); + + // 创建闹钟触发时的广播意图 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, PendingIntent.FLAG_UPDATE_CURRENT); - AlarmManager alermManager = (AlarmManager) context + // 设置数据URI(附带便签ID) + sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + c.getLong(COLUMN_ID))); + + // 创建PendingIntent(用于触发AlarmReceiver) + // FLAG_UPDATE_CURRENT:若已存在则更新现有PendingIntent + PendingIntent pendingIntent = PendingIntent.getBroadcast( + context, 0, sender, PendingIntent.FLAG_UPDATE_CURRENT); + + // 获取系统闹钟服务 + AlarmManager alarmManager = (AlarmManager) context .getSystemService(Context.ALARM_SERVICE); - alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); - } while (c.moveToNext()); + + // 设置闹钟 + // AlarmManager.RTC_WAKEUP:唤醒系统并使用绝对时间 + // alertDate:闹钟触发时间 + // pendingIntent:触发时执行的PendingIntent + alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + + } while (c.moveToNext()); // 遍历所有符合条件的便签 } - c.close(); + c.close(); // 关闭Cursor释放资源 } } -} +} \ No newline at end of file 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..ea4c0c4 100644 --- a/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java +++ b/app/src/main/java/net/micode/notes/ui/AlarmReceiver.java @@ -1,17 +1,15 @@ /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 遵循Apache License, Version 2.0("许可证"); + * 除非遵守许可证,否则不得使用本文件。 + * 您可以在以下地址获取许可证副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,软件 + * 按"原样"分发,不附带任何明示或暗示的保证或条件。 + * 请参阅许可证,了解具体的权限和限制。 */ package net.micode.notes.ui; @@ -20,11 +18,18 @@ 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 intent.setClass(context, AlarmAlertActivity.class); + // 添加新任务标志,确保在独立任务栈中启动活动 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动闹钟提醒活动 context.startActivity(intent); } -} +} \ No newline at end of file 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..d330e02 100644 --- a/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java +++ b/app/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java @@ -29,62 +29,122 @@ 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(); + + // 是否使用 24 小时制 private boolean mIs24HourView; + + // 日期时间设置回调监听器 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) { + int dayOfMonth, int hourOfDay, int minute) { + // 更新 mDate 对象中的年月日时分信息 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.set(Calendar.SECOND, 0); // 秒数归零 mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); + + // 设置“确定”按钮,点击事件回调到当前类的 onClick 方法 setButton(context.getString(R.string.datetime_dialog_ok), this); - setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); + // 设置“取消”按钮,无回调事件 + setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener) null); + + // 设置当前时间格式(是否 24 小时制) set24HourView(DateFormat.is24HourFormat(this.getContext())); + + // 初始化对话框标题 updateTitle(mDate.getTimeInMillis()); } + /** + * 设置是否 24 小时制显示 + * @param is24HourView true 为 24 小时制,false 为 12 小时制 + */ 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 | - DateUtils.FORMAT_SHOW_DATE | - DateUtils.FORMAT_SHOW_TIME; + 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) { + /** + * 点击对话框按钮时的回调方法 + * @param dialog 对话框对象 + * @param which 按钮类型(确定、取消) + */ + public void onClick(DialogInterface dialog, int which) { 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..cd2dd91 100644 --- a/app/src/main/java/net/micode/notes/ui/DropdownMenu.java +++ b/app/src/main/java/net/micode/notes/ui/DropdownMenu.java @@ -27,17 +27,42 @@ import android.widget.PopupMenu.OnMenuItemClickListener; import net.micode.notes.R; +/** + * 自定义下拉菜单控件,封装了 Button 和 PopupMenu 的交互逻辑 + */ public class DropdownMenu { + + // 用作点击触发下拉菜单的按钮 private Button mButton; + + // Android 提供的弹出式菜单控件 private PopupMenu mPopupMenu; + + // 菜单项对象 private Menu mMenu; + /** + * 构造方法,初始化 DropdownMenu + * @param context 上下文对象 + * @param button 作为菜单触发器的按钮 + * @param menuId 菜单资源文件 ID(定义菜单项) + */ 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 +70,29 @@ public class DropdownMenu { }); } + /** + * 设置菜单项点击监听器 + * @param listener 菜单项点击事件回调 + */ public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } + /** + * 通过菜单项 ID 查找指定菜单项 + * @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..77f887c 100644 --- a/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java +++ b/app/src/main/java/net/micode/notes/ui/FoldersListAdapter.java @@ -28,50 +28,95 @@ import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; - +/** + * FoldersListAdapter 用于将数据库中的文件夹数据绑定到 ListView 的适配器 + */ public class FoldersListAdapter extends CursorAdapter { - public static final String [] PROJECTION = { - NoteColumns.ID, - NoteColumns.SNIPPET + + // 数据库查询所需的字段(文件夹 ID 和摘要) + public static final String[] PROJECTION = { + NoteColumns.ID, // 文件夹 ID + NoteColumns.SNIPPET // 文件夹显示名称(摘要) }; - public static final int ID_COLUMN = 0; + // 数据库查询结果中 ID 和名称的列索引 + public static final int ID_COLUMN = 0; 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 + // 自动生成的构造方法 } + /** + * 创建新的文件夹列表项视图 + * @param context 上下文 + * @param cursor 游标(当前数据) + * @param parent 父布局 + * @return 新建的文件夹列表项视图 + */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new FolderListItem(context); } + /** + * 将当前游标的数据绑定到对应的列表项视图 + * @param view 当前列表项视图 + * @param context 上下文 + * @param cursor 当前游标 + */ @Override public void bindView(View view, Context context, Cursor cursor) { 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); + // 判断是否为根目录 + String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) + ? context.getString(R.string.menu_move_parent_folder) // 显示“上一级文件夹” + : cursor.getString(NAME_COLUMN); // 显示普通文件夹名称 ((FolderListItem) view).bind(folderName); } } + /** + * 获取指定位置的文件夹名称 + * @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); + return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) + ? context.getString(R.string.menu_move_parent_folder) + : cursor.getString(NAME_COLUMN); } + /** + * 自定义的文件夹列表项视图,继承自 LinearLayout + */ 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); + // 绑定显示文件夹名称的 TextView 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/NotesListItem.java b/app/src/main/java/net/micode/notes/ui/NotesListItem.java index 1221e80..aa3db5d 100644 --- a/app/src/main/java/net/micode/notes/ui/NotesListItem.java +++ b/app/src/main/java/net/micode/notes/ui/NotesListItem.java @@ -29,17 +29,31 @@ import net.micode.notes.data.Notes; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - +/** + * NotesListItem 是笔记列表的单个条目视图,支持显示普通笔记、文件夹、通话记录等多种样式 + */ public class NotesListItem extends LinearLayout { + + // 提醒图标 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); mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mTitle = (TextView) findViewById(R.id.tv_title); @@ -48,7 +62,15 @@ 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); mCheckBox.setChecked(checked); @@ -57,6 +79,8 @@ public class NotesListItem extends LinearLayout { } mItemData = data; + + // 如果是通话记录文件夹 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.GONE); mAlert.setVisibility(View.VISIBLE); @@ -64,27 +88,33 @@ public class NotesListItem extends LinearLayout { mTitle.setText(context.getString(R.string.call_record_folder_name) + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setImageResource(R.drawable.call_record); - } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { + } + // 如果是通话记录文件夹下的记录 + else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.VISIBLE); mCallName.setText(data.getCallName()); - mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); + mTitle.setTextAppearance(context, R.style.TextAppearanceSecondaryItem); mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); - if (data.hasAlert()) { + if (data.hasAlert()) { // 如果设置了提醒 mAlert.setImageResource(R.drawable.clock); mAlert.setVisibility(View.VISIBLE); } else { mAlert.setVisibility(View.GONE); } - } else { + } + // 其他普通笔记或文件夹 + 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())); + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setVisibility(View.GONE); - } else { + } + // 如果是普通笔记 + else { mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); if (data.hasAlert()) { mAlert.setImageResource(R.drawable.clock); @@ -94,28 +124,39 @@ public class NotesListItem extends LinearLayout { } } } + + // 显示相对时间(如:5分钟前) mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); + // 设置背景样式 setBackground(data); } + /** + * 根据笔记类型和位置设置对应的背景样式 + * @param data 当前笔记数据 + */ private void setBackground(NoteItemData data) { int id = data.getBgColorId(); - if (data.getType() == Notes.TYPE_NOTE) { - if (data.isSingle() || data.isOneFollowingFolder()) { + if (data.getType() == Notes.TYPE_NOTE) { // 普通笔记 + if (data.isSingle() || data.isOneFollowingFolder()) { // 单个笔记或仅后面跟随文件夹 setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); - } else if (data.isLast()) { + } else if (data.isLast()) { // 列表最后一个 setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); - } else if (data.isFirst() || data.isMultiFollowingFolder()) { + } else if (data.isFirst() || data.isMultiFollowingFolder()) { // 列表第一个或前后跟随文件夹 setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); - } else { + } else { // 中间笔记 setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); } - } else { + } else { // 文件夹 setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } + /** + * 获取当前条目的数据对象 + * @return NoteItemData + */ public NoteItemData getItemData() { return mItemData; }