ui大部分注释和widget包注释

kongzikang
孔子康 8 months ago
parent ad62bc39b8
commit fb1e409f46

@ -14,67 +14,74 @@
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
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;
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.view.Window;
import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import java.io.IOException;
// AlarmAlertActivity类继承自Activity同时实现了OnClickListener和OnDismissListener接口
// 主要用于处理闹钟提醒相关的功能,例如展示提醒界面、播放提醒声音以及响应用户在提醒对话框中的操作等
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId; // 用于存储对应笔记的唯一标识符ID
private String mSnippet; // 用于存储笔记内容的摘要信息,方便展示给用户查看
private static final int SNIPPET_PREW_MAX_LEN = 60; // 定义笔记摘要信息展示的最大长度,超过该长度会进行截断处理
MediaPlayer mPlayer; // 用于播放闹钟提醒声音的MediaPlayer对象实例
// onCreate方法是Android系统中Activity的生命周期方法在Activity创建时被调用在此方法中完成Activity的初始化工作例如设置窗口属性、获取传递过来的意图数据、准备播放声音以及展示提醒对话框等操作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 请求去除Activity的标题栏使界面更加简洁专注于闹钟提醒内容展示
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // 设置窗口的标志位,使得窗口在屏幕锁定的情况下也能够显示出来,确保用户能及时看到提醒
if (!isScreenOn()) {
// 如果屏幕当前处于关闭状态,添加以下多个标志位,用于控制屏幕相关的行为,比如保持屏幕常亮、点亮屏幕、允许在屏幕锁定时进行操作等
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
Intent intent = getIntent();
Intent intent = getIntent(); // 获取启动该Activity的Intent对象用于获取传递过来的相关数据
try {
// 从Intent传递的数据中获取笔记的ID这里假设数据的格式是特定的路径形式从中提取出对应位置的元素并转换为长整型
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
// 通过DataUtils工具类的方法根据笔记ID从内容解析器中获取笔记的摘要内容
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
// 如果摘要内容的长度超过了预设的最大长度,进行截断处理,并添加特定的提示字符串,否则直接使用原摘要内容
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;
e.printStackTrace(); // 如果在获取数据过程中出现参数异常,打印异常堆栈信息方便调试
return; // 直接返回,不再继续执行后续可能依赖正确数据的操作
}
mPlayer = new MediaPlayer();
mPlayer = new MediaPlayer(); // 创建一个新的MediaPlayer对象用于后续播放闹钟提醒声音
// 通过DataUtils工具类判断该笔记在数据库中是否可见满足特定的类型条件等如果可见则展示操作对话框并播放闹钟声音否则直接结束该Activity
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
playAlarmSound();
@ -83,56 +90,70 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
// isScreenOn方法用于判断当前设备的屏幕是否处于开启状态
// 通过获取系统的PowerManager服务并调用其isScreenOn方法来进行判断返回一个布尔值表示屏幕的开启情况
private boolean isScreenOn() {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
// playAlarmSound方法用于播放闹钟提醒的声音主要涉及获取铃声资源、根据系统设置配置音频流类型、准备并启动声音播放等操作
// 如果在过程中出现各种异常情况,会打印相应的异常堆栈信息方便排查问题
private void playAlarmSound() {
// 通过RingtoneManager获取系统中设置的默认闹钟铃声的Uri作为播放声音的数据源
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
// 从系统设置中获取与静音模式相关的音频流设置值用于后续判断如何配置MediaPlayer的音频流类型
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
// 根据静音模式相关设置判断是否使用特定的音频流类型如果满足条件则设置MediaPlayer的音频流类型为获取到的设置值否则设置为常规的闹钟音频流类型
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM))!= 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
// 为MediaPlayer设置声音数据源即之前获取到的闹钟铃声的Uri
mPlayer.setDataSource(this, url);
mPlayer.prepare();
mPlayer.setLooping(true);
mPlayer.start();
mPlayer.prepare(); // 准备MediaPlayer进行播放例如加载音频数据等操作
mPlayer.setLooping(true); // 设置声音循环播放,以持续提醒用户
mPlayer.start(); // 启动MediaPlayer开始播放闹钟提醒声音
} 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
// 如果MediaPlayer处于不正确的状态而导致操作异常例如在未准备好的情况下尝试播放打印异常堆栈信息
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
// 如果在读取数据源等I/O操作中出现异常例如文件不存在或无法读取打印异常堆栈信息
e.printStackTrace();
}
}
// showActionDialog方法用于创建并显示一个包含操作按钮的对话框用于向用户展示笔记摘要信息以及提供相应的操作选项
// 例如确认提醒或者进入笔记详情等操作同时设置对话框关闭的监听器为当前类因为实现了OnDismissListener接口
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);
AlertDialog.Builder dialog = new AlertDialog.Builder(this); // 创建一个AlertDialog的构建器传入当前Activity上下文
dialog.setTitle(R.string.app_name); // 设置对话框的标题为应用名称,通常用于标识该提醒所属的应用
dialog.setMessage(mSnippet); // 设置对话框显示的消息内容为之前获取到的笔记摘要信息
dialog.setPositiveButton(R.string.notealert_ok, this); // 设置对话框的确定按钮点击按钮的响应逻辑由当前类实现的OnClickListener接口处理
if (isScreenOn()) {
// 如果屏幕处于开启状态,添加一个进入按钮,点击该按钮可进入笔记编辑等相关界面,同样点击响应逻辑由当前类处理
dialog.setNegativeButton(R.string.notealert_enter, this);
}
dialog.show().setOnDismissListener(this);
dialog.show().setOnDismissListener(this); // 显示对话框,并设置对话框关闭的监听器为当前类实例,以便在对话框关闭时执行相应逻辑
}
// onClick方法是实现OnClickListener接口的方法用于处理对话框中按钮点击的事件
// 根据点击的按钮不同执行相应的操作,例如点击进入按钮时会跳转到笔记编辑页面等
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_NEGATIVE:
// 当点击Negative按钮通常是进入相关界面的按钮创建一个Intent用于启动NoteEditActivity
// 设置相应的动作和传递笔记的ID作为额外数据然后启动该Activity实现界面跳转
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, mNoteId);
@ -143,16 +164,20 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD
}
}
// onDismiss方法是实现OnDismissListener接口的方法用于处理对话框关闭的事件
// 在对话框关闭时会调用stopAlarmSound方法停止正在播放的闹钟声音并结束当前Activity
public void onDismiss(DialogInterface dialog) {
stopAlarmSound();
finish();
}
// stopAlarmSound方法用于停止正在播放的闹钟声音
// 首先判断MediaPlayer对象是否为空如果不为空则执行停止播放、释放资源的操作并将MediaPlayer对象置为null释放相关内存资源
private void stopAlarmSound() {
if (mPlayer != null) {
if (mPlayer!= null) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
}
}
}
}

@ -14,52 +14,74 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// AlarmInitReceiver类继承自BroadcastReceiver用于接收系统广播消息
// 其主要功能是根据笔记的提醒日期等条件初始化设置闹钟提醒相关的PendingIntent和AlarmManager
// 以便在指定时间触发相应的提醒操作。
public class AlarmInitReceiver extends BroadcastReceiver {
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义一个字符串数组用于指定查询数据库时需要获取的列信息这里只获取笔记的ID和提醒日期这两列数据。
private static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE
};
// 定义常量表示查询结果中笔记ID列对应的索引位置方便后续从Cursor中获取相应数据初始化为0表示在结果集中的第一列从0开始计数
private static final int COLUMN_ID = 0;
// 定义常量表示查询结果中提醒日期列对应的索引位置方便后续从Cursor中获取相应数据初始化为1表示在结果集中的第二列从0开始计数
private static final int COLUMN_ALERTED_DATE = 1;
// onReceive方法是BroadcastReceiver类中必须实现的抽象方法当该广播接收器接收到相应广播时会被调用
// 在此方法中会查询数据库中符合条件的笔记记录,并为每条记录设置对应的闹钟提醒。
@Override
public void onReceive(Context context, Intent intent) {
// 获取当前系统的时间戳(以毫秒为单位),用于后续作为条件筛选出提醒日期大于当前时间的笔记记录,即还未到提醒时间但需要设置提醒的笔记。
long currentDate = System.currentTimeMillis();
// 通过内容解析器查询数据库从指定的笔记内容UriNotes.CONTENT_NOTE_URI中获取数据
// 使用之前定义的PROJECTION指定要获取的列通过条件筛选出提醒日期大于当前时间且类型为常规笔记Notes.TYPE_NOTE的记录
// 条件中的占位符通过后面的字符串数组进行替换最后一个参数null表示不进行排序等额外操作。
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[] { String.valueOf(currentDate) },
null);
if (c != null) {
// 判断游标是否成功获取到结果,如果不为空,表示查询到了符合条件的笔记记录。
if (c!= null) {
// 将游标移动到查询结果的第一条记录位置如果有记录则返回true可以开始遍历结果集。
if (c.moveToFirst()) {
do {
// 从游标中获取提醒日期这一列的数据以长整型表示对应之前定义的COLUMN_ALERTED_DATE索引位置
// 该日期就是这条笔记设置的提醒时间点(以毫秒为单位的时间戳)。
long alertDate = c.getLong(COLUMN_ALERTED_DATE);
// 创建一个Intent对象用于指定当闹钟触发时要启动的广播接收器这里指定为AlarmReceiver类
// 并且通过ContentUris.withAppendedId方法将当前笔记的ID附加到对应的内容Uri上以便后续能识别是哪个笔记的提醒触发了。
Intent sender = new Intent(context, AlarmReceiver.class);
sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID)));
// 创建一个PendingIntent对象用于包装之前创建的Intent使得该Intent可以在合适的时机闹钟触发时被系统安全地调用
// 参数中的0表示请求码这里暂设为0最后的0表示默认的标志位也可根据具体需求设置不同的标志位来控制其行为。
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取系统的AlarmManager服务用于设置闹钟相关的操作如指定触发时间、关联PendingIntent等。
AlarmManager alermManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
// 使用AlarmManager的set方法设置一个一次性的闹钟提醒指定闹钟类型为RTC_WAKEUP在指定的绝对时间唤醒设备并触发提醒即使设备处于睡眠状态
// 传入之前获取到的提醒日期作为触发时间以及关联对应的PendingIntent当到达提醒时间时系统会通过该PendingIntent触发相应的广播操作。
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext());
} while (c.moveToNext()); // 将游标移动到下一条记录位置,如果还有下一条记录则继续循环执行上述设置闹钟的操作。
}
c.close(); // 关闭游标,释放相关资源,避免内存泄漏等问题,因为查询操作完成后不再需要游标了。
}
c.close();
}
}
}

