Android Oreo出来一段时间了,提供了很多新特性,比如通知栏样式、自动填充、电池寿命优化、自动调整TextView、XML中使用字体、可下载的字体和表情(有两篇文章推荐Android Oreo可下载字体和android开发排版指南)、自适应图标、快捷方式固定、应用程序的宽色域颜色、WebView功能增强、Java 8语言API和运行时优化,等等。
本文介绍Oreo 8.0对通知栏带来了的变化以及用户体验。
1,通知渠道
作为android开发者,肯定能很熟悉地写出弹出通知栏的代码:
var id = 1000
private fun showNotification() {
var message = "message:${Math.random()}"
var mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//初始化要用到的系统服务
var mBuilder = NotificationCompat.Builder(this)
mBuilder.setContentTitle(getString(R.string.app_name))
.setContentText("内容")
// .setContentIntent(getDefalutIntent(Notification.FLAG_AUTO_CANCEL))
// .setNumber(number)//显示数量
.setTicker("测试通知来啦")//通知首次出现在通知栏,带上升动画效果的
.setWhen(System.currentTimeMillis())//通知产生的时间,会在通知信息里显示
.setPriority(Notification.PRIORITY_MAX)//设置该通知优先级
.setAutoCancel(true)//设置这个标志当用户单击面板就可以让通知将自动取消
.setOngoing(false)//ture,设置他为一个正在进行的通知。他们通常是用来表示一个后台任务,用户积极参与(如播放音乐)或以某种方式正在等待,因此占用设备(如一个文件下载,同步操作,主动网络连接)
// .setDefaults(Notification.DEFAULT_VIBRATE)//向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合:
//Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 // requires VIBRATE permission
.setSmallIcon(R.mipmap.icon_anim_plane_1)
mBuilder.setAutoCancel(true)//点击后让通知将消失
.setContentTitle(getString(R.string.app_name))
.setContentText(message)
.setTicker(message)
mNotificationManager.notify(id, mBuilder.build())
}
这是Oreo 8.0 (API 26)之前规范的写法,但是这种写法在Oreo 8.0手机上不能唤起通知栏,而且NotificationCompat.Builder(Context context)方法已经被标记为deprecated,取而代之的是NotificationCompat.Builder(Context context, String channelId)。可以看到,构造器添加了一个新的参数String channelId,这个是是渠道ID,每个通知都必须有一个渠道ID,其允许您为要显示的每种通知类型创建用户可自定义的渠道。用户界面将通知渠道称之为通知类别。
//NotificationBuilder.kt
@TargetApi(Build.VERSION_CODES.O)
class NotificationBuilder(
private val context: Context,
private val safeContext: Context = context.safeContext(),
private val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(safeContext),
private val channelBuilder: NotificationChannelBuilder = NotificationChannelBuilder(context, CHANNEL_IDS),
private val random: Random = Random()
) {
private var notificationId: Int = 62//by bindSharedPreference(context, KEY_NOTIFICATION_ID, 0)
fun sendBundledNotification(message: Message) =
with(notificationManager) {
channelBuilder.ensureChannelsExist(createChannel)
randomChannelId.also {
notify(notificationId++, buildNotification(message, it))
notify(SUMMARY_ID, buildSummary(message, it))
}
}
private val randomChannelId
get() = CHANNEL_IDS[random.nextInt(CHANNEL_IDS.size)]
private val createChannel: (channelId: String) -> NotificationChannel? = { channelId ->
when (channelId) {
IMPORTANT_CHANNEL_ID -> NotificationChannel(channelId,
context.getString(R.string.important_channel_name),
NotificationManager.IMPORTANCE_HIGH)
.apply {
description = context.getString(R.string.important_channel_description)
}
NORMAL_CHANNEL_ID -> NotificationChannel(channelId,
context.getString(R.string.normal_channel_name),
NotificationManager.IMPORTANCE_DEFAULT)
.apply {
description = context.getString(R.string.normal_channel_description)
}
LOW_CHANNEL_ID -> NotificationChannel(channelId,
context.getString(R.string.low_channel_name),
NotificationManager.IMPORTANCE_LOW)
.apply {
description = context.getString(R.string.low_channel_description)
}
else -> null
}
}
private fun buildNotification(message: Message, channelId: String): Notification =
with(NotificationCompat.Builder(context, channelId)) {
message.apply {
setContentTitle(sender)
setContentText(text)
setWhen(timestamp?.toEpochMilli() ?: System.currentTimeMillis())
}
setSmallIcon(getIconId(channelId))
setShowWhen(true)
setGroup(GROUP_KEY)
build()
}
private fun getIconId(channelId: String) =
when (channelId) {
IMPORTANT_CHANNEL_ID -> R.drawable.ic_important
LOW_CHANNEL_ID -> R.drawable.ic_low
else -> R.drawable.ic_message
}
private fun buildSummary(message: Message, channelId: String): Notification =
with(NotificationCompat.Builder(context, channelId)) {
setContentTitle(SUMMARY_TITLE)
setContentText(SUMMARY_TEXT)
setWhen(message.timestamp?.toEpochMilli() ?: System.currentTimeMillis())
setSmallIcon(R.drawable.ic_message)
setShowWhen(true)
setGroup(GROUP_KEY)
setGroupSummary(true)
build()
}
companion object {
private const val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
private const val GROUP_KEY = "Messenger"
private const val SUMMARY_ID = 0
private const val SUMMARY_TITLE = "Nougat Messenger"
private const val SUMMARY_TEXT = "You have unread messages"
private const val IMPORTANT_CHANNEL_ID = "IMPORTANT_CHANNEL_ID"
private const val NORMAL_CHANNEL_ID = "NORMAL_CHANNEL_ID"
private const val LOW_CHANNEL_ID = "LOW_CHANNEL_ID"
private val CHANNEL_IDS =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
listOf(IMPORTANT_CHANNEL_ID, NORMAL_CHANNEL_ID, LOW_CHANNEL_ID)
} else {
listOf(NORMAL_CHANNEL_ID)
}
}
}
//NotificationChannelBuilder.kt
class NotificationChannelBuilder(
context: Context,
private val channelIds: List<String>,
private val notificationManager: NotificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
) {
fun ensureChannelsExist(createChannel: (channelId: String) -> NotificationChannel?) =
ifAtLeast(Build.VERSION_CODES.O) {
notificationManager.ensureChannelsExist(createChannel)
}
@TargetApi(Build.VERSION_CODES.O)
private fun NotificationManager.ensureChannelsExist(createChannel: (channelId: String) -> NotificationChannel?) {
channelIds
.filter { !notificationChannelIds().contains(it) }
.forEach {
createChannel(it)?.also {
notificationManager.createNotificationChannel(it)
}
}
}
@TargetApi(Build.VERSION_CODES.O)
private fun NotificationManager.notificationChannelIds() =
notificationChannels.map { it.id }
}
上面代码有点长,其实和API 26之前的写法差别大概只有三点:1,NotificationCompat.Builder使用了新的带channelId参数的构造器,分别在函数buildNotification()和函数buildSummary()中调用;2,根据channelId设置不同的smallIcon,参考方法getIconId();3,有了一个新的调用ChannelBuilder.ensureChannelsExist,它检查不存在的channelId并创建相应的渠道。
通知渠道提供了一些属性:id(唯一标识)、name(名字)、importance(重要性)、description(描述)、sound(声音)、light(光)、vibrate(震动)、LockscreenVisibility(锁屏可见性)、bypassDnd(免打扰)、showBadge(类似iOS的3DTouch)等等。
通过创建渠道组可以对渠道进行分组,调用 setGroup()方法将某个渠道关联到某个渠道组。需要注意的是,只能在将渠道提交给通知管理器NotificationManager之前修改渠道与渠道组之间的关联,这所以因为NotificationManager调用createNotificationChannel()方法之后,再对NotificationChannel进行的任何修改都将无效。代码如下:
// 通知渠道组的id.
val group = "channel_group_01"
// 用户可见的通知渠道组名称.
var name = "name"
notificationManager.createNotificationChannelGroup(NotificationChannelGroup(group, name))
如果长按一条通知栏,会出现它的渠道名字和其它相关信息,如图所示。
点击“ALL CATEGORIES”按钮进入渠道相关属性设置页面。
使用新的API创建通知栏基本就这么多,貌似东西也不是很多哦。
2,通知标志
Android 8.0中,长按app的启动图标会显示通知标志,有点类似iOS的3DTouch。只有通知渠道设置属性setShowBadge(true)(默认是true)的时候,通知标志才会起作用。通知标志可反映某个应用是否存在与其关联、并且用户尚未予以清除也未对其采取行动的通知。通知标志也称为通知点。
3,休眠
用户可以将通知置于休眠状态,以便稍后重新显示它。重新显示时通知的重要程度与首次显示时相同。应用可以移除或更新已休眠的通知,但更新休眠的通知并不会使其重新显示。操作方式是左滑或者右滑,这时会出现两个按钮:时钟按钮和设置按钮,点击时钟按钮。4,通知超时
现在,使用 setTimeoutAfter() 创建通知时您可以设置超时。您可以使用此函数指定一个持续时间,超过该持续时间后,通知应取消。如果需要,您可以在指定的超时持续时间之前取消通知。
5,通知设置
当您使用Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCESIntent 从通知创建指向应用通知设置的链接时,您可以调用 setSettingsText() 来设置要显示的文本。此系统可以提供以下 Extra 数据和 Intent,用于过滤应用必须向用户显示的设置:EXTRA_CHANNEL_ID、NOTIFICATION_TAG 和 NOTIFICATION_ID。
6,通知清除
系统现在可区分通知是由用户清除,还是由应用移除。要查看清除通知的方式,您应实现 NotificationListenerService 类的新 onNotificationRemoved() 函数。
7,背景颜色
您现在可以设置和启用通知的背景颜色。只能在用户必须一眼就能看到的持续任务的通知中使用此功能。例如,您可以为与驾车路线或正在进行的通话有关的通知设置背景颜色。您还可以使用 Notification.Builder.setColor() 设置所需的背景颜色。这样做将允许您使用 Notification.Builder.setColorized() 启用通知的背景颜色设置。
8,消息样式
现在,使用 MessagingStyle 类的通知可在其折叠形式中显示更多内容。对于与消息有关的通知,您应使用 MessagingStyle 类。您还可以使用新的 addHistoricMessage() 函数,通过向与消息相关的通知添加历史消息为会话提供上下文。
参考文献:
- https://blog.stylingandroid.com/oreo-notifications-channels-part-1/
- https://blog.stylingandroid.com/oreo-notifications-channels-part-2/
- https://developer.android.com/reference/android/app/NotificationChannel.html
- https://www.androidauthority.com/android-8-0-oreo-app-implementing-notification-channels-801097/
- https://developer.android.com/about/versions/oreo/index.html
- http://www.jianshu.com/p/92afa56aee05
网友评论