1.1 DatabaseHelper.java文件代码
package cn.itcast.myapplication;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private NoteAdapter adapter;
private DatabaseHelper dbHelper;
private static final int NOTIFICATION_PERMISSION_CODE = 123; // Android 13的SDK版本号
private static final int ANDROID_13_SDK = 33;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new DatabaseHelper(this);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
findViewById(R.id.fabAdd).setOnClickListener(v -> {
startActivity(new Intent(this, EditActivity.class));
});
loadNotes();
// 检查通知是否启用
if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) {
showNotificationSettingsDialog();
}
}
/**
* 显示通知设置对话框
*/
private void showNotificationSettingsDialog() {
new AlertDialog.Builder(this)
.setTitle("开启通知")
.setMessage("要使记事本的提醒功能正常工作,需要在设置中启用通知。")
.setPositiveButton("去设置", (dialog, which) -> {
// 打开应用通知设置
Intent intent = new Intent();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
} else {
intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("app_package", getPackageName());
intent.putExtra("app_uid", getApplicationInfo().uid);
}
startActivity(intent);
})
.setNegativeButton("取消", null)
.create()
.show();
}
private void loadNotes() {
List<Note> notes = new ArrayList<>();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTE, null, null, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_ID));
String title = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_TITLE));
String content = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_CONTENT));
String time = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_TIME))
long reminderTime = 0; // 获取提醒相关字段,默认值为0(无提醒)
boolean hasReminder = false;
if (cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME) != -1) { // 检查列是否存在(适应数据库升级)
reminderTime = cursor.getLong(cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME));
}
if (cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER) != -1) {
hasReminder = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER)) == 1;
}
notes.add(new Note(id, title, content, time, reminderTime, hasReminder));
}
cursor.close();
}
adapter = new NoteAdapter(notes, new NoteAdapter.OnItemClickListener() {
@Override
public void onItemClick(Note note) {
Intent intent = new Intent(MainActivity.this, EditActivity.class);
intent.putExtra("NOTE_ID", note.getId());
startActivity(intent);
}
@Override
public void onItemLongClick(Note note) {
showDeleteDialog(note);
}
});
recyclerView.setAdapter(adapter);
}
private void showDeleteDialog(Note note) {
new AlertDialog.Builder(this)
.setTitle("删除记录")
.setMessage("确定要删除这条记录吗?")
.setPositiveButton("删除", (dialog, which) -> {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete(DatabaseHelper.TABLE_NOTE,
DatabaseHelper.COLUMN_ID + "=?",
new String[]{String.valueOf(note.getId())})
if (note.hasReminder()) { // 如果有提醒,需要取消
NotificationHelper.cancelAlarm(this, note.getId());
}
loadNotes(); // 刷新列表
})
.setNegativeButton("取消", null)
.show();
}
@Override
protected void onResume() {
super.onResume();
loadNotes(); // 每次返回时刷新数据
}
1.2 EditActivity.java文件代码
package cn.itcast.myapplication;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class EditActivity extends AppCompatActivity {
private EditText etTitle, etContent;
private CheckBox cbReminder;
private TextView tvReminderTime;
private Button btnSetReminder;
private DatabaseHelper dbHelper;
private int noteId = -1;
private Calendar reminderCalendar; // 用于存储提醒时间
private boolean hasReminder = false; // 是否设置了提醒
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit);
dbHelper = new DatabaseHelper(this);
etTitle = findViewById(R.id.etTitle);
etContent = findViewById(R.id.etContent);
cbReminder = findViewById(R.id.cbReminder);
tvReminderTime = findViewById(R.id.tvReminderTime);
btnSetReminder = findViewById(R.id.btnSetReminder);
reminderCalendar = Calendar.getInstance();
btnSetReminder.setOnClickListener(v -> showDateTimePicker());
// 设置提醒开关的变化监听
cbReminder.setOnCheckedChangeListener((buttonView, isChecked) -> {
hasReminder = isChecked;
btnSetReminder.setEnabled(isChecked);
if (!isChecked) {
tvReminderTime.setText("未设置提醒");
}
});
noteId = getIntent().getIntExtra("NOTE_ID", -1);
if (noteId != -1) {
SQLiteDatabase db = dbHelper.getReadableDatabase(); // 加载已有数据
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTE, null,
DatabaseHelper.COLUMN_ID + "=?", new String[]{String.valueOf(noteId)}, null, null, null);
if (cursor != null && cursor.moveToFirst()) { etTitle.setText(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_TITLE))); etContent.setText(cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_CONTENT)));
if (cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME) != -1 &&// 读取提醒相关信息
cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER) != -1) {
long reminderTime = cursor.getLong(cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME));
hasReminder = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER)) == 1;
if (hasReminder && reminderTime > 0) {
reminderCalendar.setTimeInMillis(reminderTime);
updateReminderTimeText();
}
cbReminder.setChecked(hasReminder);
btnSetReminder.setEnabled(hasReminder);
}
cursor.close();
}
}
findViewById(R.id.btnSave).setOnClickListener(v -> saveNote());
}
/**
* 显示日期和时间选择器
*/
private void showDateTimePicker() {
final Calendar currentCalendar = Calendar.getInstance();
DatePickerDialog datePickerDialog = new DatePickerDialog( this, // 创建日期选择器
(view, year, month, dayOfMonth) -> {
reminderCalendar.set(Calendar.YEAR, year);
reminderCalendar.set(Calendar.MONTH, month);
reminderCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
new TimePickerDialog( // 选择完日期后,显示时间选择器
EditActivity.this,
(view1, hourOfDay, minute) -> {
reminderCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
reminderCalendar.set(Calendar.MINUTE, minute);
reminderCalendar.set(Calendar.SECOND, 0);
updateReminderTimeText(); // 更新界面上的时间显示
},
currentCalendar.get(Calendar.HOUR_OF_DAY),
currentCalendar.get(Calendar.MINUTE),
true
).show();
},
currentCalendar.get(Calendar.YEAR),
currentCalendar.get(Calendar.MONTH),
currentCalendar.get(Calendar.DAY_OF_MONTH)
);
// 设置最小日期为当天
datePickerDialog.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
datePickerDialog.show();
}
/**
* 更新界面上的提醒时间文本
*/
private void updateReminderTimeText() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault());
tvReminderTime.setText("提醒时间:" + sdf.format(reminderCalendar.getTime()));
}
private void saveNote() {
String title = etTitle.getText().toString().trim();
String content = etContent.getText().toString().trim();
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()).format(new Date());
if (title.isEmpty()) {
Toast.makeText(this, "请输入标题", Toast.LENGTH_SHORT).show();
return;
}
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(DatabaseHelper.COLUMN_TITLE, title);
values.put(DatabaseHelper.COLUMN_CONTENT, content);
values.put(DatabaseHelper.COLUMN_TIME, time);
values.put(DatabaseHelper.COLUMN_HAS_REMINDER, hasReminder ? 1 : 0);
if (hasReminder) {
values.put(DatabaseHelper.COLUMN_REMINDER_TIME, reminderCalendar.getTimeInMillis());
} else {
values.put(DatabaseHelper.COLUMN_REMINDER_TIME, 0); // 没有提醒,设置为0
}
if (noteId == -1) {
long newId = db.insert(DatabaseHelper.TABLE_NOTE, null, values); // 新增记录
noteId = (int) newId;
} else {
db.update(DatabaseHelper.TABLE_NOTE, values, // 更新记录
DatabaseHelper.COLUMN_ID + "=?", new String[]{String.valueOf(noteId)});
}
// 如果设置了提醒,则设置闹钟
if (hasReminder && reminderCalendar.getTimeInMillis() > System.currentTimeMillis()) {
NotificationHelper.setAlarm(
this,
noteId,
title,
content,
reminderCalendar.getTimeInMillis()
);
Toast.makeText(this, "已设置提醒", Toast.LENGTH_SHORT).show();
} else if (hasReminder) {
// 如果设置的提醒时间已过期
Toast.makeText(this, "提醒时间已过期,请重新设置", Toast.LENGTH_SHORT).show();
return;
} else {
NotificationHelper.cancelAlarm(this, noteId); // 如果之前有提醒,现在取消了,则取消闹钟
}
finish(); // 返回主界面
}
}
1.3 MainActivity.java文件代码
package cn.itcast.myapplication;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private NoteAdapter adapter;
private DatabaseHelper dbHelper;
private static final int NOTIFICATION_PERMISSION_CODE = 123;
private static final int ANDROID_13_SDK = 33;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new DatabaseHelper(this);
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
findViewById(R.id.fabAdd).setOnClickListener(v -> {
startActivity(new Intent(this, EditActivity.class));
});
loadNotes();
if (!NotificationManagerCompat.from(this).areNotificationsEnabled()) { // 检查通知是否启用 showNotificationSettingsDialog();
}
}
/**
* 显示通知设置对话框
*/
private void showNotificationSettingsDialog() {
new AlertDialog.Builder(this)
.setTitle("开启通知")
.setMessage("要使记事本的提醒功能正常工作,需要在设置中启用通知。")
.setPositiveButton("去设置", (dialog, which) -> {
Intent intent = new Intent(); // 打开应用通知设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
} else {
intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
intent.putExtra("app_package", getPackageName());
intent.putExtra("app_uid", getApplicationInfo().uid);
}
startActivity(intent);
})
.setNegativeButton("取消", null)
.create()
.show();
}
private void loadNotes() {
List<Note> notes = new ArrayList<>();
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query(DatabaseHelper.TABLE_NOTE, null, null, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_ID));
String title = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_TITLE));
String content = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_CONTENT));
String time = cursor.getString(cursor.getColumnIndex(DatabaseHelper.COLUMN_TIME));
long reminderTime = 0; // 获取提醒相关字段,默认值为0(无提醒)
boolean hasReminder = false;
// 检查列是否存在(适应数据库升级)
if (cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME) != -1) {
reminderTime = cursor.getLong(cursor.getColumnIndex(DatabaseHelper.COLUMN_REMINDER_TIME));
}
if (cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER) != -1) {
hasReminder = cursor.getInt(cursor.getColumnIndex(DatabaseHelper.COLUMN_HAS_REMINDER)) == 1;
}
notes.add(new Note(id, title, content, time, reminderTime, hasReminder));
}
cursor.close();
}
adapter = new NoteAdapter(notes, new NoteAdapter.OnItemClickListener() {
@Override
public void onItemClick(Note note) {
Intent intent = new Intent(MainActivity.this, EditActivity.class);
intent.putExtra("NOTE_ID", note.getId());
startActivity(intent);
}
@Override
public void onItemLongClick(Note note) {
showDeleteDialog(note);
}
});
recyclerView.setAdapter(adapter);
}
private void showDeleteDialog(Note note) {
new AlertDialog.Builder(this)
.setTitle("删除记录")
.setMessage("确定要删除这条记录吗?")
.setPositiveButton("删除", (dialog, which) -> {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete(DatabaseHelper.TABLE_NOTE,
DatabaseHelper.COLUMN_ID + "=?",
new String[]{String.valueOf(note.getId())});
if (note.hasReminder()) { // 如果有提醒,需要取消
NotificationHelper.cancelAlarm(this, note.getId());
}
loadNotes(); // 刷新列表
})
.setNegativeButton("取消", null)
.show();
}
@Override
protected void onResume() {
super.onResume();
loadNotes(); // 每次返回时刷新数据
}
}
1.4 Note.java文件代码
package cn.itcast.myapplication;
public class Note {
private int id;
private String title;
private String content;
private String time;
private long reminderTime; // 提醒时间(毫秒)
private boolean hasReminder; // 是否设置提醒
public Note(int id, String title, String content, String time, long reminderTime, boolean hasReminder) { // 构造函数、Getter 和 Setter 方法
this.id = id;
this.title = title;
this.content = content;
this.time = time;
this.reminderTime = reminderTime;
this.hasReminder = hasReminder;
}
public int getId() { return id; }
public String getTitle() { return title; }
public String getContent() { return content; }
public String getTime() { return time; }
public long getReminderTime() { return reminderTime; }
public boolean hasReminder() { return hasReminder; }
public void setReminderTime(long reminderTime) { this.reminderTime = reminderTime; }
public void setHasReminder(boolean hasReminder) { this.hasReminder = hasReminder; }
}
1.5 NoteAdapter.java文件代码
package cn.itcast.myapplication;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class NoteAdapter extends RecyclerView.Adapter<NoteAdapter.ViewHolder> {
private List<Note> notes;
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(Note note);
void onItemLongClick(Note note);
}
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle, tvTime, tvContent;
public ViewHolder(View itemView) {
super(itemView);
tvTitle = itemView.findViewById(R.id.tvTitle);
tvTime = itemView.findViewById(R.id.tvTime);
tvContent = itemView.findViewById(R.id.tvContent);
}
}
public NoteAdapter(List<Note> notes, OnItemClickListener listener) {
this.notes = notes;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_note, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Note note = notes.get(position);
holder.tvTitle.setText(note.getTitle());
holder.tvTime.setText(note.getTime());
holder.tvContent.setText(note.getContent());
holder.itemView.setOnClickListener(v -> listener.onItemClick(note));
holder.itemView.setOnLongClickListener(v -> {
listener.onItemLongClick(note);
return true;
});
}
@Override
public int getItemCount() {
return notes.size();
}
}
1.6 NotificationHelper.java文件代码
package cn.itcast.myapplication;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.provider.Settings;
public class NotificationHelper {
private static final String CHANNEL_ID = "note_reminder_channel";
private static final String CHANNEL_NAME = "记事本提醒";
private static final String CHANNEL_DESCRIPTION = "记事本应用的提醒通知";
private static final String ACTION_STOP_VIBRATION = "cn.itcast.myapplication.STOP_VIBRATION"; // 震动相关常量
private static Vibrator activeVibrator; // 用于存储当前活跃的震动器
private static int activeNoteId = -1; // 当前正在震动的笔记ID
/**
* 设置提醒闹钟
* @param context 上下文
* @param noteId 笔记ID
* @param title 笔记标题
* @param content 笔记内容
* @param timeInMillis 提醒时间(毫秒)
*/
public static void setAlarm(Context context, int noteId, String title, String content, long timeInMillis) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, ReminderReceiver.class);
intent.putExtra("NOTE_ID", noteId);
intent.putExtra("NOTE_TITLE", title);
intent.putExtra("NOTE_CONTENT", content);
// 使用笔记ID作为请求码,确保每个笔记的提醒是唯一的
PendingIntent pendingIntent = PendingIntent.getBroadcast( context, noteId, intent,
PendingIntent.FLAG_UPDATE_CURRENT );
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 设置精确闹钟 alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
}
Toast.makeText(context, "提醒已设置:" + formatTime(timeInMillis), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "设置提醒失败:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
/**
* 格式化时间为友好显示
*/
private static String formatTime(long timeInMillis) {
return android.text.format.DateFormat.format("yyyy-MM-dd HH:mm", timeInMillis).toString();
}
/**
* 取消提醒闹钟
* @param context 上下文
* @param noteId 笔记ID
*/
public static void cancelAlarm(Context context, int noteId) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, ReminderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
noteId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
);
alarmManager.cancel(pendingIntent);
if (activeNoteId == noteId) {
stopVibration(); // 同时关闭震动和通知
dismissNotification(context, noteId);
}
}
/**
* 创建通知渠道(Android 8.0+需要)
* @param context 上下文
*/
public static void createNotificationChannel(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription(CHANNEL_DESCRIPTION);
channel.enableVibration(true);
channel.enableLights(true);
channel.setLightColor(Color.RED);
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
channel.setShowBadge(true);
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
/**
* 显示提醒通知
* @param context 上下文
* @param noteId 笔记ID
* @param title 通知标题
* @param content 通知内容
*/
public static void showNotification(Context context, int noteId, String title, String content) {
activeNoteId = noteId; // 保存当前正在震动的笔记ID
createNotificationChannel(context); // 创建通知渠道
Intent openIntent = new Intent(context, EditActivity.class); // 创建打开编辑活动的意图
openIntent.putExtra("NOTE_ID", noteId);
openIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent openPendingIntent = PendingIntent.getActivity(context, noteId,openIntent,
PendingIntent.FLAG_UPDATE_CURRENT );
Intent stopIntent = new Intent(context, StopVibrationActivity.class); // 创建停止震动的意图
stopIntent.putExtra("NOTE_ID", noteId);
stopIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent stopPendingIntent = PendingIntent.getActivity(
context,
noteId + 1000,
stopIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); //获取默认的通知声音
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) // 构建通知
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("⚠️ " + title)
.setContentText(content)
.setPriority(NotificationCompat.PRIORITY_MAX) // 最高优先级
.setCategory(NotificationCompat.CATEGORY_ALARM)
.setSound(defaultSoundUri)
.setLights(Color.RED, 1000, 500)
.setOngoing(true) // 让通知不能被轻易移除
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setContentIntent(openPendingIntent)
.setAutoCancel(false); // 点击后不自动移除通知
builder.addAction( // 添加停止震动按钮
android.R.drawable.ic_delete, // 使用系统内置的删除图标
"停止震动",
stopPendingIntent
);
try
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); // 显示通知
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
notificationManager.notify(noteId, builder.build()); // 检查是否开启了通知
startVibration(context); // 执行持续震动
// 显示一个Toast提示,防止用户找不到通知
Toast.makeText(context, "提醒:" + title, Toast.LENGTH_LONG).show();
} else {
// 没有通知权限,只执行震动和Toast提示
Toast.makeText(context, "⚠️ 提醒:" + title + "\n(无通知权限,请在设置中授予权限)", Toast.LENGTH_LONG).show();
startVibration(context);
showNotificationPermissionToast(context); // 提示用户开启通知权限
}
} catch (SecurityException e) {
Toast.makeText(context, "无法显示通知,请在设置中授予权限", Toast.LENGTH_LONG).show();
startVibration(context);
} catch (Exception e) {
Toast.makeText(context, "通知显示失败:" + e.getMessage(), Toast.LENGTH_LONG).show();
startVibration(context);
}
}
/**
* 提示用户开启通知权限
*/
private static void showNotificationPermissionToast(Context context) {
Toast.makeText(context,
"请在设置中为记事本开启通知权限,否则将无法收到提醒通知",
Toast.LENGTH_LONG).show();
}
/**
* 关闭通知
* @param context 上下文
* @param noteId 笔记ID
*/
public static void dismissNotification(Context context, int noteId) {
NotificationManagerCompat.from(context).cancel(noteId);
}
/**
* 触发设备持续震动
* @param context 上下文
*/
public static void startVibration(Context context) {
stopVibration(); // 先停止之前的震动
Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); //创建新的震动
if (vibrator != null && vibrator.hasVibrator()) {
activeVibrator = vibrator;
// 震动模式:等待时间,震动时间,等待时间,震动时间...(毫秒)
// 第一个数字是等待时间,设为0表示立即开始震动
long[] pattern = {0, 2000, 1000, 2000, 1000, 2000, 1000, 2000}; // 较长的震动-暂停模式
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// 对于Android 8.0及以上设备,使用VibrationEffect
// 参数REPEAT表示从pattern的第几个索引开始循环,-1表示不循环
vibrator.vibrate(VibrationEffect.createWaveform(pattern, 0)); // 从索引0开始循环
} else {
// 最后一个参数表示重复模式,-1表示不重复,0表示从pattern的第0个索引开始重复
vibrator.vibrate(pattern, 0); // 从索引0开始循环
}
} catch (Exception e) {
// 处理震动失败的情况
Toast.makeText(context, "震动功能可能不可用", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(context, "设备不支持震动功能", Toast.LENGTH_SHORT).show();
}
}
/**
* 停止设备震动
*/
public static void stopVibration() {
if (activeVibrator != null) {
try {
activeVibrator.cancel();
} catch (Exception e) {
// 忽略停止震动时的异常
} finally {
activeVibrator = null;
}
}
}
/**
* 检查是否为停止震动的动作
* @param action 动作字符串
* @return 是否是停止震动动作
*/
public static boolean isStopVibrationAction(String action) {
return ACTION_STOP_VIBRATION.equals(action);
}
}
1.7 ReminderReceiver.java文件代码
package cn.itcast.myapplication;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
/**
* 提醒广播接收器,接收提醒闹钟触发的广播并显示通知
*/
public class ReminderReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
try {
String action = intent.getAction(); // 检查是否是停止震动的请求
if (NotificationHelper.isStopVibrationAction(action)) { // 停止震动
NotificationHelper.stopVibration();
Toast.makeText(context, "震动已停止", Toast.LENGTH_SHORT).show();
return;
}
int noteId = intent.getIntExtra("NOTE_ID", -1); // 处理正常的提醒
String title = intent.getStringExtra("NOTE_TITLE");
String content = intent.getStringExtra("NOTE_CONTENT");
if (noteId != -1 && title != null) {
NotificationHelper.showNotification(context, noteId, "提醒: " + title, content);
} else { // 显示通知并震动
Toast.makeText(context, "提醒失败:无效的笔记信息", Toast.LENGTH_SHORT).show(); // 参数无效
}
} catch (Exception e) {
Toast.makeText(context, "提醒处理失败:" + e.getMessage(), Toast.LENGTH_LONG).show(); // 捕获所有可能的异常
}
}
}
1.8 StopVibrationActivity.java文件代码
package cn.itcast.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
/**
* 停止震动的活动
* 这个活动只是一个透明的桥梁,不会显示UI界面
* 作用是停止震动并关闭相关通知,然后立即结束自己
*/
public class StopVibrationActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int noteId = getIntent().getIntExtra("NOTE_ID", -1); // 获取笔记ID
NotificationHelper.stopVibration();// 停止震动
if (noteId != -1) {
NotificationHelper.dismissNotification(this, noteId); // 移除通知
Toast.makeText(this, "震动已停止", Toast.LENGTH_SHORT).show(); // 显示提示
finish();
}
}
在该项目里,简要分析其中的以下代码
public void onItemClick(Note note) {
Intent intent = new Intent(MainActivity.this, EditActivity.class);
intent.putExtra("NOTE_ID", note.getId());
startActivity(intent);
}