@ -14,17 +14,22 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AlarmReceiver extends BroadcastReceiver {
// 定义一个广播接收器类继承自BroadcastReceiver用于接收系统广播等相关消息
public class AlarmReceiver extends BroadcastReceiver {
// 重写onReceive方法当接收到广播时会执行此方法
@Override
public void onReceive(Context context, Intent intent) {
// 设置intent要启动的目标Activity为AlarmAlertActivity.class这样后续启动时就会启动对应的Activity
intent.setClass(context, AlarmAlertActivity.class);
// 给intent添加一个标志表明启动的Activity是一个新任务常用于从非Activity的上下文中启动Activity的场景
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 通过传入的上下文对象启动对应的Activity这里利用了之前设置好的intent相关信息
context.startActivity(intent);
}
}
}

@ -14,77 +14,108 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import java.util.Calendar;
import java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
// 自定义的日期时间选择对话框类继承自AlertDialog用于以对话框的形式展示日期时间选择界面并处理相关交互逻辑
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
// 用于存储当前选择的日期时间信息的Calendar对象初始化为当前系统时间对应的实例后续会根据用户选择进行更新
private Calendar mDate = Calendar.getInstance();
// 用于标记是否处于24小时制视图初始会根据系统设置进行判断赋值后续也可通过方法进行设置改变
private boolean mIs24HourView;
// 定义一个接口类型的成员变量,用于监听用户完成日期时间选择并点击确定按钮后的操作,外部类可实现此接口来响应最终的选择结果
private OnDateTimeSetListener mOnDateTimeSetListener;
// 包含日期时间选择具体交互控件的DateTimePicker实例用于在对话框中展示可操作的日期时间选择界面
private DateTimePicker mDateTimePicker;
// 定义一个接口,外部类需要实现此接口,当用户在对话框中完成日期时间选择并点击确定按钮后,会回调此接口中的方法,传入对话框本身以及最终选择的日期时间(以毫秒为单位的时间戳)
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
// 构造函数创建一个DateTimePickerDialog实例传入上下文以及初始的日期时间以毫秒为单位的时间戳完成对话框的初始化设置
public DateTimePickerDialog(Context context, long date) {
super(context);
// 创建一个DateTimePicker实例用于在对话框中展示日期时间选择的具体交互界面
mDateTimePicker = new DateTimePicker(context);
// 将DateTimePicker实例设置为对话框的视图内容这样对话框中就会显示日期时间选择相关的UI组件
setView(mDateTimePicker);
// 为DateTimePicker设置日期时间改变监听器当用户在DateTimePicker中操作改变了日期、时间等内容时会触发此监听器中的方法
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
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方法更新对话框标题显示使其展示当前选择的日期时间信息
updateTitle(mDate.getTimeInMillis());
}
});
// 设置对话框初始显示的日期时间先将传入的时间戳设置到内部的Calendar对象mDate同时将秒数设置为0保证初始时间更规整
mDate.setTimeInMillis(date);
mDate.set(Calendar.SECOND, 0);
// 将DateTimePicker的当前日期时间设置为和内部存储的初始日期时间一致保证界面显示和内部数据的同步
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
// 设置对话框的确定按钮文本以及点击监听器点击确定按钮时会触发当前类实现了OnClickListener接口的onClick方法
setButton(context.getString(R.string.datetime_dialog_ok), this);
// 设置对话框的取消按钮文本以及点击监听器为null表示点击取消按钮直接关闭对话框不做额外处理
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
// 根据系统设置判断是否为24小时制视图并设置到内部相应的变量以及对应的UI显示状态例如是否显示AM/PM选择器等
set24HourView(DateFormat.is24HourFormat(this.getContext()));
// 初始更新一次对话框标题,展示初始的日期时间信息
updateTitle(mDate.getTimeInMillis());
}
// 设置是否为24小时制视图的方法传入布尔值参数更新内部的标记变量并会影响DateTimePicker中相关UI组件的显示情况如AM/PM选择器的可见性等
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
// 设置日期时间选择完成后的监听器方法外部类可通过传入实现了OnDateTimeSetListener接口的实例来响应最终用户确定选择的操作
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
// 根据传入的日期时间(以毫秒为单位的时间戳)更新对话框的标题显示内容,按照指定的格式展示日期、时间等信息
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
DateUtils.FORMAT_SHOW_YEAR | // 显示年份
DateUtils.FORMAT_SHOW_DATE | // 显示日期(月、日等)
DateUtils.FORMAT_SHOW_TIME; // 显示时间(小时、分钟等)
// 根据是否为24小时制视图添加对应的时间格式标志用于控制最终显示的时间格式24小时制还是12小时制带AM/PM
flag |= mIs24HourView? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_12HOUR;
// 使用DateUtils工具类的formatDateTime方法按照设置好的格式标志将传入的日期时间转换为字符串并设置为对话框的标题内容
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}
// 实现OnClickListener接口的onClick方法当用户点击对话框中的按钮确定按钮因为在构造函数中设置了当前类为确定按钮的点击监听器时会触发此方法
// 如果设置了日期时间选择完成后的监听器mOnDateTimeSetListener则调用其OnDateTimeSet方法传入对话框本身以及当前选择的日期时间以毫秒为单位的时间戳通知外部类用户已完成选择操作
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
if (mOnDateTimeSetListener!= null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}
}
}

@ -14,30 +14,45 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import android.content.Context;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
import net.micode.notes.R;
public class DropdownMenu {
// 自定义的下拉菜单类,用于创建和管理一个基于按钮点击弹出的菜单,方便在安卓界面中实现下拉式的功能菜单交互
public class DropdownMenu {
// 用于显示下拉菜单的按钮控件,点击此按钮会弹出对应的菜单,外部传入并在类内部进行相关操作
private Button mButton;
// 实际的PopupMenu实例它管理着弹出菜单的显示、隐藏以及菜单项相关操作等逻辑
private PopupMenu mPopupMenu;
// 对应的Menu实例用于获取、操作具体的菜单项通过PopupMenu获取并进行后续的查找、设置等操作
private Menu mMenu;
// 构造函数用于创建一个DropdownMenu实例传入上下文、用于触发弹出菜单的按钮以及菜单资源ID用于定义菜单项等信息
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
// 设置按钮的背景资源为指定的下拉图标资源通常用于提示用户此按钮可点击弹出菜单这里从R.drawable.dropdown_icon获取对应的图标资源
mButton.setBackgroundResource(R.drawable.dropdown_icon);
// 创建一个PopupMenu实例关联传入的上下文以及触发弹出的按钮这样弹出菜单会基于此按钮的位置等属性进行显示
mPopupMenu = new PopupMenu(context, mButton);
// 获取PopupMenu对应的Menu实例后续可以通过这个实例来操作具体的菜单项例如添加、查找、设置菜单项属性等
mMenu = mPopupMenu.getMenu();
// 使用菜单填充器从PopupMenu获取根据传入的菜单资源ID通常在XML资源文件中定义了菜单项的文本、图标等信息将菜单项填充到Menu实例中完成菜单的初始化构建
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
// 为按钮设置点击监听器当按钮被点击时触发内部的onClick方法在该方法中调用PopupMenu的show方法来显示弹出菜单
mButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mPopupMenu.show();
@ -45,17 +60,21 @@ public class DropdownMenu {
});
}
// 设置下拉菜单中菜单项的点击监听器方法传入实现了OnMenuItemClickListener接口的实例用于响应各个菜单项被点击后的操作逻辑
// 如果PopupMenu实例不为空则将传入的监听器设置给PopupMenu这样当用户点击菜单项时对应的回调方法就会被触发
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
if (mPopupMenu!= null) {
mPopupMenu.setOnMenuItemClickListener(listener);
}
}
// 根据传入的菜单项ID在已构建的Menu实例中查找对应的MenuItem实例方便外部类获取菜单项进行进一步的操作例如获取菜单项的文本、图标、设置是否可用等
public MenuItem findItem(int id) {
return mMenu.findItem(id);
}
// 设置按钮显示的文本内容,可用于给下拉菜单按钮设置一个标题之类的提示性文字,方便用户了解此下拉菜单的大致功能等信息
public void setTitle(CharSequence title) {
mButton.setText(title);
}
}
}

