Android前台服务讲解二之自定义通知视图(RemoteViews)及数据UI更新
Notification支持文字内容显示、震动、三色灯、铃声等多种提示形式,在默认情况下,Notification仅显示消息标题、消息内容、送达时间这3项内容。
1.更新系统通知Notification显示数据
1.1创建通知
/*** 创建服务通知*/private fun createForegroundNotification(): Notification {val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)//通知小图标builder.setSmallIcon(R.mipmap.ic_launcher_round)//通知标题builder.setContentTitle("苏宁窖藏")//通知内容builder.setContentText("苏宁是国内优秀的跨国企业?$count")//设置通知显示的时间builder.setWhen(System.currentTimeMillis())//设定启动的内容val activityIntent: Intent = Intent(this, MainActivity::class.java)activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASKval pendingIntent: PendingIntent = PendingIntent.getActivity(this,1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)builder.setContentIntent(pendingIntent)builder.priority = NotificationCompat.PRIORITY_DEFAULT//设置为进行中的通知builder.setOngoing(true)//创建通知并返回return builder.build()}
1.2更新通知显示内容
- 其中notifyId是通知的唯一标识当通知的notify一致时,再发布通知则会覆盖原有的通知内容;这个方法也常用于实时更新通知内容;
- 前台服务发布通知的方法为startForeground(),使用方法和notificationManager.notify()类似,不需要另外再注册Manager;
主线程更新函数
private var count: Int = 0;private val handler: Handler = Handler(Looper.getMainLooper());private val mRunnable: Runnable = object : Runnable {override fun run() {val notification: Notification = createForegroundNotification()//发送通知到状态栏val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//通知更新UI
// notificationManager.notify(NOTIFICATION_ID, notification);//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的IDstartForeground(NOTIFICATION_ID, notification)count++handler.postDelayed(this, 1000)}}
2.如何删除关闭通知
1)通过NotificationCompat.Builder
设置setAutoCancel(true)
,这样当用户点击通知后,通知自动删除;
2)通过NotificationManager.cancel(id)
方法,删除指定 id 的通知;
3)通过 NotificationManager.cancelAll()
方法,删除该应用的所有通知;
3.通知栏自定义样式
自定义通知栏我们就要使用RemoteViews了,在SDK为16及以上才支持:
private fun getBigContentView(): RemoteViews{return RemoteViews(this.packageName, R.layout.notify_big_content_view)}private fun getContentView(): RemoteViews{return RemoteViews(this.packageName, R.layout.notify_content_view)}
自定义通知栏会有大图样式和小图样式即普通样式和扩展样式,高度上边会有要求限制,普通样式高度不能超过64dp,扩展高度不能超过256dp;
今天我们主要讲一下大小图样式显示的适配;
如果我们可爱的产品和设计妹子给到了优美的大图样式,那我们的设置方法如下:
/*** 创建服务通知*/private fun createForegroundNotification(): Notification {val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)//通知小图标builder.setSmallIcon(R.mipmap.ic_launcher_round)//通知标题builder.setContentTitle("苏宁窖藏")//通知内容builder.setContentText("苏宁是国内优秀的跨国企业?$count")//设置通知显示的时间builder.setWhen(System.currentTimeMillis())//设定启动的内容val activityIntent: Intent = Intent(this, MainActivity::class.java)activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASKval pendingIntent: PendingIntent = PendingIntent.getActivity(this,1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)builder.setContentIntent(pendingIntent)//普通样式(小图样式)builder.setCustomContentView(getContentView())//扩展样式(大图样式)builder.setCustomBigContentView(getBigContentView())builder.priority = NotificationCompat.PRIORITY_DEFAULT//设置为进行中的通知builder.setOngoing(true)//创建通知并返回return builder.build()}//普通样式视图private fun getContentView(): RemoteViews{val remoteViews: RemoteViews = RemoteViews(this.packageName, R.layout.notify_content_view)return remoteViews}//大图样式视图private fun getBigContentView(): RemoteViews{return RemoteViews(this.packageName, R.layout.notify_big_content_view)}
普通样式
builder.setCustomContentView(getContentView())
扩展样式
builder.setCustomBigContentView(getBigContentView())
显示效果如下:
长按通知显示扩展通知
4.基于RemoteViews实现音乐播放
4.1创建通知的时候为播放上一集按钮添加监听事件
private fun getBigContentView(): RemoteViews{bigNormalView = RemoteViews(this.packageName, R.layout.notify_big_content_view)val intent: Intent = Intent(PLAY_MUSIC)intent.component = ComponentName("com.yifan.service", "com.yifan.service.PlayBroadcastReceiver")val pendPreviousButtonIntent = PendingIntent.getBroadcast(this, count++, intent, PendingIntent.FLAG_UPDATE_CURRENT)//设置播放上一集按钮点击监听bigNormalView.setOnClickPendingIntent(R.id.play_last, pendPreviousButtonIntent);//设置播放相关按钮等相关监听操作...return bigNormalView}
4.2通过BroadcastReceiver接受点击通知事件
class PlayBroadcastReceiver : BroadcastReceiver(){override fun onReceive(context: Context?, intent: Intent?) {if(intent?.action == ForegroundService.Companion.PLAY_MUSIC){Toast.makeText(context, "播放上一首音乐", Toast.LENGTH_SHORT).show()//更新UI按钮显示Single.exe()}}
}
使用广播注意事项:
val intent: Intent = Intent(PLAY_MUSIC)
intent.component = ComponentName("com.yifan.service", "com.yifan.service.PlayBroadcastReceiver")
val pendPreviousButtonIntent = PendingIntent.getBroadcast(this, count++, intent, PendingIntent.FLAG_UPDATE_CURRENT)
红色代码第1行,指的是要发送一条广播,并且指定了广播的名称,这个跟我们之前注册的广播名称一一对应。
红色代码第2行,在Android 7.0及以下版本不是必须的,但是Android 8.0或者更高版本,发送广播的条件更加严苛,必须添加这一行内容。创建的ComponentName实例化对象有两个参数,第1个参数是指接收广播类的包名,第2个参数是指接收广播类的完整类名。
红色代码第3行,指的是发送广播。
4.3更新通知UI
override fun callback() {//更新UI显示bigNormalView.setTextViewText(R.id.play_last, "PlayLast")val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//通知通知UI更新了,更新显示notificationManager.notify(NOTIFICATION_ID, notification)}
4.4完整的前台通知服务代码
/*** 前台服务通知*/
class ForegroundService : Service(), UpdateUICallback{companion object{private const val TAG = "ForegroundService"//当前服务是否在运行var serviceIsLive: Boolean = false//通知IDprivate const val NOTIFICATION_ID = 1111//唯一的通知通道的ID,8.0及以上使用private const val notificationChannelId = "notification_channel_id_01"//广播监听器Actionconst val PLAY_MUSIC = "com.yifan.service.PLAY_MUSIC"}override fun onCreate() {super.onCreate()Log.d(TAG,"OnCreate")//标记服务启动serviceIsLive = true//开启前台服务通知startForegroundWithNotification()//监听UI更新回调UpdateSingleInstance.setCallBack(this)}override fun onBind(intent: Intent?): IBinder? {Log.d(TAG,"onBind")return null}override fun onUnbind(intent: Intent?): Boolean {Log.d(TAG,"onUnbind")return super.onUnbind(intent)}override fun onRebind(intent: Intent?) {super.onRebind(intent)}override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {Log.d(TAG,"onStartCommand")//数据获取val data: String? = intent?.getStringExtra("Foreground") ?: ""Toast.makeText(this, data, Toast.LENGTH_SHORT).show()return super.onStartCommand(intent, flags, startId)}/*** 开启前景服务并发送通知*/private fun startForegroundWithNotification(){//8.0及以上注册通知渠道createNotificationChannel()notification = createForegroundNotification()
// notification.contentView//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的IDstartForeground(NOTIFICATION_ID, notification)
// handler.postDelayed(mRunnable, 1000)}private var count: Int = 0;private val handler: Handler = Handler(Looper.getMainLooper());private val mRunnable: Runnable = object : Runnable {override fun run() {notification = createForegroundNotification()
// notification.defaults//发送通知到状态栏val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//通知更新UI
// notificationManager.notify(NOTIFICATION_ID, notification);//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的IDstartForeground(NOTIFICATION_ID, notification)count++handler.postDelayed(this, 1000)}}/*** 创建通知渠道*/private fun createNotificationChannel(){val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//Android8.0以上的系统,新建消息通道if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){//用户可见的通道名称val channelName: String = "Foreground Service Notification"//通道的重要程度val importance: Int = NotificationManager.IMPORTANCE_HIGH//构建通知渠道val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,channelName, importance)notificationChannel.description = "Channel description"//LED灯notificationChannel.enableLights(true)notificationChannel.lightColor = Color.RED//震动notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)notificationChannel.enableVibration(true)//向系统注册通知渠道,注册后不能改变重要性以及其他通知行为notificationManager.createNotificationChannel(notificationChannel)}}/*** 创建服务通知*/private fun createForegroundNotification(): Notification {val notificationBuidler = NotificationCompat.Builder(applicationContext, notificationChannelId)//通知小图标notificationBuidler.setSmallIcon(R.mipmap.ic_launcher_round)//通知标题notificationBuidler.setContentTitle("苏宁窖藏")//通知内容notificationBuidler.setContentText("苏宁是国内优秀的跨国企业?$count")//设置通知显示的时间notificationBuidler.setWhen(System.currentTimeMillis())//设定启动的内容val activityIntent: Intent = Intent(this, MainActivity::class.java)activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASKval pendingIntent: PendingIntent = PendingIntent.getActivity(this,1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)notificationBuidler.setContentIntent(pendingIntent)notificationBuidler.setCustomContentView(getContentView())notificationBuidler.setCustomBigContentView(getBigContentView())notificationBuidler.priority = NotificationCompat.PRIORITY_DEFAULT//设置为进行中的通知notificationBuidler.setOngoing(true)// notificationManager.notify()//创建通知并返回return notificationBuidler.build()}private lateinit var normalView: RemoteViewsprivate lateinit var bigNormalView: RemoteViewsprivate lateinit var notification: Notificationprivate fun getContentView(): RemoteViews{normalView = RemoteViews(this.packageName, R.layout.notify_content_view)
// remoteViews.set
// remoteViews.setOnClickFillInIntent()
// remoteViews.setImageViewResource()//上一首图标添加点击监听val intent: Intent = Intent(PLAY_MUSIC)intent.component = ComponentName("com.yifan.service", "com.yifan.service.PlayBroadcastReceiver")val pendPreviousButtonIntent = PendingIntent.getBroadcast(this, count++, intent, PendingIntent.FLAG_UPDATE_CURRENT)normalView.setOnClickPendingIntent(R.id.play_last, pendPreviousButtonIntent);
// PendingIntent.getreturn normalView}private fun getBigContentView(): RemoteViews{bigNormalView = RemoteViews(this.packageName, R.layout.notify_big_content_view)val intent: Intent = Intent(PLAY_MUSIC)intent.component = ComponentName("com.yifan.service", "com.yifan.service.PlayBroadcastReceiver")val pendPreviousButtonIntent = PendingIntent.getBroadcast(this, count++, intent, PendingIntent.FLAG_UPDATE_CURRENT)bigNormalView.setOnClickPendingIntent(R.id.play_last, pendPreviousButtonIntent);return bigNormalView}override fun onDestroy() {super.onDestroy()Log.d(TAG, "onDestroy")stopForeground(true)ForegroundService.serviceIsLive = false;handler.removeCallbacks(mRunnable)}override fun updateUI() {
// Toast.makeText(this, "播放上一首音乐11111", Toast.LENGTH_SHORT).show()bigNormalView.setTextViewText(R.id.play_last, "PlayLast")val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.notify(NOTIFICATION_ID, notification)}
}/*** 更新UI回调*/
interface UpdateUICallback{fun updateUI()
}/*** 更新通信工具类,ForegroundService实现UpdateUICallback接口,* 广播接收者调用UpdateSingleInstance.updateUI()通知ForegroundService更新UI*/
object UpdateSingleInstance{private var updateUICallback: UpdateUICallback? = null/*** 注册监听*/fun setCallBack(updateUICallback: UpdateUICallback){this.updateUICallback = updateUICallback}fun cancelCallBack(){this.updateUICallback = null}/*** 通知UpdateSingleInstance更新UI*/fun updateUI(){updateUICallback?.updateUI()}
}/*** 广播接收者接受通知播放点击操作*/
class PlayBroadcastReceiver : BroadcastReceiver(){override fun onReceive(context: Context?, intent: Intent?) {if(intent?.action == ForegroundService.Companion.PLAY_MUSIC){Toast.makeText(context, "播放上一首音乐", Toast.LENGTH_SHORT).show()UpdateSingleInstance.updateUI()}}
}
Notification通知布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><ImageViewandroid:layout_width="50dp"android:layout_height="50dp"android:src="@mipmap/ic_launcher"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="20dp"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="流浪的人在外想念你"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/play_last"android:text="上一首"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="暂停"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="下一首"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="收藏"/></LinearLayout></LinearLayout>
</LinearLayout>
源码参考:
服务实例使用
参考:
基于android的网络音乐播放器-通知栏控制(RemoteViews)(十)_xgq330409675的博客-CSDN博客
基于android的网络音乐播放器-通知栏控制(RemoteViews)(十)_xgq330409675的博客-CSDN博客
Android 通知栏自定义样式_stil_king的博客-CSDN博客_android 通知栏自定义android Foreground Service 前台服务/notification全局通知_ex_xyz的博客-CSDN博客_android 前台服务通知
Android前台服务讲解二之自定义通知视图(RemoteViews)及数据UI更新相关推荐
- Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解
Android绘图机制(二)--自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解 我们要想画好一些炫酷的View,首先我们得知道怎么去画一些基础的图案,比如矩形,圆 ...
- android 前台服务自定义布局不显示_Android前台服务通知未显示
我正在尝试启动前台服务.我收到通知,该服务确实启动但通知始终被抑制.我仔细检查了应用是否允许在我的设备上的应用信息中显示通知.这是我的代码: private void showNotification ...
- android 前台服务自定义布局不显示_Android自定义LinearLayout布局显示不完整的解决方法...
发现问题 原需求,在一个伸缩列表中,自定义LinearLayout继承LinearLayout动态添加布局. 然而实现的时候:一共遍历了30条数据,却只显示了一条 断点查看代码:遍历addView() ...
- Android Service 服务(二)—— BroadcastReceiver
一. BroadcastReceiver简介 BroadcastReceiver,用于异步接收广播Intent,广播Intent是通过调用Context.sendBroadcast()发送.Broad ...
- android前台服务需要解绑,Android接入
初始化 配置通道服务 必须在应用启动时(Application的onCreate方法中)初始化通道服务配置,所有进程都需要执行.try { // 参考脚手架demo从配置文件获取 Map ipStra ...
- Android自定义控件学习(二)-----自定义attr Style styleable以及其应用
相信每一位从事Android开发的猿都遇到过需要自己去自定义View的需求,如果想通过xml指定一些我们自己需要的参数,就需要自己声明一个styleable,并在里面自己定义一些attr属性,这个过程 ...
- Android 项目必备(二十九)-->App 在线升级与更新
文章目录 前言 实战 前言 1. 用户使用 App 的时候升级提醒有两种方式获得: 通过应用市场获取: 打开应用之后提醒用户更新升级. 2. 更新操作一般是在用户点击了升级按钮之后开始执行的,这里的升 ...
- android自定义listview 显示数组,android中使用arrayadapter类的自定义列表视图
从https://groups.google.com/forum/?fromgroups#!topic/android-developers/No0LrgJ6q2M绘制 public class Ma ...
- Android开发笔记(二十九)使用SharedPreferences存取数据
SharedPreferences使用场景 共享参数(SharedPreferences)是Android上的一个轻量级存储工具,存储结构是类似map的key-value键值对形式.它主要用于保存ap ...
最新文章
- sci-learn fit_transform() 与 transform()
- Android应用程序组件Content Provider的共享数据更新通知机制分析(3)
- 网络攻防 第三周学习总结
- 有道智能学习灯 初体验
- python中xml模块_python学习第十五天-2(XML模块)
- 库克“一语成谶”:又有 30 万台安卓设备被“感染”了!| 文末福利
- 2019年技术盘点微服务篇(一) | 程序员硬核评测
- Github排序(转载)
- UTF8 与 UTF8 +BOM 区别
- Python程序:输出斐波那契数列
- wpf和winform的那点区别
- oracle 用户解锁和修改用户密码
- title属性样式 原生dom_HTML DOM title 属性
- AI科学计算领域的再突破,昇思MindSpore做“基石”的决心有多强?
- 银行家算法C++代码实现
- CSDN各产品线月度NPS分析报告新鲜出炉【2021年7月】
- layui icon服务器上显示不出来,关于layui的动态图标不显示的解决方法
- 伍斯特理工学院计算机科学硕士,美国伍斯特理工学院数据科学硕士录取
- Python全栈笔记(六)
- Python下载qq音乐歌曲实例教程