You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 19:48:13