@ -14,67 +14,90 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
// FoldersListAdapter类继承自CursorAdapter用于将数据库游标Cursor中的数据适配到特定的视图上
// 在这里主要是为了展示文件夹相关信息列表比如文件夹名称等方便在列表视图等UI组件中显示。
public class FoldersListAdapter extends CursorAdapter {
public class FoldersListAdapter extends CursorAdapter {
// 定义一个字符串数组,用于指定从数据库查询时需要获取的列名,这里只获取了"ID"和"SNIPPET"两列数据,
// 通常会对应数据库中存储文件夹相关信息的列,后续用于填充视图展示给用户。
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};
// 定义一个常量表示在查询结果游标Cursor中"ID"列所在的索引位置,方便后续通过游标获取对应列的数据。
public static final int ID_COLUMN = 0;
// 定义一个常量表示在查询结果游标Cursor中"NAME"此处实际对应SNIPPET列可能用于表示文件夹名称相关内容列所在的索引位置。
public static final int NAME_COLUMN = 1;
// 构造函数接收上下文Context和数据库游标Cursor作为参数调用父类CursorAdapter的构造函数进行初始化操作。
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
// TODO Auto-generated constructor stub,这里可以添加一些自定义的初始化逻辑,如果有需要的话,当前为空。
}
// 重写CursorAdapter的newView方法此方法用于创建一个新的视图View对象该视图将用于显示游标中对应的数据项。
// 在这里返回一个自定义的FolderListItem实例它继承自LinearLayout是每个列表项对应的视图布局。
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context);
}
// 重写CursorAdapter的bindView方法此方法用于将游标Cursor中当前位置的数据绑定到指定的视图View
// 也就是填充视图中的各个控件如TextView等使其显示对应的数据内容。
@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
// 根据游标中获取的文件夹ID判断是否是根文件夹通过和Notes.ID_ROOT_FOLDER比较
// 如果是根文件夹,则获取对应的字符串资源(通常是用于显示"上级文件夹"之类表示根文件夹含义的文本)作为文件夹名称,
// 如果不是根文件夹则从游标中获取对应列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的bind方法将获取到的文件夹名称设置到对应的TextView控件中完成数据绑定显示操作。
((FolderListItem) view).bind(folderName);
}
}
// 定义一个方法用于根据给定的位置position获取对应的文件夹名称。
// 首先通过getItem方法继承自CursorAdapter获取该位置对应的游标Cursor对象
// 然后同样根据游标中文件夹ID判断是否是根文件夹来确定返回的文件夹名称内容。
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER)? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
// 定义一个内部类FolderListItem继承自LinearLayout代表每个文件夹列表项的具体视图布局
// 内部包含了用于显示文件夹名称的TextView控件并提供了相应的方法来设置显示的文本内容。
private class FolderListItem extends LinearLayout {
private TextView mName;
// 构造函数接收上下文Context作为参数调用父类LinearLayout的构造函数进行初始化
// 并通过inflate方法加载对应的布局文件R.layout.folder_list_item到当前视图中
// 然后获取布局中用于显示文件夹名称的TextView控件实例。
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this);
mName = (TextView) findViewById(R.id.tv_folder_name);
}
// 定义一个方法用于将传入的文件夹名称name设置到内部的TextView控件mName实现文本显示更新。
public void bind(String name) {
mName.setText(name);
}
}
}
}

@ -14,194 +14,259 @@
* limitations under the License.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
public class NoteEditText extends EditText {
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
// NoteEditText类继承自EditText是一个自定义的文本编辑框控件在原生EditText的基础上添加了一些特定的功能和交互逻辑
// 例如处理特定按键事件、触摸事件以及上下文菜单相关操作等,用于笔记编辑等相关场景。
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
// 用于记录当前文本编辑框的索引位置,可能在多个编辑框组成的列表等场景下用于区分不同的编辑框个体。
private int mIndex;
// 用于记录在删除操作前文本选择的起始位置,方便后续判断删除相关逻辑,例如是否删除整个编辑框内容等情况。
private int mSelectionStartBeforeDelete;
// 定义表示电话号码链接的协议头scheme字符串用于识别文本中是否包含电话号码链接。
private static final String SCHEME_TEL = "tel:" ;
// 定义表示网页链接HTTP协议的协议头字符串用于识别文本中的网页链接。
private static final String SCHEME_HTTP = "http:" ;
// 定义表示电子邮件链接的协议头字符串,用于识别文本中的邮件链接。
private static final String SCHEME_EMAIL = "mailto:" ;
// 创建一个HashMap用于存储不同协议头scheme与对应的字符串资源ID的映射关系
// 这些字符串资源ID通常用于在界面上显示对应链接类型的操作提示文本等内容。
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
// 将不同协议头与对应的字符串资源ID添加到映射表中方便后续根据链接类型查找对应的显示文本资源。
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
// 定义一个接口,用于与外部类进行交互,外部类实现此接口可以监听该文本编辑框内文本相关的变化事件,
// 例如文本删除、回车键按下添加新编辑框以及文本有无变化等情况,并做出相应处理。
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
* {@link KeyEvent#KEYCODE_DEL}
*
*/
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
* {@link KeyEvent#KEYCODE_ENTER}
*
*/
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
* UI
*/
void onTextChange(int index, boolean hasText);
}
// 用于存储实现了OnTextViewChangeListener接口的实例以便在文本编辑框相关事件发生时通知外部类进行相应处理。
private OnTextViewChangeListener mOnTextViewChangeListener;
// 构造函数只传入上下文Context参数调用父类构造函数创建实例并初始化索引位置为0
// 这种构造方式常用于通过代码动态创建该编辑框实例的场景。
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
// 设置当前文本编辑框的索引位置的方法,外部可调用此方法来更新索引值,方便区分不同编辑框个体。
public void setIndex(int index) {
mIndex = index;
}
// 设置文本变化监听器的方法外部类通过传入实现了OnTextViewChangeListener接口的实例
// 来注册监听该编辑框相关文本变化事件,以便做出相应处理。
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
// 构造函数传入上下文Context和属性集AttributeSet参数按照安卓系统默认的文本编辑框样式通过android.R.attr.editTextStyle指定创建实例
// 通常用于在XML布局文件中定义该编辑框并解析相关属性进行初始化的场景。
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
// 构造函数传入上下文Context、属性集AttributeSet和默认样式defStyle参数创建编辑框实例
// 可用于更灵活地自定义编辑框的样式和属性当前构造函数中没有添加额外的初始化逻辑TODO处可按需添加
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
// 重写父类的onTouchEvent方法用于处理触摸事件在这里主要是处理按下ACTION_DOWN动作
// 目的是根据触摸的坐标位置来设置文本的选择位置,方便用户后续进行复制、粘贴等操作与选中位置相关的操作。
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 获取触摸点的原始X坐标
int x = (int) event.getX();
// 获取触摸点的原始Y坐标
int y = (int) event.getY();
// 减去左边的总内边距,将坐标转换为相对于文本内容区域的坐标
x -= getTotalPaddingLeft();
// 减去上边的总内边距,同样将坐标转换为相对于文本内容区域的坐标
y -= getTotalPaddingTop();
// 加上滚动的X偏移量考虑文本滚动情况获取准确的相对于文本内容的坐标
x += getScrollX();
// 加上滚动的Y偏移量获取准确的相对于文本内容的坐标
y += getScrollY();
// 获取文本的布局信息对象,用于后续根据坐标获取对应文本位置等操作
Layout layout = getLayout();
// 根据触摸点的垂直坐标Y坐标获取对应的文本行数
int line = layout.getLineForVertical(y);
// 根据触摸点的水平坐标X坐标以及所在行数获取对应的文本字符偏移量即文本中的位置索引
int off = layout.getOffsetForHorizontal(line, x);
// 根据获取到的字符偏移量设置文本的选择范围,这里只设置了一个位置,相当于将光标定位到该位置
Selection.setSelection(getText(), off);
break;
}
// 调用父类的onTouchEvent方法继续处理其他触摸事件相关逻辑保证原生的触摸行为也能正常执行
return super.onTouchEvent(event);
}
// 重写父类的onKeyDown方法用于处理按键按下事件在这里主要是针对回车键KEYCODE_ENTER和删除键KEYCODE_DEL按下时做一些前期记录等准备工作
// 具体的业务逻辑处理在按键抬起onKeyUp方法中进行。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
// 如果设置了文本变化监听器mOnTextViewChangeListener不为空表示外部类关注回车键按下事件
// 这里返回false让后续的按键抬起onKeyUp事件能继续处理相关逻辑例如添加新编辑框等操作。
if (mOnTextViewChangeListener!= null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
// 当按下删除键时,记录当前文本选择的起始位置,方便后续判断是否删除整个编辑框内容等情况。
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
// 调用父类的onKeyDown方法继续处理其他按键按下相关逻辑保证原生的按键按下行为也能正常执行
return super.onKeyDown(keyCode, event);
}
// 重写父类的onKeyUp方法用于处理按键抬起事件在这里针对回车键KEYCODE_ENTER和删除键KEYCODE_DEL抬起时
// 根据不同情况执行相应的业务逻辑例如删除编辑框、添加新编辑框等操作前提是设置了文本变化监听器mOnTextViewChangeListener不为空
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
if (mOnTextViewChangeListener!= null) {
// 判断如果文本选择的起始位置为0通常表示光标在文本开头且当前编辑框索引不为0说明不是第一个编辑框
// 则调用文本变化监听器的onEditTextDelete方法通知外部类删除当前编辑框然后返回true表示已经处理了该按键事件。
if (0 == mSelectionStartBeforeDelete && mIndex!= 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
// 如果没有设置文本变化监听器,则打印日志提示信息,表示监听器未设置,无法处理删除相关逻辑。
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
if (mOnTextViewChangeListener!= null) {
// 获取当前文本选择的起始位置,用于后续截取文本操作。
int selectionStart = getSelectionStart();
// 获取从文本选择起始位置到文本末尾的子字符串,可能是要添加到新编辑框的内容(如果有需要的话)。
String text = getText().subSequence(selectionStart, length()).toString();
// 将当前编辑框的文本内容截取为从开头到选择起始位置的部分,相当于清空了选择位置之后的文本(可能是要添加到新编辑框的部分)。
setText(getText().subSequence(0, selectionStart));
// 调用文本变化监听器的onEditTextEnter方法通知外部类在当前编辑框之后添加新编辑框并传入新编辑框索引和可能要添加的文本内容。
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
// 如果没有设置文本变化监听器,则打印日志提示信息,表示监听器未设置,无法处理回车键按下添加新编辑框相关逻辑。
Log.d(TAG, "OnTextViewChangeListener was not seted");
}
break;
default:
break;
}
// 调用父类的onKeyUp方法继续处理其他按键抬起相关逻辑保证原生的按键抬起行为也能正常执行
return super.onKeyUp(keyCode, event);
}
// 重写父类的onFocusChanged方法用于处理焦点变化事件在这里根据焦点状态是否获得焦点以及文本内容是否为空
// 通过文本变化监听器mOnTextViewChangeListener通知外部类文本有无情况以便外部类进行相应的UI元素显示隐藏等操作。
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (mOnTextViewChangeListener!= null) {
if (!focused && TextUtils.isEmpty(getText())) {
// 如果失去焦点且文本内容为空调用文本变化监听器的onTextChange方法通知外部类文本为空的情况参数hasText为false。
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
// 如果获得焦点或者文本内容不为空调用文本变化监听器的onTextChange方法通知外部类文本有内容的情况参数hasText为true。
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
// 重写父类的onCreateContextMenu方法用于创建上下文菜单长按文本等操作弹出的菜单
// 在这里主要是检查当前选中的文本中是否包含URL链接通过URLSpan判断如果包含且只有一个链接
// 则根据链接的协议头scheme查找对应的字符串资源ID添加一个菜单项用于执行链接相关操作如打开网页、拨打电话等
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
// 获取当前文本选择的起始位置
int selStart = getSelectionStart();
// 获取当前文本选择的结束位置
int selEnd = getSelectionEnd();
// 获取选择起始位置和结束位置中的较小值,用于后续范围判断等操作
int min = Math.min(selStart, selEnd);
// 获取选择起始位置和结束位置中的较大值,用于后续范围判断等操作
int max = Math.max(selStart, selEnd);
// 获取在选择文本范围内的所有URLSpan对象即包含的链接信息返回一个数组可能包含多个链接但这里后续只处理长度为1的情况
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
// 遍历存储协议头与字符串资源ID映射关系的集合检查链接的协议头是否匹配已定义的协议头如tel、http、mailto等
if(urls[0].getURL().indexOf(schema) >= 0) {
// 如果匹配获取对应的字符串资源ID用于设置菜单项显示的文本内容。
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
// 如果没有匹配到已知的协议头设置一个默认的字符串资源ID表示其他类型链接。
defaultResId = R.string.note_link_other;
}
// 添加一个菜单项到上下文菜单中菜单项的ID、顺序、分组等参数暂设为0可根据实际需求调整显示的文本内容根据获取到的字符串资源ID获取
// 并设置菜单项的点击监听器当点击该菜单项时通过URLSpan的onClick方法执行链接相关操作如打开网页、拨打电话等
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
@ -214,4 +279,4 @@ public class NoteEditText extends EditText {
}
super.onCreateContextMenu(menu);
}
}
}

