如何为带TextView的自定义Android通知添加类WhatsApp式时间差显示
实现通知中相对时间差的显示(如2min、1day)
要实现像WhatsApp或Facebook那样的动态相对时间显示,你需要完成三个核心步骤:记录通知的触发时间、计算并格式化时间差、定时更新通知内容。以下是具体的代码实现和细节说明:
1. 保存通知的触发时间
首先,在你第一次创建通知时,需要把通知的触发时间(也就是当前时间)保存下来,这样后续才能计算和当前时间的差值。推荐使用SharedPreferences来存储这个时间戳:
// 创建通知时,保存触发时间到SharedPreferences long notificationTriggerTime = System.currentTimeMillis(); SharedPreferences prefs = _context.getSharedPreferences("NotificationTimestamps", Context.MODE_PRIVATE); prefs.edit().putLong("trigger_time", notificationTriggerTime).apply();
2. 编写时间差格式化方法
接下来,写一个工具方法,用来计算通知触发时间和当前时间的差值,并转换成用户友好的字符串格式(比如"10min"、"1day")。这里还可以处理复数形式,让显示更自然:
/** * 计算并格式化时间差字符串 * @param triggerTime 通知触发的时间戳(毫秒) * @return 格式化后的相对时间字符串 */ private String formatTimeDifference(long triggerTime) { long currentTime = System.currentTimeMillis(); long timeDiffMillis = currentTime - triggerTime; // 转换为不同时间单位 long seconds = timeDiffMillis / 1000; long minutes = seconds / 60; long hours = minutes / 60; long days = hours / 24; if (days > 0) { return days + (days == 1 ? " day" : " days"); } else if (hours > 0) { return hours + (hours == 1 ? " hour" : " hours"); } else if (minutes > 0) { return minutes + (minutes == 1 ? " min" : " mins"); } else { return "Just now"; // 小于1分钟的情况 } }
3. 更新通知的TextView内容
把原来设置当前时间的代码替换成调用上面的格式化方法,获取相对时间差并设置到TextView:
// 从SharedPreferences获取保存的触发时间 SharedPreferences prefs = _context.getSharedPreferences("NotificationTimestamps", Context.MODE_PRIVATE); long triggerTime = prefs.getLong("trigger_time", System.currentTimeMillis()); // 默认值用当前时间避免异常 // 获取格式化后的时间差字符串 String timeDiffText = formatTimeDifference(triggerTime); // 设置到TextView contentView.setTextViewText(R.id.id_hours, timeDiffText);
4. 定时更新通知内容
因为时间差是动态变化的,你需要定时更新通知的显示内容。针对Android不同版本的后台限制,推荐两种方案:
方案A:使用WorkManager(推荐,适配Android 8.0+)
WorkManager是Google推荐的后台任务调度方案,能更好地应对系统的后台限制。
首先创建一个Worker类来处理通知更新:
public class NotificationUpdateWorker extends Worker { public NotificationUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // 重新构建通知的RemoteViews RemoteViews contentView = new RemoteViews(getApplicationContext().getPackageName(), R.layout.your_notification_layout); // 获取保存的触发时间 SharedPreferences prefs = getApplicationContext().getSharedPreferences("NotificationTimestamps", Context.MODE_PRIVATE); long triggerTime = prefs.getLong("trigger_time", System.currentTimeMillis()); // 设置时间差文本 contentView.setTextViewText(R.id.id_hours, formatTimeDifference(triggerTime)); // 重新构建通知 NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext(), "id") .setSmallIcon(R.drawable.smoke_icon) .setContent(contentView) .setOnlyAlertOnce(true) .setPriority(NotificationCompat.PRIORITY_MAX) .setOngoing(PrefManager.getInstance(getApplicationContext()).getPrefNotify()); // 更新通知(注意通知ID要和创建时一致) NotificationManagerCompat notificationManager = NotificationManagerCompat.from(getApplicationContext()); notificationManager.notify(1, builder.build()); return Result.success(); } // 复用之前的格式化方法 private String formatTimeDifference(long triggerTime) { long currentTime = System.currentTimeMillis(); long timeDiffMillis = currentTime - triggerTime; long seconds = timeDiffMillis / 1000; long minutes = seconds / 60; long hours = minutes / 60; long days = hours / 24; if (days > 0) { return days + (days == 1 ? " day" : " days"); } else if (hours > 0) { return hours + (hours == 1 ? " hour" : " hours"); } else if (minutes > 0) { return minutes + (minutes == 1 ? " min" : " mins"); } else { return "Just now"; } } }
然后在创建通知时,启动周期性任务:
// 创建每隔15分钟执行一次的任务(Android 8.0+最小间隔为15分钟) PeriodicWorkRequest updateRequest = new PeriodicWorkRequest.Builder( NotificationUpdateWorker.class, 15, TimeUnit.MINUTES) .build(); // 加入任务队列 WorkManager.getInstance(_context).enqueue(updateRequest);
方案B:使用AlarmManager(适合旧版本Android)
如果你的应用需要支持Android 8.0以下的版本,可以使用AlarmManager:
首先创建一个BroadcastReceiver:
public class NotificationUpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 和Worker里的更新逻辑一致,重新构建通知并更新 RemoteViews contentView = new RemoteViews(context.getPackageName(), R.layout.your_notification_layout); SharedPreferences prefs = context.getSharedPreferences("NotificationTimestamps", Context.MODE_PRIVATE); long triggerTime = prefs.getLong("trigger_time", System.currentTimeMillis()); contentView.setTextViewText(R.id.id_hours, formatTimeDifference(triggerTime)); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, "id") .setSmallIcon(R.drawable.smoke_icon) .setContent(contentView) .setOnlyAlertOnce(true) .setPriority(NotificationCompat.PRIORITY_MAX) .setOngoing(PrefManager.getInstance(context).getPrefNotify()); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); notificationManager.notify(1, builder.build()); } private String formatTimeDifference(long triggerTime) { // 复用格式化方法 long currentTime = System.currentTimeMillis(); long timeDiffMillis = currentTime - triggerTime; long seconds = timeDiffMillis / 1000; long minutes = seconds / 60; long hours = minutes / 60; long days = hours / 24; if (days > 0) { return days + (days == 1 ? " day" : " days"); } else if (hours > 0) { return hours + (hours == 1 ? " hour" : " hours"); } else if (minutes > 0) { return minutes + (minutes == 1 ? " min" : " mins"); } else { return "Just now"; } } }
然后在Manifest里注册这个Receiver:
<receiver android:name=".NotificationUpdateReceiver" />
最后在创建通知时设置定时触发:
AlarmManager alarmManager = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE); Intent updateIntent = new Intent(_context, NotificationUpdateReceiver.class); PendingIntent pendingIntent = PendingIntent.getBroadcast( _context, 0, updateIntent, PendingIntent.FLAG_IMMUTABLE); // 每隔1分钟触发一次(Android 8.0+后台限制可能导致不生效,所以更推荐WorkManager) long interval = 60 * 1000; alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + interval, interval, pendingIntent);
注意事项
- 确保通知ID(
notify()方法的第一个参数)在创建和更新时保持一致,这样才能覆盖原通知。 - 如果你的通知是非持续的(
setOngoing(false)),不需要定时更新,因为通知会被用户清除;只有持续通知才需要动态更新时间。 - 可以根据需求调整时间差的显示粒度,比如增加"秒"的显示,或者自定义更贴合产品的文案。
内容的提问来源于stack exchange,提问作者Francesco Pio Bocci
相关产品推荐
相关产品推荐

