关于Android 自定义通知栏UI(RemoteViews)的使用(四):RemoteViews

5,424 阅读4分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

前面都已经介绍了Notification的常用方法, 以及一些简单的进阶,高阶使用,那么接下来介绍的是对通知栏UI的自定义实现

RemoteViews

Android 的通知是都是系统自带的样式,无法通过设置自定义布局就可以修改原有的UI布局或者样式,所以Android新增了一个对通知UI自定义的组件RemoteViews,通过它可以自定义一个布局,把RemoteViews作为一个UI布局设置给Notification的内容显示。
RemoteViews,根据字面意思应该是一种远程的View,其实RemoteView表示的是一个View结构,它可以在其他进程中显示,由于它在其他进程中显示,为了能够更新它的界面,RemoteViews提供了一组基础的操作用于跨进程更新它的界面,RemoteViews主要用于通知栏和桌面小部件的开发。

RemoteViews的使用

创建 RemoteViews 对象我们只需要知道当前应用包名和布局文件的资源 id,比较简单。

    // 构建 remoteView
    RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.layout_notification);
    remoteView.setTextViewText(R.id.tvMsg, "RemoteViews");
    remoteView.setImageViewResource(R.id.ivIcon, R.mipmap.ic_launcher_round);
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    // 设置自定义 RemoteViews
    builder.setContent(remoteView).setSmallIcon(R.mipmap.ic_launcher);
    // 设置通知的优先级(悬浮通知)
    builder.setPriority(NotificationCompat.PRIORITY_MAX);
    // 设置通知的点击行为:这里启动一个 Activity
    Intent intent = new Intent(this, SecondActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    builder.setContentIntent(pendingIntent);
    builder.setAutoCancel(true);
    Notification notification = builder.build();
    NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    manager.notify(1001, notification);

如果只是单纯的显示自定义的UI,不需要任何操作,不需要更新内容,这样就可以实现自定义通知栏的效果了,但是需要实现自定义通知栏的UI,在大多数情况下,都是需要更新UI,或者对UI做事件处理,那么对于RemoteViews来说就比较复杂了。

RemoteViews 事件处理

RemoteViews 给里面的View设置点击事件,需要使用 PendingIntent 并通过 setOnClickPendingIntent 方法来实现。

Intent intent = new Intent(mContext, SecondActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
remoteView.setOnClickPendingIntent(R.id.ivIcon, pendingIntent);

这样就可以对自定义UI里面的控件点击事件做处理了

RemoteViews的UI更新

要更新 RemoteViews 就不是那么容易了,因为我们无法直接访问布局文件中的 View,而必须通过 RemoteViews 提供的特定的方法来更新 View。比如设置 TextView 文本内容需要用 setTextViewText 方法,设置 ImageView 图片需要通过 setImageViewResource 方法。之所以更新 RemoteViews 如此复杂,直接原因是因为 RemoteViews 没有提供跟 View 类似的 findViewById 这个方法,我们无法获取到 RemoteViews 中的子 View。
其实用起来之后也没有那么复杂,我使用的广播BroadcastReceiver,PendingIntent的分发,不只是Activity,也可以通过广播BroadcastReceiver来接受Intent的传递,那RemoteViews的Ui更新是不是也可以通过广播来实现呢。

Intent intent = new Intent(mContext, RemoteViewReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
remoteView.setOnClickPendingIntent(R.id.ivIcon, pendingIntent);

广播接受的地方,在更新UI

public class RemoteViewReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (KnowledgeConstants.STATUS_FOR_START.equals(action)) {
            RemoteViewNotificationUtils.getInstance().setClickAction(false);
        } else if (KnowledgeConstants.STATUS_FOR_PAUSE.equals(action)) {
            RemoteViewNotificationUtils.getInstance().setClickAction(true);
        }
    }
}

RemoteViewNotificationUtils这个类是封装的对RemoteViews的工具类,setClickAction这个方法也贴出来,具体实现,根据具体需求来做,我的需求是更新播放器的状态

/***
 * 设置点击事件
 * @param isStartPlay
 */
public void setClickAction(boolean isStartPlay) {
    Log.e("setClickAction:",isStartPlay));
    Intent intent = new Intent(mContext, RemoteViewReceiver.class);
    if (isStartPlay) {
        intent.setAction(RemoteViewConstants.STATUS_FOR_START);
        remoteView.setImageViewResource(R.id.iv_control_start, RemoteViewUtils.isNightMode() ? R.drawable.icon_pause_white : R.drawable.icon_pause);
    } else {
        intent.setAction(RemoteViewConstants.STATUS_FOR_PAUSE);
        remoteView.setImageViewResource(R.id.iv_control_start, RemoteViewUtils.isNightMode() ? R.drawable.icon_start_white : R.drawable.icon_start);
    }
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteView.setOnClickPendingIntent(R.id.iv_control_start, pendingIntent);
    notifyRemoteViews();
}

最后更新下通知就好了,实现上是不是也没用那么复杂,但是使用过程中,还有有很多点需要注意,比如更新网络图片,比如广播的注册和取消注册,bitmap的处理等等,这些细节我就不多说了,RemoteViews也支持Bitmap的类型的图片,所以也支持网络图片的加载:

remoteView.setImageViewBitmap(R.id.iv_bg, bgBitmap)

RemoteViews还可以用于定义桌面小部件的使用,这里就不多说了,因为实际的应用场景不是很多,如果有这方面的需求,大家也可以参照网上的一些资料,实现起来也很简单,也是通过广播更新UI和处理UI的点击事件的。

总结

以上基本把Notification的使用说了下,有什么不懂可以留言评论,说的不对的地方,也请大神予以指导