@ -14,19 +14,23 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import android.content.Context;
import android.database.Cursor;
import android.text.TextUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
// NoteItemData类用于从数据库游标Cursor中提取笔记相关的数据信息并进行一些必要的处理和状态判断
// 方便在应用中以对象的形式使用和传递笔记的各项属性数据例如笔记的ID、创建日期、是否有附件等信息。
public class NoteItemData {
public class NoteItemData {
// 定义一个字符串数组,用于指定从数据库查询笔记相关信息时需要获取的列名,涵盖了笔记的多个属性字段,
// 后续可通过游标Cursor根据这些列名获取对应的数据来填充当前类的各个成员变量。
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
@ -42,91 +46,161 @@ public class NoteItemData {
NoteColumns.WIDGET_TYPE,
};
// 定义一个常量表示在查询结果游标Cursor中"ID"列所在的索引位置,方便后续通过游标获取对应列的数据。
private static final int ID_COLUMN = 0;
// 定义一个常量表示在查询结果游标Cursor中"ALERTED_DATE"列所在的索引位置,用于获取笔记的提醒日期数据。
private static final int ALERTED_DATE_COLUMN = 1;
// 定义一个常量表示在查询结果游标Cursor中"BG_COLOR_ID"列所在的索引位置用于获取笔记的背景颜色ID数据。
private static final int BG_COLOR_ID_COLUMN = 2;
// 定义一个常量表示在查询结果游标Cursor中"CREATED_DATE"列所在的索引位置,用于获取笔记的创建日期数据。
private static final int CREATED_DATE_COLUMN = 3;
// 定义一个常量表示在查询结果游标Cursor中"HAS_ATTACHMENT"列所在的索引位置,用于判断笔记是否有附件。
private static final int HAS_ATTACHMENT_COLUMN = 4;
// 定义一个常量表示在查询结果游标Cursor中"MODIFIED_DATE"列所在的索引位置,用于获取笔记的修改日期数据。
private static final int MODIFIED_DATE_COLUMN = 5;
// 定义一个常量表示在查询结果游标Cursor中"NOTES_COUNT"列所在的索引位置,可能用于获取与笔记相关的数量信息(具体含义取决于业务逻辑)。
private static final int NOTES_COUNT_COLUMN = 6;
// 定义一个常量表示在查询结果游标Cursor中"PARENT_ID"列所在的索引位置用于获取笔记所属父级的ID例如所属文件夹的ID等
private static final int PARENT_ID_COLUMN = 7;
// 定义一个常量表示在查询结果游标Cursor中"SNIPPET"列所在的索引位置,通常用于获取笔记的摘要、简短描述等文本内容。
private static final int SNIPPET_COLUMN = 8;
// 定义一个常量表示在查询结果游标Cursor中"TYPE"列所在的索引位置,用于获取笔记的类型信息(例如普通笔记、系统笔记等不同类型的区分)。
private static final int TYPE_COLUMN = 9;
// 定义一个常量表示在查询结果游标Cursor中"WIDGET_ID"列所在的索引位置可能与笔记在桌面小部件等相关功能中的标识ID有关。
private static final int WIDGET_ID_COLUMN = 10;
// 定义一个常量表示在查询结果游标Cursor中"WIDGET_TYPE"列所在的索引位置,可能用于区分不同类型的桌面小部件相关信息(如果有涉及的话)。
private static final int WIDGET_TYPE_COLUMN = 11;
// 用于存储笔记的ID从数据库游标中获取对应列的数据进行赋值代表该笔记在系统中的唯一标识。
private long mId;
// 用于存储笔记的提醒日期,从数据库游标获取对应数据,可用于判断笔记是否设置了提醒以及具体的提醒时间点。
private long mAlertDate;
// 用于存储笔记的背景颜色ID根据游标数据赋值可能用于在界面上显示笔记时设置对应的背景颜色。
private int mBgColorId;
// 用于存储笔记的创建日期,通过游标获取,记录笔记最初被创建的时间信息。
private long mCreatedDate;
// 用于标记笔记是否有附件根据游标中对应列的数据转换为布尔值进行存储大于0表示有附件赋值为true否则为false
private boolean mHasAttachment;
// 用于存储笔记的修改日期,从游标获取相应数据,可用于跟踪笔记最后一次被修改的时间。
private long mModifiedDate;
// 用于存储与笔记相关的数量信息(具体含义取决于业务逻辑,可能是关联的子笔记数量等情况),从游标获取对应整数值进行赋值。
private int mNotesCount;
// 用于存储笔记所属父级的ID比如所属文件夹的ID从游标获取相应长整数值方便判断笔记的归属关系等。
private long mParentId;
// 用于存储笔记的摘要、简短描述等文本内容,从游标获取字符串数据,并进行一些特定字符串替换操作后赋值,去除可能存在的特定标记字符。
private String mSnippet;
// 用于存储笔记的类型信息,从游标获取对应整数来表示不同类型(例如普通笔记、系统笔记等不同分类),便于后续根据类型进行不同的业务逻辑处理。
private int mType;
// 用于存储可能与笔记在桌面小部件等相关功能中的标识ID从游标获取对应整数值进行赋值具体用途取决于相关小部件功能实现
private int mWidgetId;
// 用于存储可能用于区分不同类型桌面小部件相关信息的类型值(如果有涉及桌面小部件相关业务逻辑的话),从游标获取对应整数进行赋值。
private int mWidgetType;
// 用于存储与笔记相关的联系人姓名信息,如果笔记属于通话记录文件夹等相关情况,会尝试获取对应的联系人姓名,初始化为空字符串。
private String mName;
// 用于存储与笔记相关的电话号码信息,如果笔记属于通话记录文件夹,会尝试获取对应的电话号码,初始化为空字符串。
private String mPhoneNumber;
// 用于标记当前笔记数据对应的笔记是否是列表中的最后一项,根据游标是否处于最后一条记录来判断赋值。
private boolean mIsLastItem;
// 用于标记当前笔记数据对应的笔记是否是列表中的第一项,根据游标是否处于第一条记录来判断赋值。
private boolean mIsFirstItem;
// 用于标记当前笔记数据对应的笔记是否是列表中唯一的一项通过判断游标获取的总记录数是否为1来赋值。
private boolean mIsOnlyOneItem;
// 用于标记当前笔记数据对应的笔记是否是某文件夹下仅跟随的一个笔记具体判断逻辑在后续方法中实现初始化为false。
private boolean mIsOneNoteFollowingFolder;
// 用于标记当前笔记数据对应的笔记是否是某文件夹下跟随的多个笔记之一具体判断逻辑在后续方法中实现初始化为false。
private boolean mIsMultiNotesFollowingFolder;
// 构造函数接收上下文Context和数据库游标Cursor作为参数用于从游标中提取笔记相关的各项数据信息并进行初始化操作
// 同时还会进行一些与笔记位置、关联情况相关的状态判断。
public NoteItemData(Context context, Cursor cursor) {
// 从游标中获取笔记的ID数据赋值给对应的成员变量mId。
mId = cursor.getLong(ID_COLUMN);
// 从游标中获取笔记的提醒日期数据赋值给mAlertDate成员变量。
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
// 从游标中获取笔记的背景颜色ID数据赋值给mBgColorId成员变量。
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
// 从游标中获取笔记的创建日期数据赋值给mCreatedDate成员变量。
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
// 根据游标中获取的表示是否有附件的数据整数值转换为布尔值后赋值给mHasAttachment成员变量大于0表示有附件赋值为true否则为false。
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0)? true : false;
// 从游标中获取笔记的修改日期数据赋值给mModifiedDate成员变量。
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
// 从游标中获取与笔记相关的数量信息具体含义取决于业务逻辑赋值给mNotesCount成员变量。
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
// 从游标中获取笔记所属父级的ID例如所属文件夹的ID等赋值给mParentId成员变量。
mParentId = cursor.getLong(PARENT_ID_COLUMN);
// 从游标中获取笔记的摘要、简短描述等文本内容赋值给mSnippet成员变量并进行特定字符串替换操作去除可能存在的特定标记字符如NoteEditActivity.TAG_CHECKED和NoteEditActivity.TAG_UNCHECKED
mSnippet = cursor.getString(SNIPPET_COLUMN);
mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace(
NoteEditActivity.TAG_UNCHECKED, "");
// 从游标中获取笔记的类型信息整数表示不同类型赋值给mType成员变量便于后续根据类型进行不同业务逻辑处理。
mType = cursor.getInt(TYPE_COLUMN);
mWidgetId = cursor.getInt(WIDGET_ID_COLUMN);
// 从游标中获取可能与笔记在桌面小部件等相关功能中的标识ID具体用途取决于相关小部件功能实现赋值给mWidgetId成员变量。
mWidgetId = cursor.getInt(WIDGET_TYPE_COLUMN);
// 从游标中获取可能用于区分不同类型桌面小部件相关信息的类型值如果有涉及桌面小部件相关业务逻辑的话赋值给mWidgetType成员变量。
mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN);
// 初始化电话号码成员变量为空字符串,后续根据笔记所属情况等可能会重新赋值。
mPhoneNumber = "";
// 如果笔记所属父级ID等于通话记录文件夹的特定IDNotes.ID_CALL_RECORD_FOLDER表示该笔记与通话记录相关尝试获取对应的电话号码。
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
// 通过DataUtils工具类的方法根据笔记ID从内容解析器context.getContentResolver()中获取对应的电话号码信息并赋值给mPhoneNumber成员变量。
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
// 如果获取到的电话号码不为空字符串,说明找到了对应的电话号码,尝试获取对应的联系人姓名。
if (!TextUtils.isEmpty(mPhoneNumber)) {
// 通过Contact类的静态方法根据上下文context和电话号码mPhoneNumber获取对应的联系人姓名赋值给mName成员变量。
mName = Contact.getContact(context, mPhoneNumber);
// 如果获取联系人姓名失败返回null则将电话号码本身作为联系人姓名赋值给mName成员变量。
if (mName == null) {
mName = mPhoneNumber;
}
}
}
// 如果最终联系人姓名仍为null可能之前获取过程都失败了则将其设置为空字符串保证成员变量有合理的初始值。
if (mName == null) {
mName = "";
}
// 调用checkPostion方法根据游标情况进行一些与笔记位置、在文件夹下关联情况等相关的状态判断更新对应的成员变量值。
checkPostion(cursor);
}
// 私有方法,用于根据游标情况进行一些与笔记位置、在文件夹下关联情况等相关的状态判断,更新对应的成员变量值,
// 例如判断是否是某文件夹下唯一跟随的笔记、是否是多个笔记跟随某文件夹等情况。
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
// 根据游标是否处于最后一条记录判断当前笔记是否是列表中的最后一项赋值给mIsLastItem成员变量。
mIsLastItem = cursor.isLast()? true : false;
// 根据游标是否处于第一条记录判断当前笔记是否是列表中的第一项赋值给mIsFirstItem成员变量。
mIsFirstItem = cursor.isFirst()? true : false;
// 通过判断游标获取的总记录数是否为1确定当前笔记是否是列表中唯一的一项赋值给mIsOnlyOneItem成员变量。
mIsOnlyOneItem = (cursor.getCount() == 1);
// 初始化是否是某文件夹下跟随的多个笔记的标记为false后续根据具体判断逻辑可能会更新。
mIsMultiNotesFollowingFolder = false;
// 初始化是否是某文件夹下仅跟随的一个笔记的标记为false后续根据具体判断逻辑可能会更新。
mIsOneNoteFollowingFolder = false;
if (mType == Notes.TYPE_NOTE && !mIsFirstItem) {
// 如果笔记类型是普通笔记Notes.TYPE_NOTE且不是列表中的第一项即前面还有其他记录进行以下判断逻辑。
if (mType == Notes.TYPE_NOTE &&!mIsFirstItem) {
// 获取当前游标所在的位置索引,用于后续判断操作。
int position = cursor.getPosition();
// 将游标移动到前一条记录,以便查看前一条记录对应的笔记类型等信息。
if (cursor.moveToPrevious()) {
// 判断前一条记录对应的笔记类型是否是文件夹类型Notes.TYPE_FOLDER或者系统类型Notes.TYPE_SYSTEM
// 如果是,则说明当前笔记可能是跟随在某个文件夹或系统记录之后的笔记,继续进行后续判断。
if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER
|| cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) {
// 判断游标获取的总记录数是否大于当前位置索引加1即当前笔记后面是否还有其他记录
// 如果是则说明当前笔记是某文件夹下跟随的多个笔记之一将对应标记变量设置为true。
if (cursor.getCount() > (position + 1)) {
mIsMultiNotesFollowingFolder = true;
} else {
// 如果后面没有其他记录了则说明当前笔记是某文件夹下仅跟随的一个笔记将对应标记变量设置为true。
mIsOneNoteFollowingFolder = true;
}
}
// 将游标再移回原来的位置(即当前笔记对应的记录位置),保证游标位置的正确性,避免影响后续其他操作对游标的使用。
if (!cursor.moveToNext()) {
throw new IllegalStateException("cursor move to previous but can't move back");
}
@ -134,90 +208,125 @@ public class NoteItemData {
}
}
// 判断当前笔记是否是某文件夹下仅跟随的一个笔记,返回对应的标记变量值,外部可调用此方法获取该状态信息。
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
// 判断当前笔记是否是某文件夹下跟随的多个笔记之一,返回对应的标记变量值,外部可调用此方法获取该状态信息。
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
// 判断当前笔记是否是列表中的最后一项,返回对应的标记变量值,外部可调用此方法获取该状态信息。
public boolean isLast() {
return mIsLastItem;
}
// 获取与笔记相关的联系人姓名信息(如果有获取到的话),返回对应的成员变量值,外部可调用此方法获取该姓名信息。
public String getCallName() {
return mName;
}
// 判断当前笔记是否是列表中的第一项,返回对应的标记变量值,外部可调用此方法获取该状态信息。
public boolean isFirst() {
return mIsFirstItem;
}
// 判断当前笔记是否是列表中唯一的一项,返回对应的标记变量值,外部可调用此方法获取该状态信息。
public boolean isSingle() {
return mIsOnlyOneItem;
}
// 获取笔记的ID返回对应的成员变量值外部可调用此方法获取笔记的唯一标识信息。
public long getId() {
return mId;
}
// 获取笔记的提醒日期,返回对应的成员变量值,外部可调用此方法获取笔记设置的提醒时间信息。
public long getAlertDate() {
return mAlertDate;
}
// 获取笔记的创建日期,返回对应的成员变量值,外部可调用此方法获取笔记最初被创建的时间信息。
public long getCreatedDate() {
return mCreatedDate;
}
// 判断笔记是否有附件返回对应的标记变量值mHasAttachment外部可调用此方法获取笔记是否包含附件的状态信息
// 如果返回true表示笔记存在附件否则表示没有附件。
public boolean hasAttachment() {
return mHasAttachment;
}
// 获取笔记的修改日期返回对应的成员变量值mModifiedDate外部可调用此方法获取笔记最后一次被修改的时间信息
// 常用于记录笔记的修改历史、版本管理等相关业务逻辑场景中。
public long getModifiedDate() {
return mModifiedDate;
}
// 获取笔记的背景颜色ID返回对应的成员变量值mBgColorId外部可调用此方法获取用于设置笔记显示背景颜色的标识信息
// 根据该ID可以在应用的颜色资源等相关配置中找到对应的具体颜色来应用到笔记展示界面上。
public int getBgColorId() {
return mBgColorId;
}
// 获取笔记所属父级的ID例如所属文件夹的ID等返回对应的成员变量值mParentId外部可调用此方法获取笔记的归属关系信息
// 便于判断笔记位于哪个文件夹下或者隶属于哪个上级模块等情况,方便进行分类管理、层级展示等操作。
public long getParentId() {
return mParentId;
}
// 获取与笔记相关的数量信息具体含义取决于业务逻辑可能是关联的子笔记数量等情况返回对应的成员变量值mNotesCount
// 外部可调用此方法获取该数量值,在涉及笔记的统计、分组等业务场景中可能会用到。
public int getNotesCount() {
return mNotesCount;
}
// 获取笔记所属文件夹的ID这里直接返回笔记的父级IDmParentId因为通常情况下父级就是所属的文件夹外部可调用此方法明确获取到笔记所属文件夹的标识
// 方便进行文件夹相关的操作,比如在文件夹内查找笔记、统计文件夹内笔记数量等操作。
public long getFolderId () {
return mParentId;
}
// 获取笔记的类型信息返回对应的成员变量值mType外部可调用此方法获取笔记所属的类型例如普通笔记、系统笔记等不同分类
// 根据不同类型可以在应用中进行差异化的展示、操作等业务逻辑处理,比如不同类型笔记有不同的编辑权限、显示样式等。
public int getType() {
return mType;
}
// 获取可能用于区分不同类型桌面小部件相关信息的类型值如果有涉及桌面小部件相关业务逻辑的话返回对应的成员变量值mWidgetType
// 外部可调用此方法获取该类型值,以便针对不同小部件类型进行相应的配置、显示控制等操作,例如不同类型小部件展示笔记的不同部分内容或者交互方式不同等。
public int getWidgetType() {
return mWidgetType;
}
// 获取可能与笔记在桌面小部件等相关功能中的标识ID具体用途取决于相关小部件功能实现返回对应的成员变量值mWidgetId
// 外部可调用此方法获取该标识ID用于在小部件相关功能中准确识别和操作对应的笔记比如在小部件中更新特定笔记的显示内容等情况。
public int getWidgetId() {
return mWidgetId;
}
// 获取笔记的摘要、简短描述等文本内容返回对应的成员变量值mSnippet外部可调用此方法获取笔记的简要信息
// 常用于在列表展示等场景中先显示笔记的简短描述,用户点击后再查看详细内容,起到一个预览的作用。
public String getSnippet() {
return mSnippet;
}
// 判断笔记是否设置了提醒通过检查提醒日期mAlertDate是否大于0来确定如果大于0则表示设置了提醒返回true否则返回false
// 外部可调用此方法快速知晓笔记是否有提醒相关的设置,以便在应用中进行相应的提醒业务逻辑处理,比如定时提醒用户查看笔记等操作。
public boolean hasAlert() {
return (mAlertDate > 0);
}
// 判断笔记是否属于通话记录类型通过检查笔记所属父级ID是否等于通话记录文件夹的特定IDNotes.ID_CALL_RECORD_FOLDER并且电话号码mPhoneNumber不为空字符串来确定
// 如果满足条件则返回true表示是通话记录相关笔记否则返回false外部可调用此方法区分通话记录笔记和其他类型笔记进行针对性的业务操作例如通话记录笔记可能有特殊的展示格式或关联操作等。
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
return (mParentId == Notes.ID_CALL_RECORD_FOLDER &&!TextUtils.isEmpty(mPhoneNumber));
}
// 静态方法接收数据库游标Cursor作为参数从游标中获取笔记的类型信息通过TYPE_COLUMN指定的列索引获取整数值并返回
// 外部可直接通过类名调用此方法方便在不需要创建NoteItemData类实例的情况下仅根据游标快速获取笔记的类型信息常用于一些仅需判断类型的简单业务场景中。
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}

