添加了时间显示,以及提醒功能。
前言
接着上篇文章的进度,继续完善项目,添加了时间显示,以及提醒功能。
这是适合初学者学习的安卓APP项目,对你有帮助的话,麻烦点赞、收藏、加评论,谢谢大家。
一、添加创建时间和结束时间
众所周知,对应每个要完成的任务,我们特别关注其截止时间,因此我们需要让每个任务显示时间,来帮助我们更好把握任务的完成。
实现效果如图:
(1.1) 添加组件
我们先在item_task.xml上加上显示创建、结束时间以及设置结束时间按钮的组件
item_task.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:id="@+id/tvTask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tvStartTime"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="@color/black"
android:textSize="12sp" />
<TextView
android:id="@+id/tvEndTime"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:textColor="@color/black"
android:textSize="12sp" />
<Button
android:id="@+id/btnSetEndTime"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="设置结束时间"
android:textColor="@color/white"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
(1.2)在添加任务模型类里添加元素与方法
然后,我们需要在任务模型类Task.java里,加上创建时间、结束时间这两个变量,以及它们对应的get和set方法,其中创建时间startTime为直接获取系统当前时间,而结束时间endTime需要手动设置。
Task.java:
package com.example.todolist.modle;
import java.util.Date;
public class Task {
private String content; //任务内容
private boolean isCompleted; //是否完成
private Date endTime; //结束时间
private Date startTime; //创建时间
public Task(String content) { //构造函数
this.content = content;
this.isCompleted = false;
this.startTime = new Date();
}
public void setContent(String content) { //设置content
this.content = content;
}
public String getContent() { //获取content
return content;
}
public boolean isCompleted() { //获取isCompleted
return isCompleted;
}
public void setCompleted(boolean isCompleted) { //设置isCompleted
this.isCompleted = isCompleted;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getStartTime() {
return startTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Date getEndTime() {
return endTime;
}
}
(1.3)实现设置结束时间按钮
为了实现手动设置结束时间,我们的思路是,当用户点击了“设置结束时间”后,应该跳出一个时间选择器,在用户选择完时间后,根据选择的时间显示结束时间。
所以,我们需要修改适配器TaskAdapter.java,第一步,我们先在单个任务项的视图组件TaskViewHolder方法中将新增元素添加进去,并将其与item_task中组件绑定
public static class TaskViewHolder extends RecyclerView.ViewHolder {
TextView tvTask; //显示任务内容
TextView tvStartTime; //创建时间
TextView tvEndTime; //结束时间
Button btnSetEndTime; //设置结束时间按钮
/**
* 构造函数,初始化视图组件
*/
public TaskViewHolder(@NonNull View itemView) {
super(itemView);
tvTask = itemView.findViewById(R.id.tvTask); //获取TextView组件
tvStartTime = itemView.findViewById(R.id.tvStartTime);
tvEndTime = itemView.findViewById(R.id.tvEndTime);
btnSetEndTime = itemView.findViewById(R.id.btnSetEndTime);
}
}
第二步,我们在onBindViewHolder方法中,将创建、结束时间的数据绑定到视图上,并将点击“设置结束时间”的触发方法写入
//时间的格式
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//显示任务的创建时间
if (task.getStartTime() != null) {
//格式化日期并显示
String satrtString = simpleDateFormat.format(task.getStartTime());
holder.tvStartTime.setText("创建时间:" + satrtString);
}
//显示任务结束时间
if (task.getEndTime() != null) {
//格式化日期并显示
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
} else {
//如果结束时间未设置,显示默认提示
holder.tvEndTime.setText("结束时间:未设置");
}
//设置结束时间的按钮点击事件
holder.btnSetEndTime.setOnClickListener(v -> {
//获取当前时间
Calendar calendar = Calendar.getInstance();
//创建日期选择器,用于选择年、月、日
DatePickerDialog datePickerDialog = new DatePickerDialog(context, (view, year, month, dayOfMonth) -> {
//创建时间选择器,用于选择小时和分钟
TimePickerDialog timePickerDialog = new TimePickerDialog(context, (view1, hourOfDay, minute) -> {
// 设置选择的时间到 Calendar 对象
calendar.set(year, month, dayOfMonth, hourOfDay, minute);
//更新任务结束时间
task.setEndTime(calendar.getTime());
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
//刷新数据并保存
notifyDataSetChanged();
saveTasks();
},
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
true
);
timePickerDialog.show(); //显示时间选择器
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
datePickerDialog.show(); //显示日期选择器
});
整个文件如下
TaskAdapter.java:
package com.example.todolist.adapter;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.todolist.R;
import com.example.todolist.modle.Task;
import com.google.gson.Gson;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
/**
* RecyclerView适配器,用于管理任务列表的显示和交互
*/
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder> {
private List<Task> tasks; //任务列表
private Context context; //上下文,用于获取资源和操作SharedPreferences
/**
* 构造函数,初始化任务列表和上下文
*
* @param tasks 任务列表
* @param context 上下文
*/
public TaskAdapter(List<Task> tasks, Context context) {
this.tasks = tasks;
this.context = context;
}
/**
* 创建并返回一个新的ViewHolder,负责显示单个任务项的视图
*/
@NonNull
@Override
public TaskAdapter.TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//加载item_task布局
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
return new TaskViewHolder(view);
}
/**
* 将任务数据绑定到对应的ViewHolder
*/
@Override
public void onBindViewHolder(@NonNull TaskAdapter.TaskViewHolder holder, int position) {
Task task = tasks.get(position); //获取指定位置任务
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //时间的格式
holder.tvTask.setText(task.getContent()); //设置任务内容
//根据任务是否完成,设置文本删除线
holder.tvTask.setPaintFlags(task.isCompleted() ? Paint.STRIKE_THRU_TEXT_FLAG : 0);
//显示任务的创建时间
if (task.getStartTime() != null) {
//格式化日期并显示
String satrtString = simpleDateFormat.format(task.getStartTime());
holder.tvStartTime.setText("创建时间:" + satrtString);
}
//显示任务结束时间
if (task.getEndTime() != null) {
//格式化日期并显示
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
} else {
//如果结束时间未设置,显示默认提示
holder.tvEndTime.setText("结束时间:未设置");
}
//设置结束时间的按钮点击事件
holder.btnSetEndTime.setOnClickListener(v -> {
//获取当前时间
Calendar calendar = Calendar.getInstance();
//创建日期选择器,用于选择年、月、日
DatePickerDialog datePickerDialog = new DatePickerDialog(context, (view, year, month, dayOfMonth) -> {
//创建时间选择器,用于选择小时和分钟
TimePickerDialog timePickerDialog = new TimePickerDialog(context, (view1, hourOfDay, minute) -> {
// 设置选择的时间到 Calendar 对象
calendar.set(year, month, dayOfMonth, hourOfDay, minute);
//更新任务结束时间
task.setEndTime(calendar.getTime());
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
//刷新数据并保存
notifyDataSetChanged();
saveTasks();
},
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
true
);
timePickerDialog.show(); //显示时间选择器
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
);
datePickerDialog.show(); //显示日期选择器
});
//设置点击监听器,单击任务时,切换其完成状态
holder.itemView.setOnClickListener(v -> {
task.setCompleted(!task.isCompleted());
holder.tvTask.setPaintFlags(task.isCompleted() ? Paint.STRIKE_THRU_TEXT_FLAG : 0);
});
//设置长按监听器,长按任务时,显示删除确认对话框
holder.itemView.setOnLongClickListener(v -> {
AlertDialog alertDialog;
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle("是否删除") //设置对话框标题
.setMessage("是否删除该任务") //设置对话框内容
//“确定”按钮
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
tasks.remove(position); //从任务列表移除任务
notifyDataSetChanged(); //通知数据变化,更新RecyclerView
saveTasks(); //调用保存函数
dialog.dismiss(); //关闭对话框
}
})
//“取消”按钮,setNegativeButton点击后自动关闭对话框
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
alertDialog = builder.create();
alertDialog.show();
return true;
});
}
/**
* 单个任务项的视图组件ViewHolder
*/
public static class TaskViewHolder extends RecyclerView.ViewHolder {
TextView tvTask; //显示任务内容
TextView tvStartTime; //创建时间
TextView tvEndTime; //结束时间
Button btnSetEndTime; //设置结束时间按钮
/**
* 构造函数,初始化视图组件
*/
public TaskViewHolder(@NonNull View itemView) {
super(itemView);
tvTask = itemView.findViewById(R.id.tvTask); //获取TextView组件
tvStartTime = itemView.findViewById(R.id.tvStartTime);
tvEndTime = itemView.findViewById(R.id.tvEndTime);
btnSetEndTime = itemView.findViewById(R.id.btnSetEndTime);
}
}
/**
* 保存函数,保存任务列表到sharedPreferences
*/
public void saveTasks() {
SharedPreferences sharedPreferences = context.getSharedPreferences("todo_list", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit(); //获取Editor对象,修改sharedPreferences
Gson gson = new Gson();
String json = gson.toJson(tasks); //将任务列表转换成JSON字符串
editor.putString("tasks", json); //将JSON字符串保存到sharedPreferences
editor.apply(); //提交修改
}
/**
* 加载函数,从SharedPreferences中加载任务列表
*/
public void loadTasks(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences("todo_list", Context.MODE_PRIVATE);
String json = sharedPreferences.getString("tasks", null); //获取保存的JSON字符串
if (json != null) {
Gson gson = new Gson();
Task[] tasksArray = gson.fromJson(json, Task[].class); //将JSON字符串转换为任务数组
tasks.clear(); //清空当前任务列表
tasks.addAll(Arrays.asList(tasksArray)); //将转换为后的任务数组添加到任务列表
notifyDataSetChanged(); //通知数据变化,更新RecyclerView
}
}
/**
* 获取RecyclerView中显示的项数,即任务列表的大小
*/
@Override
public int getItemCount() {
return tasks.size();
}
}
自此,我们就能实现文章开头的时间显示效果。
二、提醒功能
(2.1)创建通知渠道
在Android 8.0以上的版本,需要创建通知渠道,才能发送通知,我们在主函数MainActivity,中编写创建通知渠道函数,并在生命周期onCreate()中调用。
/**
* 创建通知渠道
*/
private void createNotificationChannel() {
// 检查设备的 Android 版本是否支持通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 定义通知渠道的名称
String name = "任务结束提醒";
// 定义通知渠道的描述
String description = "任务结束";
// 定义通知渠道的重要性(默认级别)
int importance = NotificationManager.IMPORTANCE_DEFAULT;
// 创建通知渠道对象
NotificationChannel channel = new NotificationChannel(
"task_reminder_channel", // 渠道 ID
name, // 渠道名称
importance // 渠道重要性
);
// 设置通知渠道的描述
channel.setDescription(description);
// 获取系统的 NotificationManager 服务
NotificationManager notificationManager = getSystemService(NotificationManager.class);
// 创建通知渠道
// 如果渠道 ID 已存在,此方法不会重复创建
notificationManager.createNotificationChannel(channel);
}
}
(2.2)新建广播接收器ReminderReceiver
创建ReminderReceiver,用于当提醒时间到达时,调用此方法,用于发送通知提醒用户
ReminderReceiver.java
package com.example.todolist.receiver;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.example.todolist.R;
public class ReminderReceiver extends BroadcastReceiver {
/**
* 广播接收器的回调方法
* 当提醒时间到达时,此方法会被调用,用于发送通知提醒用户
*/
@Override
public void onReceive(Context context, Intent intent) {
// 从 Intent 中提取任务内容
String taskContent = intent.getStringExtra("taskContent");
// 从 Intent 中提取任务 ID
int taskId = intent.getIntExtra("taskId", 0);
// 创建通知构建器
// 参数:
// 1. 应用上下文
// 2. 通知渠道 ID(必须与创建通知渠道时的 ID 一致)
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "task_reminder_channel")
.setSmallIcon(R.drawable.ic_launcher_foreground) // 设置通知图标
.setContentTitle("任务提醒") // 设置通知标题
.setContentText(taskContent) // 设置通知内容
.setPriority(NotificationCompat.PRIORITY_DEFAULT); // 设置通知优先级
// 获取系统的 NotificationManagerCompat 服务
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
// 检查是否拥有发送通知的权限
// Android 13 及以上版本需要显式请求 POST_NOTIFICATIONS 权限
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
// 如果没有权限,直接返回,不发送通知
return;
}
// 发送通知
// 参数:
// 1. 通知 ID(用于区分不同的通知)
// 2. 构建好的通知对象
notificationManagerCompat.notify(taskId, builder.build());
}
}
(3.3)在TaskAdapter,编写设置提醒、取消提醒方法,并在合适的地方调用
TaskAdapter.java
package com.example.todolist.adapter;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.PendingIntent;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Build;
import android.widget.Toast;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.example.todolist.R;
import com.example.todolist.modle.Task;
import com.example.todolist.receiver.ReminderReceiver;
import com.google.gson.Gson;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
/**
* RecyclerView适配器,用于管理任务列表的显示和交互
*/
public class TaskAdapter extends RecyclerView.Adapter<TaskAdapter.TaskViewHolder> {
private List<Task> tasks; // 任务列表
private Context context; // 上下文,用于获取资源和操作SharedPreferences
/**
* 构造函数,初始化任务列表和上下文
*
* @param tasks 任务列表
* @param context 上下文
*/
public TaskAdapter(List<Task> tasks, Context context) {
this.tasks = tasks;
this.context = context;
}
/**
* 创建并返回一个新的ViewHolder,负责显示单个任务项的视图
*/
@NonNull
@Override
public TaskAdapter.TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 加载item_task布局
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_task, parent, false);
return new TaskViewHolder(view);
}
/**
* 将任务数据绑定到对应的ViewHolder
*/
@Override
public void onBindViewHolder(@NonNull TaskAdapter.TaskViewHolder holder, int position) {
Task task = tasks.get(position); // 获取指定位置任务
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 时间的格式
holder.tvTask.setText(task.getContent()); // 设置任务内容
// 根据任务是否完成,设置文本删除线
holder.tvTask.setPaintFlags(task.isCompleted() ? Paint.STRIKE_THRU_TEXT_FLAG : 0);
// 显示任务的创建时间
if (task.getStartTime() != null) {
// 格式化日期并显示
String satrtString = simpleDateFormat.format(task.getStartTime());
holder.tvStartTime.setText("创建时间:" + satrtString);
}
// 显示任务结束时间
if (task.getEndTime() != null) {
// 格式化日期并显示
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
} else {
// 如果结束时间未设置,显示默认提示
holder.tvEndTime.setText("结束时间:未设置");
}
// 设置结束时间的按钮点击事件
holder.btnSetEndTime.setOnClickListener(v -> {
// 获取当前时间
Calendar calendar = Calendar.getInstance();
// 创建日期选择器,用于选择年、月、日
DatePickerDialog datePickerDialog = new DatePickerDialog(context, (view, year, month, dayOfMonth) -> {
// 创建时间选择器,用于选择小时和分钟
TimePickerDialog timePickerDialog = new TimePickerDialog(context, (view1, hourOfDay, minute) -> {
// 设置选择的时间到 Calendar 对象
calendar.set(year, month, dayOfMonth, hourOfDay, minute);
// 检查时间是否早于当前时间
if (calendar.getTimeInMillis() < System.currentTimeMillis()) {
Toast.makeText(context, "设置的时间早于当前时间,请重新选择", Toast.LENGTH_SHORT).show();
return; // 取消设置提醒
}
// 更新任务结束时间
task.setEndTime(calendar.getTime());
String endString = simpleDateFormat.format(task.getEndTime());
holder.tvEndTime.setText("结束时间:" + endString);
scheduleReminder(task, context);
// 刷新数据并保存
notifyDataSetChanged();
saveTasks();
},
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
true);
timePickerDialog.show(); // 显示时间选择器
},
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH));
datePickerDialog.show(); // 显示日期选择器
});
// 设置点击监听器,单击任务时,切换其完成状态
holder.itemView.setOnClickListener(v -> {
task.setCompleted(!task.isCompleted());
holder.tvTask.setPaintFlags(task.isCompleted() ? Paint.STRIKE_THRU_TEXT_FLAG : 0);
if (task.isCompleted()) {
cancelReminder(task, context); // 完成任务时取消提醒
}else {
scheduleReminder(task, context);
}
});
// 设置长按监听器,长按任务时,显示删除确认对话框
holder.itemView.setOnLongClickListener(v -> {
AlertDialog alertDialog;
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle("是否删除") // 设置对话框标题
.setMessage("是否删除该任务") // 设置对话框内容
// "确定"按钮
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
tasks.remove(position); // 从任务列表移除任务
notifyDataSetChanged(); // 通知数据变化,更新RecyclerView
cancelReminder(task,context);
saveTasks(); // 调用保存函数
dialog.dismiss(); // 关闭对话框
}
})
// "取消"按钮,setNegativeButton点击后自动关闭对话框
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
alertDialog = builder.create();
alertDialog.show();
return true;
});
}
/**
* 单个任务项的视图组件ViewHolder
*/
public static class TaskViewHolder extends RecyclerView.ViewHolder {
TextView tvTask; // 显示任务内容
TextView tvStartTime; // 创建时间
TextView tvEndTime; // 结束时间
Button btnSetEndTime; // 设置结束时间按钮
/**
* 构造函数,初始化视图组件
*/
public TaskViewHolder(@NonNull View itemView) {
super(itemView);
tvTask = itemView.findViewById(R.id.tvTask); // 获取TextView组件
tvStartTime = itemView.findViewById(R.id.tvStartTime);
tvEndTime = itemView.findViewById(R.id.tvEndTime);
btnSetEndTime = itemView.findViewById(R.id.btnSetEndTime);
}
}
/**
* 保存函数,保存任务列表到sharedPreferences
*/
public void saveTasks() {
SharedPreferences sharedPreferences = context.getSharedPreferences("todo_list", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit(); // 获取Editor对象,修改sharedPreferences
Gson gson = new Gson();
String json = gson.toJson(tasks); // 将任务列表转换成JSON字符串
editor.putString("tasks", json); // 将JSON字符串保存到sharedPreferences
editor.apply(); // 提交修改
}
/**
* 加载函数,从SharedPreferences中加载任务列表
*/
public void loadTasks(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences("todo_list", Context.MODE_PRIVATE);
String json = sharedPreferences.getString("tasks", null); // 获取保存的JSON字符串
if (json != null) {
Gson gson = new Gson();
Task[] tasksArray = gson.fromJson(json, Task[].class); // 将JSON字符串转换为任务数组
tasks.clear(); // 清空当前任务列表
tasks.addAll(Arrays.asList(tasksArray)); // 将转换为后的任务数组添加到任务列表
notifyDataSetChanged(); // 通知数据变化,更新RecyclerView
}
}
/**
* 设置提醒
*
* @param task 任务对象,包含任务内容和结束时间
* @param context 应用上下文
*/
private void scheduleReminder(Task task, Context context) {
// 如果任务没有设置结束时间,则无法设置提醒,直接返回
if (task.getEndTime() == null) {
return;
}
// 创建一个日历对象,用于处理时间
Calendar calendar = Calendar.getInstance();
calendar.setTime(task.getEndTime());
// 使用任务内容和结束时间的哈希值作为唯一请求码
// 这样可以确保每个任务的请求码是唯一的
int requestCode = (task.getContent() + task.getEndTime().getTime()).hashCode();
// 创建一个 Intent,指向 ReminderReceiver(广播接收器)
// ReminderReceiver 会在提醒时间到达时触发
Intent intent = new Intent(context, ReminderReceiver.class);
intent.putExtra("taskContent", task.getContent()); // 传递任务内容
intent.putExtra("taskId", requestCode); // 传递请求码
// 创建 PendingIntent,用于在指定时间触发广播
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, // 应用上下文
requestCode, // 唯一请求码
intent, // 广播接收器的 Intent
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE // 标志位
);
// 获取系统的 AlarmManager 服务
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
// 根据 Android 版本选择不同的设置方式
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12 及以上版本需要检查精确闹钟权限
if (alarmManager.canScheduleExactAlarms()) {
// 如果有精确闹钟权限,使用精确闹钟
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, // 使用系统时间(即使设备休眠也会唤醒)
calendar.getTimeInMillis(), // 提醒时间
pendingIntent // 待触发的 PendingIntent
);
// 显示成功提示
Toast.makeText(context, "已设置提醒:" + task.getContent(), Toast.LENGTH_SHORT).show();
} else {
// 如果没有精确闹钟权限,使用不精确的闹钟
alarmManager.set(
AlarmManager.RTC_WAKEUP, // 使用系统时间(即使设备休眠也会唤醒)
calendar.getTimeInMillis(), // 提醒时间
pendingIntent // 待触发的 PendingIntent
);
// 显示权限不足的提示
Toast.makeText(context, "提醒可能不准确,请授予精确闹钟权限", Toast.LENGTH_SHORT).show();
}
} else {
// Android 12 以下版本可以直接使用精确闹钟
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP, // 使用系统时间(即使设备休眠也会唤醒)
calendar.getTimeInMillis(), // 提醒时间
pendingIntent // 待触发的 PendingIntent
);
// 显示成功提示
Toast.makeText(context, "已设置提醒:" + task.getContent(), Toast.LENGTH_SHORT).show();
}
}
}
/**
* 取消提醒
*
* @param task 任务对象,包含任务内容和结束时间
* @param context 应用上下文
*/
private void cancelReminder(Task task, Context context) {
// 如果任务没有设置结束时间,则无法取消提醒,直接返回
if (task.getEndTime() == null) {
return;
}
// 使用任务内容和结束时间的哈希值作为唯一请求码
// 这样可以确保每个任务的请求码是唯一的
int requestCode = (task.getContent() + task.getEndTime().getTime()).hashCode();
// 创建一个 Intent,指向 ReminderReceiver(广播接收器)
Intent intent = new Intent(context, ReminderReceiver.class);
intent.putExtra("taskId", requestCode); // 传递请求码
// 创建 PendingIntent,用于取消提醒
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, // 应用上下文
requestCode, // 唯一请求码
intent, // 广播接收器的 Intent
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE // 标志位
);
// 获取系统的 AlarmManager 服务
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
// 取消提醒
alarmManager.cancel(pendingIntent);
}
// 释放 PendingIntent 资源
pendingIntent.cancel();
}
/**
* 获取RecyclerView中显示的项数,即任务列表的大小
*/
@Override
public int getItemCount() {
return tasks.size();
}
}
三、运行结果
(1)设置结束时间
(2)通知提醒
四、总结
在这篇文章中,我们实现了设置结束时间、提醒功能。
我会持续更新此项目,如果需要源码的,可以在评论区回我,或者是私信我,感谢大家的阅读,下篇文章见。