返回

Android 通知栏进度条实现详解 | RemoteViews

Android

Android通知栏进度条的实现方法探究

Android通知栏作为应用与用户交互的重要途径,可以提供实时的状态更新和提醒。 其中的进度条功能, 例如Uber的接单通知栏展示进度, 如何才能准确控制其进度呢? 一个关键的挑战在于无法直接获取通知栏的宽度。 以下提供一些解决此类问题的策略和代码示例。

1. 基于RemoteViews的定制化视图

定制化视图是实现复杂通知栏内容的基础。 利用RemoteViews,可以构建一个包含LinearLayout、FrameLayout和ImageView等元素的自定义布局,模拟进度条效果。

核心思想

不依赖通知栏宽度,而是控制进度条“已完成”部分的宽度,使其与总进度百分比对应。

具体步骤

  1. 创建布局文件 (notification_progress_layout.xml):

    <LinearLayout
        android:id="@+id/notification_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        xmlns:android="http://schemas.android.com/apk/res/android">
    
        <FrameLayout
            android:id="@+id/filled"
            android:layout_width="0dp"
            android:layout_height="@dimen/height"
            android:layout_weight="0"
            android:background="@color/color2"/>
    
        <ImageView
            android:id="@+id/progress_indicator"
            android:layout_width="wrap_content"
            android:layout_height="@dimen/indicator_height"
            android:src="@drawable/ic_progress_indicator"
            android:scaleType="fitCenter"
            android:adjustViewBounds="true"/>
    
        <FrameLayout
            android:id="@+id/not_filled"
            android:layout_width="0dp"
            android:layout_height="@dimen/height"
            android:layout_weight="1"
            android:background="@color/color2"/>
    </LinearLayout>
    
  2. 计算已完成部分和未完成部分的权重比例:

    // progress is a float between 0 and 1 representing the progress
    private void updateProgress(NotificationManager notificationManager, String channelId, int notificationId, float progress) {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_progress_layout);
        // 控制已完成部分的layout_weight
        remoteViews.setFloat(R.id.filled, "layout_weight", progress);
        // 控制未完成部分的layout_weight。 这里使用了 (1 - progress) 来动态的更新未完成部分的宽度
        remoteViews.setFloat(R.id.not_filled, "layout_weight", 1- progress);
    
        Notification notification = new NotificationCompat.Builder(context, channelId)
                .setSmallIcon(R.drawable.ic_notification) //设置通知小图标
                .setOngoing(true) //设置为true,notification放置在正在运行栏目中
                .setCustomContentView(remoteViews)
                .build();
    
        notificationManager.notify(notificationId, notification);
    }
    
    // 如何使用上述函数:
    float currentProgress = 0.6f; // 例如,当前进度为60%
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    String channelId = "your_channel_id";
    int notificationId = 12345; // 为每个Notification定义一个唯一的ID
    updateProgress(notificationManager, channelId, notificationId, currentProgress);
    

原理

该方法的核心在于layout_weight属性。 LinearLayout会根据子View设置的权重值,自动调整各个子View的宽度,最终填充整个可用空间。 将已完成部分和未完成部分的权重加起来保持为一个固定值(比如1),并通过动态调整已完成部分的权重来控制进度条的显示。

2. 巧用ProgressBar组件

Android SDK提供了一个ProgressBar组件,虽然标准样式与Uber的进度条有所差异,但是可以通过样式定制进行修改,也可以在定制的 RemoteViews 配合ProgressBar 实现类似的功能。

操作步骤

  1. 添加ProgressBar到RemoteViews中:
    修改上述的 notification_progress_layout.xml。使用android sdk 提供的 progressbar. 务必设置progress bar 的id 为 "android:id/progress",这是android sdk 提供的 progressbar 固定的Id
      <ProgressBar
        android:id="@android:id/progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        android:progress="50" />
    
  2. 更新ProgressBar的进度
    这里注意使用 setProgressBar 更新进度,需要保证id 为 "android:id/progress"。
      private void updateProgressBar(NotificationManager notificationManager, String channelId, int notificationId, int progress) {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_progress_layout);
        remoteViews.setProgressBar(android.R.id.progress, 100, progress, false);
    
        Notification notification = new NotificationCompat.Builder(context, channelId)
                .setSmallIcon(R.drawable.ic_notification) //设置通知小图标
                .setOngoing(true) //设置为true,notification放置在正在运行栏目中
                .setCustomContentView(remoteViews)
                .build();
    
        notificationManager.notify(notificationId, notification);
    }
    
    // 使用该函数的例子
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    String channelId = "your_channel_id";
    int notificationId = 12345; // 为每个Notification定义一个唯一的ID
    int currentProgress = 75; // 假设当前的进度是 75%
    updateProgressBar(notificationManager, channelId, notificationId, currentProgress);
    

3. 利用第三方库

一些第三方库,例如Android Support LibraryAndroidX Library 中的组件,提供了更加灵活和便捷的方式来定制通知栏。 这些库通常包含了各种自定义View和辅助类,可以简化通知栏UI的开发过程。
(鉴于已经有了RemoteViewsProgressBar 实现的思路, 这里就不在使用第三方库上进一步展开了).

总而言之,创建精确定制的进度条,重点在于找到与父容器适配的方法,比如用权重来分配各个视图的大小。使用 Android 提供的进度条组件可以减少许多手动开发的工作,当然定制化就会收到一些限制。希望这些方法能帮助您构建功能强大的通知栏进度条,从而提升用户体验。