@ -14,30 +14,40 @@
* limitations under the License.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
// NotesListItem类继承自LinearLayout代表了笔记列表中每一项的视图布局用于展示笔记相关的各项信息
// 如标题、时间、是否有提醒等,并根据笔记的不同类型、状态等来设置对应的显示样式和背景等。
public class NotesListItem extends LinearLayout {
public class NotesListItem extends LinearLayout {
// 用于显示提醒相关图标的ImageView控件例如有提醒时显示闹钟图标等方便用户直观看到笔记是否设置了提醒功能。
private ImageView mAlert;
// 用于显示笔记标题的TextView控件会根据笔记的类型、具体内容等情况设置相应的文本内容进行展示。
private TextView mTitle;
// 用于显示笔记时间相关信息的TextView控件通常会展示笔记的修改时间等并以相对时间的格式如“几分钟前”“昨天”等进行显示。
private TextView mTime;
// 用于显示与笔记相关的联系人姓名如果笔记属于通话记录等相关情况的TextView控件若不涉及则可能隐藏该控件。
private TextView mCallName;
// 用于存储当前列表项对应的笔记数据对象NoteItemData类型包含了笔记的各种详细信息方便在视图设置等操作中获取对应的数据进行展示和判断。
private NoteItemData mItemData;
// 用于在选择模式下例如多选操作时显示选择状态的CheckBox控件根据是否处于选择模式以及笔记类型等情况决定其可见性和选中状态。
private CheckBox mCheckBox;
// 构造函数接收上下文Context作为参数调用父类LinearLayout的构造函数进行初始化
// 并通过inflate方法加载对应的布局文件R.layout.note_item到当前视图中然后获取布局中各个相关的子控件实例。
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
@ -48,75 +58,126 @@ public class NotesListItem extends LinearLayout {
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
// 用于将笔记相关的数据绑定到当前列表项视图上,根据笔记数据的不同情况(如类型、是否有提醒等)以及传入的一些显示控制参数(如是否处于选择模式、是否已选中),
// 来设置各个子控件的显示内容、可见性以及背景等样式,完成数据与视图的关联展示。
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
// 如果处于选择模式choiceMode为true并且笔记类型是普通笔记Notes.TYPE_NOTE则显示CheckBox控件
// 并根据传入的是否已选中参数checked设置其选中状态用于在多选等操作场景下展示选择情况。
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
} else {
// 如果不满足上述条件非选择模式或者不是普通笔记类型则隐藏CheckBox控件不展示选择相关操作界面。
mCheckBox.setVisibility(View.GONE);
}
// 将传入的笔记数据对象赋值给成员变量mItemData方便后续在其他方法中获取该笔记的各项详细信息。
mItemData = data;
// 如果笔记的ID等于通话记录文件夹的特定IDNotes.ID_CALL_RECORD_FOLDER说明当前列表项对应的是通话记录文件夹进行以下设置
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
// 隐藏显示联系人姓名的TextView控件mCallName因为通话记录文件夹本身不需要显示联系人姓名。
mCallName.setVisibility(View.GONE);
// 显示提醒图标相关的ImageView控件mAlert可能用于表示该文件夹有特殊含义或者相关提醒功能具体取决于业务逻辑
mAlert.setVisibility(View.VISIBLE);
// 设置标题mTitle的文本外观样式为主要项的样式通过R.style.TextAppearancePrimaryItem资源样式设置使其在界面上以相应的样式展示。
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
// 设置标题的文本内容,由通话记录文件夹的名称(通过字符串资源获取)和该文件夹内文件数量(通过格式化字符串资源结合笔记数据中的文件数量信息获取)组成,展示给用户文件夹相关信息。
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
// 设置提醒图标mAlert显示的图片资源为通话记录相关的特定图标R.drawable.call_record以特定图标表示该文件夹的性质。
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
}
// 如果笔记所属父级的ID等于通话记录文件夹的特定IDNotes.ID_CALL_RECORD_FOLDER说明当前笔记属于通话记录文件夹下的具体笔记进行如下设置
else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
// 显示联系人姓名的TextView控件mCallName并设置其文本内容为笔记相关的联系人姓名通过笔记数据中的联系人姓名信息获取展示给用户对应的联系人信息。
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
// 设置标题mTitle的文本外观样式为次要项的样式通过R.style.TextAppearanceSecondaryItem资源样式设置使其与文件夹等其他项在样式上有所区分展示。
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
// 设置标题的文本内容为经过格式化后的笔记摘要信息通过DataUtils工具类的方法对笔记数据中的摘要内容进行格式化处理获取展示笔记的简要内容。
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
// 如果笔记设置了提醒通过笔记数据中的提醒日期判断hasAlert方法返回true则进行以下提醒图标相关设置
if (data.hasAlert()) {
// 设置提醒图标mAlert显示的图片资源为闹钟图标R.drawable.clock直观提示用户该笔记设置了提醒功能。
mAlert.setImageResource(R.drawable.clock);
// 显示提醒图标相关的ImageView控件mAlert使其在界面上可见。
mAlert.setVisibility(View.VISIBLE);
} else {
// 如果笔记没有设置提醒则隐藏提醒图标相关的ImageView控件mAlert不在界面上展示该图标。
mAlert.setVisibility(View.GONE);
}
} else {
}
// 如果笔记不属于上述通话记录文件夹相关的情况,则进行以下通用设置:
else {
// 隐藏联系人姓名的TextView控件mCallName因为该笔记与通话记录无关不需要展示联系人姓名。
mCallName.setVisibility(View.GONE);
// 设置标题mTitle的文本外观样式为主要项的样式通过R.style.TextAppearancePrimaryItem资源样式设置以相应的样式展示标题内容。
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
// 如果笔记类型是文件夹类型Notes.TYPE_FOLDER进行以下文件夹相关的标题设置
if (data.getType() == Notes.TYPE_FOLDER) {
// 设置标题的文本内容为笔记的摘要信息(通过笔记数据中的摘要内容获取)加上该文件夹内文件数量(通过格式化字符串资源结合笔记数据中的文件数量信息获取),展示文件夹及其包含文件数量的相关信息给用户。
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
// 隐藏提醒图标相关的ImageView控件mAlert文件夹通常不需要展示提醒图标除非有特殊业务逻辑要求
mAlert.setVisibility(View.GONE);
} else {
// 如果笔记不是文件夹类型即普通笔记等其他类型设置标题的文本内容为经过格式化后的笔记摘要信息通过DataUtils工具类的方法对笔记数据中的摘要内容进行格式化处理获取展示笔记的简要内容。
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
// 如果笔记设置了提醒通过笔记数据中的提醒日期判断hasAlert方法返回true则进行以下提醒图标相关设置
if (data.hasAlert()) {
// 设置提醒图标mAlert显示的图片资源为闹钟图标R.drawable.clock直观提示用户该笔记设置了提醒功能。
mAlert.setImageResource(R.drawable.clock);
// 显示提醒图标相关的ImageView控件mAlert使其在界面上可见。
mAlert.setVisibility(View.VISIBLE);
} else {
// 如果笔记没有设置提醒则隐藏提醒图标相关的ImageView控件mAlert不在界面上展示该图标。
mAlert.setVisibility(View.GONE);
}
}
}
// 设置显示时间的TextView控件mTime的文本内容通过DateUtils工具类的方法将笔记的修改日期通过笔记数据中的修改日期信息获取转换为相对时间格式的字符串如“几分钟前”“昨天”等进行展示方便用户直观了解笔记的修改时间情况。
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
// 调用setBackground方法根据笔记数据中的背景颜色ID以及笔记的类型、位置等状态信息设置当前列表项的背景样式使其在列表中以合适的背景展示。
setBackground(data);
}
// 私有方法根据笔记数据NoteItemData类型中的背景颜色ID以及笔记的类型、位置等状态信息
// 通过NoteItemBgResources工具类获取对应的背景资源并设置为当前列表项的背景实现不同情况下的差异化背景展示。
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
// 如果笔记类型是普通笔记Notes.TYPE_NOTE根据笔记在列表中的不同位置、数量等情况设置不同的背景资源
if (data.getType() == Notes.TYPE_NOTE) {
// 如果笔记是列表中唯一的一项或者是某文件夹下仅跟随的一个笔记(通过笔记数据中的相关判断方法确定),
// 通过NoteItemBgResources工具类获取对应的单个笔记背景资源根据背景颜色ID获取具体资源ID并设置为当前列表项的背景。
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
}
// 如果笔记是列表中的最后一项通过笔记数据中的相关判断方法确定通过NoteItemBgResources工具类获取对应的最后一个笔记背景资源根据背景颜色ID获取具体资源ID并设置为当前列表项的背景。
else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
}
// 如果笔记是列表中的第一项或者是某文件夹下跟随的多个笔记之一(通过笔记数据中的相关判断方法确定),
// 通过NoteItemBgResources工具类获取对应的第一个笔记或多个笔记中某个的背景资源根据背景颜色ID获取具体资源ID并设置为当前列表项的背景。
else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
// 如果笔记不属于上述特殊位置等情况则通过NoteItemBgResources工具类获取对应的普通笔记背景资源根据背景颜色ID获取具体资源ID并设置为当前列表项的背景。
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
// 如果笔记不是普通笔记类型例如是文件夹类型等通过NoteItemBgResources工具类获取对应的文件夹背景资源并设置为当前列表项的背景以区别于普通笔记的背景展示。
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
// 对外提供获取当前列表项对应的笔记数据对象的方法,外部可调用此方法获取笔记的详细信息,例如在点击列表项等操作后,
// 通过获取笔记数据进一步进行查看详情、编辑等相关业务逻辑处理。
public NoteItemData getItemData() {
return mItemData;
}
}
}

@ -14,42 +14,61 @@
* limitations under the License.
*/
package net.micode.notes.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import android.widget.RemoteViews;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
public abstract class NoteWidgetProvider extends AppWidgetProvider {
package net.micode.notes.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import android.widget.RemoteViews;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
// NoteWidgetProvider类是一个抽象类继承自AppWidgetProvider用于为桌面小部件提供通用的基础功能和抽象方法定义
// 子类可以继承它并实现特定的抽象方法来定制不同类型桌面小部件的具体行为,例如设置背景资源、布局以及小部件类型等,同时它也处理了一些小部件相关的通用逻辑,如数据更新、删除等操作。
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义一个字符串数组用于指定从数据库查询小部件相关笔记信息时需要获取的列名包括笔记的ID、背景颜色ID以及摘要信息等
// 后续通过数据库游标Cursor可根据这些列名获取对应的数据来进行小部件的内容展示等操作。
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义一个常量表示在查询结果游标Cursor中"ID"列所在的索引位置,方便后续通过游标获取对应列的数据。
public static final int COLUMN_ID = 0;
// 定义一个常量表示在查询结果游标Cursor中"BG_COLOR_ID"列所在的索引位置用于获取笔记的背景颜色ID数据来设置小部件的背景样式。
public static final int COLUMN_BG_COLOR_ID = 1;
// 定义一个常量表示在查询结果游标Cursor中"SNIPPET"列所在的索引位置,用于获取笔记的摘要信息,可展示在小部件上作为简要内容。
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider";
// 重写父类AppWidgetProvider的onDeleted方法该方法在桌面小部件被删除时会被调用
// 这里主要用于处理与小部件相关的数据清理工作例如将数据库中与被删除小部件关联的记录进行更新清除对应的小部件ID关联等信息。
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// 创建一个ContentValues对象用于存储要更新到数据库中的键值对数据这里主要是要更新笔记数据表中与小部件ID相关的字段。
ContentValues values = new ContentValues();
// 将NoteColumns.WIDGET_ID字段的值设置为无效的小部件IDAppWidgetManager.INVALID_APPWIDGET_ID表示该笔记不再与任何小部件关联。
values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
// 遍历传入的被删除小部件的ID数组对每个小部件ID执行以下数据库更新操作。
for (int i = 0; i < appWidgetIds.length; i++) {
// 使用上下文的内容解析器context.getContentResolver())来更新数据库中的数据,
// 更新的表是Notes.CONTENT_NOTE_URI所指向的表通常是存储笔记相关信息的表
// 使用values中设置的数据进行更新更新的条件是NoteColumns.WIDGET_ID字段等于当前遍历到的小部件ID
// 并且传入一个字符串数组作为条件参数的值将小部件ID转换为字符串形式用于条件匹配。
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
@ -57,6 +76,8 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
// 私有方法用于根据给定的小部件IDwidgetId从数据库中查询获取与该小部件相关的笔记信息返回一个数据库游标Cursor对象
// 通过设置查询条件筛选出特定小部件关联且不属于回收站文件夹Notes.ID_TRASH_FOLER的笔记信息方便后续获取具体的数据进行小部件内容展示等操作。
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
@ -65,68 +86,107 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
null);
}
// 对外公开的update方法用于触发小部件的更新操作它实际上是调用了另一个重载的私有update方法并传入默认的隐私模式参数false
// 方便外部类(如子类或者其他调用者)简单地调用该方法来启动小部件更新流程,而无需关心隐私模式相关的细节(可在重载方法中处理)。
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
// 私有重载的update方法是真正执行小部件更新具体逻辑的地方它会遍历传入的小部件ID数组对每个有效的小部件ID进行一系列操作
// 包括从数据库获取相关笔记信息、设置小部件的视图内容如文本、背景图片等、创建点击小部件时的意图PendingIntent最终完成小部件的更新展示。
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
// 遍历传入的小部件ID数组对每个小部件ID进行更新操作。
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
// 检查当前小部件ID是否是有效的小部件ID不等于AppWidgetManager.INVALID_APPWIDGET_ID如果有效则进行后续更新操作。
if (appWidgetIds[i]!= AppWidgetManager.INVALID_APPWIDGET_ID) {
// 获取默认的背景颜色ID通过ResourceParser工具类的方法获取用于在没有获取到具体笔记的背景颜色ID时作为默认背景设置初始设置小部件的背景样式。
int bgId = ResourceParser.getDefaultBgId(context);
// 初始化用于存储笔记摘要信息的字符串为空,后续会根据从数据库获取的情况进行更新,该摘要信息会展示在小部件上作为简要内容。
String snippet = "";
// 创建一个意图Intent对象用于指定点击小部件时要启动的活动Activity这里初始设置为启动NoteEditActivity后续会根据情况调整。
Intent intent = new Intent(context, NoteEditActivity.class);
// 设置意图的标志位,使得如果对应的活动已经在栈顶(处于运行状态),则不会重新创建新的实例,而是复用已有的活动实例,优化性能并保证界面状态的连贯性。
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
// 将当前小部件的ID作为额外的数据添加到意图中方便在目标活动NoteEditActivity中获取并知道是哪个小部件触发的操作用于后续相关逻辑处理。
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
// 将当前小部件的类型通过抽象方法getWidgetType获取由具体子类实现返回不同类型值作为额外的数据添加到意图中同样方便在目标活动中区分不同类型小部件的相关操作。
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
// 调用getNoteWidgetInfo方法根据当前小部件ID从数据库中获取与该小部件相关的笔记信息返回一个游标对象用于后续获取具体数据。
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
if (c!= null && c.moveToFirst()) {
// 如果游标不为空且能够移动到第一条记录(表示查询到了相关数据),进行以下操作:
if (c.getCount() > 1) {
// 如果查询到的记录数大于1说明存在多个笔记与同一个小部件ID关联这可能是不符合预期的情况记录错误日志并关闭游标直接返回不进行后续更新操作。
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
// 获取游标中对应列COLUMN_SNIPPET的笔记摘要信息并赋值给snippet变量用于后续展示在小部件上。
snippet = c.getString(COLUMN_SNIPPET);
// 获取游标中对应列COLUMN_BG_COLOR_ID的笔记背景颜色ID并赋值给bgId变量用于设置小部件的背景样式覆盖之前的默认背景颜色ID。
bgId = c.getInt(COLUMN_BG_COLOR_ID);
// 将游标中对应列COLUMN_ID的笔记ID作为额外的数据添加到意图中方便在目标活动中明确具体是哪个笔记相关的操作例如编辑该笔记等。
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
// 设置意图的动作Action为Intent.ACTION_VIEW通常表示查看相关内容的操作意味着点击小部件后可能是查看对应的笔记详情等行为具体取决于目标活动的实现
intent.setAction(Intent.ACTION_VIEW);
} else {
// 如果游标为空或者无法移动到第一条记录(表示没有查询到与该小部件相关的有效笔记信息),进行以下操作:
snippet = context.getResources().getString(R.string.widget_havenot_content);
// 设置意图的动作Action为Intent.ACTION_INSERT_OR_EDIT意味着点击小部件后可能是进行插入新笔记或者编辑相关内容的操作同样具体取决于目标活动的实现因为没有关联的已有笔记所以提供创建或编辑的入口。
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
}
if (c != null) {
// 如果游标对象不为空,关闭游标,释放相关资源,避免内存泄漏等问题,因为已经获取完需要的数据了。
if (c!= null) {
c.close();
}
// 创建一个RemoteViews对象用于设置小部件的远程视图内容通过传入应用的包名和小部件的布局资源ID由抽象方法getLayoutId获取具体子类实现返回不同布局资源来初始化
// 后续可通过该对象设置小部件上各个子视图(如文本视图、图片视图等)的显示内容、属性等。
RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId());
// 设置小部件上背景图片视图通过R.id.widget_bg_image指定的图片资源调用抽象方法getBgResourceId并传入获取到的背景颜色IDbgId来获取对应的背景图片资源进行背景设置。
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
// 将背景颜色ID作为额外的数据添加到意图中方便在目标活动中获取并可能用于保持界面显示等相关一致性操作例如设置相同的背景颜色等
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* Generate the pending intent to start host for the widget
*/
// 定义一个PendingIntent对象用于包装意图intent使其可以在合适的时候被触发执行例如点击小部件时初始化为null后续根据隐私模式情况进行创建。
PendingIntent pendingIntent = null;
if (privacyMode) {
// 如果处于隐私模式privacyMode为true进行以下操作
rv.setTextViewText(R.id.widget_text,
context.getString(R.string.widget_under_visit_mode));
// 创建一个PendingIntent用于启动NotesListActivity当点击小部件时会跳转到该活动
// 并设置标志位为PendingIntent.FLAG_UPDATE_CURRENT保证如果已经存在相同的PendingIntent会更新其携带的意图数据保持最新状态。
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(
context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
} else {
// 如果不处于隐私模式privacyMode为false进行以下操作
rv.setTextViewText(R.id.widget_text, snippet);
// 创建一个PendingIntent包装之前创建的intent根据情况设置了不同动作、携带不同数据等用于执行与笔记相关的操作如查看、编辑等
// 同样设置标志位为PendingIntent.FLAG_UPDATE_CURRENT保证意图数据的更新使得点击小部件时能正确执行相应操作。
pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
// 设置小部件上文本视图通过R.id.widget_text指定的点击事件PendingIntent使得点击该文本区域时会触发对应的操作如跳转到相关活动等完成小部件交互逻辑的设置。
rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent);
// 通过AppWidgetManager对象使用小部件的ID更新小部件的远程视图内容将之前设置好的RemoteViews对象应用到小部件上完成小部件的更新展示。
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
}
}
// 抽象方法用于获取给定背景颜色IDbgId对应的小部件背景资源ID由具体的子类实现根据不同类型小部件或者不同的业务逻辑返回相应的背景资源ID
// 以便在小部件更新时设置合适的背景样式。
protected abstract int getBgResourceId(int bgId);
// 抽象方法用于获取小部件对应的布局资源ID由具体的子类实现不同类型的小部件如不同尺寸、不同样式等可以返回各自对应的布局资源
// 用于在小部件更新时创建RemoteViews对象来设置小部件的整体布局和显示内容。
protected abstract int getLayoutId();
// 抽象方法,用于获取小部件的类型标识,由具体的子类实现返回不同的类型值(例如不同尺寸类型的小部件返回不同的类型常量等),
// 方便在应用中对不同类型小部件进行区分和针对性的管理、操作以及相关业务逻辑处理,比如根据类型设置不同的显示规则、数据加载方式等。
protected abstract int getWidgetType();
}
}

@ -14,34 +14,46 @@
* limitations under the License.
*/
package net.micode.notes.widget;
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
// NoteWidgetProvider_2x类继承自NoteWidgetProvider是用于实现特定尺寸这里可能是2倍尺寸相关从类名推测桌面小部件的提供类
// 它重写了父类的一些方法来定制该尺寸小部件的相关属性和更新逻辑等内容。
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
// 重写父类的onUpdate方法该方法会在桌面小部件需要更新时被调用例如定时更新或者有相关数据变化触发更新的情况
// 在这里直接调用了父类的update方法将更新的具体操作委托给父类来处理自身没有添加额外的更新逻辑当然也可以根据需要添加特定于2倍尺寸小部件的更新操作
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类的getLayoutId方法用于获取该尺寸桌面小部件对应的布局资源ID。
// 返回的是R.layout.widget_2x意味着这个2倍尺寸的小部件会使用名为"widget_2x"的布局文件来进行界面展示,
// 该布局文件中定义了小部件在界面上显示的各种控件及其布局方式等内容。
@Override
protected int getLayoutId() {
return R.layout.widget_2x;
}
// 重写父类的getBgResourceId方法该方法的作用是根据传入的背景资源IDbgId获取适用于该尺寸桌面小部件的背景资源ID。
// 通过调用ResourceParser.WidgetBgResources工具类中的getWidget2xBgResource方法传入bgId来获取对应2倍尺寸小部件的背景资源
// 这样可以针对不同的背景设置需求,为该尺寸小部件准确配置合适的背景样式。
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
// 重写父类的getWidgetType方法用于明确该桌面小部件的类型。
// 返回的是Notes.TYPE_WIDGET_2X以此标识该小部件属于特定的2倍尺寸类型方便在应用中对不同类型的小部件进行区分和针对性的管理、操作等处理。
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X;
}
}
}

@ -14,33 +14,48 @@
* limitations under the License.
*/
package net.micode.notes.widget;
package net.micode.notes.widget;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
// NoteWidgetProvider_4x类继承自NoteWidgetProvider它主要用于处理特定的4倍尺寸桌面小部件相关的逻辑
// 通过重写父类的部分方法来定制该4倍尺寸小部件在布局、背景资源以及类型标识等方面的特性。
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
// 重写父类的onUpdate方法此方法会在桌面小部件需要更新时被调用例如系统定时触发小部件更新、相关数据变化导致小部件内容需刷新等情况。
// 在这里它直接调用了父类的update方法将具体的更新操作交给父类去执行自身暂时没有添加额外的、针对4倍尺寸小部件特有的更新逻辑
// 不过后续可以根据具体需求在此方法中添加相应内容比如根据4倍尺寸特点来更新小部件显示的内容等操作。
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
// 重写父类的getLayoutId方法其目的是获取该4倍尺寸桌面小部件所对应的布局资源ID。
// 这里返回R.layout.widget_4x表示这个4倍尺寸的小部件将会使用名为“widget_4x”的布局文件来进行界面展示
// 该布局文件中定义了适合4倍尺寸小部件的各种控件摆放位置、大小以及样式等布局相关内容以此来呈现出符合该尺寸特点的视觉效果。
protected int getLayoutId() {
return R.layout.widget_4x;
}
// 重写父类的getBgResourceId方法该方法负责根据传入的背景资源IDbgId来获取适用于这个4倍尺寸桌面小部件的背景资源ID。
// 通过调用ResourceParser.WidgetBgResources工具类中的getWidget4xBgResource方法并传入bgId参数就能得到对应4倍尺寸小部件的特定背景资源
// 这样可以依据不同的背景设置需求,为该尺寸小部件准确地配置合适的背景样式,使其在外观上更符合整体设计要求。
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
// 重写父类的getWidgetType方法用于明确标识该桌面小部件的类型为4倍尺寸类型。
// 返回的是Notes.TYPE_WIDGET_4X借助这个返回值在整个应用中就能方便地区分不同尺寸类型的小部件进而对其进行有针对性的管理、操作以及相关业务逻辑处理
// 比如针对不同类型小部件采用不同的数据加载方式、显示规则等。
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X;
}
}
}
Loading…
Cancel
Save