通知渠道
在过去,用户是没办法对信息做区分的,要么同意接受所有信息,要么屏蔽所有信息,这也是 Android 通知功能的痛点。于是,Android 8.0 系统引入了通知渠道这个概念。
通知渠道,就是每条通知都要属于一个对应的渠道。每个应用程序都可以自由地创建当前应用拥有哪些通知渠道,但是这些通知渠道的控制权是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动或者是否要关闭这个渠道的通知。
通知渠道一旦创建之后就不能再修改了,因此开发者需要仔细分析自己的应用程序一共有哪些类型的通知,然后再去创建相应的通知渠道。
创建通知渠道
需要一个 NotificationManager 对通知进行管理,可以通过调用 Context 的 getSystemService() 获取。getSystemService() 接收一个字符串参数用于确定获取系统的哪个服务,这里传入 Context.NOTIFICATION_SERVICE 即可。
1 2 val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.0 ){ val channel = NotificationChannel(channelId,channelName,importance) manager.createNotificationChannel(channel) }
通知的基本用法
相比于 BroadcastReceiver 和 Service,在 Activity 里创建通知的场景还是比较少的,因为一般只有当程序进入后台时才需要使用通知。
AndroidX 库中提供了一个 NotificationCompat 类,使用这个类的构造器创建 Notification 对象,可以保证程序在所有 Android 系统版本上都能正常工作。
1 2 3 val notification = NotificationCompat.Builder(context,channelId).build()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 val notification = NotificationCompat.Builder(context,channelId) .setContentTitle("This is content title" ) .setContentText("This is content text" ) .setSmallIcon(R.drawable.small_icon) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_icon)) .build() manager.notify(1 ,notification)
示例
首先获取了 NotificationManager 的实例,并创建了一个 ID 为 normal 通知渠道。
在按钮的点击事件里完成了通知的创建工作,通过 PendingIntent 添加点击效果,取消通知的方式有两种。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagerif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ val channel = NotificationChannel("normal" ,"Normal" , NotificationManager.IMPORTANCE_DEFAULT) manager.createNotificationChannel(channel) } btnSendNotice.setOnClickListener{ val intent = Intent(this ,CameraAlbumActivity::class .java) val pi = PendingIntent.getActivity(this ,0 ,intent,0 ) val notification = NotificationCompat.Builder(this ,"normal" ) .setContentTitle("This is content title" ) .setContentText("This is content text" ) .setSmallIcon(R.drawable.small_icon) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_icon)) .setContentIntent(pi) .build() manager.notify(1 ,notification) }
通知的进阶技巧
NotificationCompat.Builder 中提供了非常丰富的 API,以便我们创建出更加多样的通知效果。
setStyle():这个方法允许我们构建出富文本的通知内容。也就是说,通知中不光可以有文字和图标,还可以包含更多的东西。接收一个 NotificationCompat.Style 参数,用来构建具体的富文本信息,如长文字、图片等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 val notification = NotificationCompat.Builder(this ,"normal" ) .setStyle(NotificationCompat.BigTextStyle() .bigText("Learn how to build notifications, send and sync data, and use voice actions, Get the official Android IDE and developer tools to build apps for Android." )) .build()
重要等级
通知渠道的重要等级越高,发出的通知就越容易获得用户的注意。比如高重要等级的通知渠道发出的通知可以弹出横幅、发出声音,而低重要等级的通知渠道发出的通知不仅可能会在某些情况下被隐藏、而且可能会被改变显示的顺序。
并且,开发者只能在创建通知渠道时为它指定初始的重要等级,如果用户不认可这个重要等级的话,可以随时进行修改,开发者对此无权再进行调整和变更,因为通知渠道一旦创建就不能再通过代码修改了。
示例(Kotlin) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 class NotificationActivity :BaseActivity () { override fun onCreate (savedInstanceState: Bundle ?) { super .onCreate(savedInstanceState) setContentView(R.layout.activity_notification) initData() } private fun initData () { val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ val channel = NotificationChannel("normal" ,"Normal" , NotificationManager.IMPORTANCE_DEFAULT) val channel2 = NotificationChannel("important" ,"Important" , NotificationManager.IMPORTANCE_HIGH) manager.createNotificationChannel(channel) manager.createNotificationChannel(channel2) } btnSendNotice.setOnClickListener{ val intent = Intent(this , CameraAlbumActivity::class .java) val pi = PendingIntent.getActivity(this ,0 ,intent,0 ) val notification = NotificationCompat.Builder(this ,"normal" ) .setContentTitle("This is content title" ) .setStyle(NotificationCompat.BigTextStyle() .bigText("Learn how to build notifications, send and sync data, and use voice actions, Get the official Android IDE and developer tools to build apps for Android." )) .setSmallIcon(R.drawable.small_icon) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_icon)) .setContentIntent(pi) .build() manager.notify(1 ,notification) val notification2 = NotificationCompat.Builder(this ,"important" ) .setContentTitle("This is content title" ) .setStyle(NotificationCompat.BigPictureStyle() .bigPicture(BitmapFactory.decodeResource(resources,R.drawable.big_image))) .setSmallIcon(R.drawable.small_icon) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.large_icon)) .setContentIntent(pi) .setAutoCancel(true ) .build() manager.notify(2 ,notification2) } } companion object { fun actionStart (context: Context ) { val intent = Intent(context, NotificationActivity::class .java) context.startActivity(intent) } } }
App 图标
在过去,Android 应用程序的图标都应该放到相应分辨率的 mipmap 目录下,不过从 Android 8.0 系统开始,Google 已经不再建议使用单一的一张图片来作为应用程序的图标,而是应该使用前景和背景分离的图标设计方式。
具体来讲,应用程序的图标应该被分为两层:前景层和背景层。前景层用来展示应用图标的 Logo,背景层用来衬托应用图标的 Logo。
并且要注意,背景层在设计时只允许定义颜色和纹理,不能定义形状。图标的形状由手机厂商来定义,手机厂商会在图标的前景层和背景层之上再盖上一层 mask,这个 mask 可以是圆角矩形、圆形或是方形等,视具体手机厂商而定,这样就可以将手机上所有程序的图标都裁剪 成相同的形状,从而统一图标的设计规范。
然后借助 Android Studio 提供的 Asset Studio 工具来制作能够兼容各个 Android 系统版本的应用程序图标。单击导航栏中 File -> New -> Image Asset 打开工具。(左边是操作区域,右边是预览区域)
操作区域:
Icon Type:保持默认即可,表示同时创建兼容 8.0 系统以及老版本系统的应用图标。
Name:应用图标的名称,保持 ic_launcher 的命名即可,这样可以覆盖掉之前自动生成的应用程序图标。
Foreground Layer 页签:用于编辑前景层
Path:图片路径
Resize:拖动条对图片进行缩放,以保证前景层的所有内容都是在安全区域中。
Background Layer 页签:用于编辑背景层
Asset Type:选取 Color 模式,并设置颜色值作为背景层的颜色。
Legacy 页签:用于编辑老版本系统的图标
预览区域:
它的主要作用就是预览应用图标在应对不同类型的 mask 的最终效果。在预览区域中给出了可能生成的图标形状。并且每个预览图标中都有一个圆圈,它叫做安全区域,必须保证图标的前景层完全处于安全区域中才行,否则可能会出现应用图标的 Logo 被手机厂商的 mask 裁减掉的情况。
Circle:圆形
Squircle:方圆形
Rounded Square:圆角正方形
Square:正方形
接下来点击 “Next” 会进入一个确认图标生成路径的界面,然后直接点击界面上的 “Finish” 按钮完成图标的制作。所有图标相关的文件都会被生成到相应分辨率的 mipmap 目录下,其中有个 mipmap-anydpi-v26 目录中放的并不是图片,而是 xml 文件,这是因为 Android 8.0 及以上系统的手机,都会使用这个目录下的文件作为图标。
1 2 3 4 5 6 7 8 <?xml version="1.0" encoding="utf-8" ?> // 适配 Android 8.0 及以上系统应用图标的标准写法 <adaptive-icon xmlns:android ="http://schemas.android.com/apk/res/android" > // 指定图标的背景层,引用的是之前设置的颜色值。 <background android:drawable ="@color/ic_launcher_background" /> // 指定图标的前景层,引用的是之前准备的 Logo 图片。 <foreground android:drawable ="@mipmap/ic_launcher_foreground" /> </adaptive-icon >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="utf-8" ?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" package ="com.sunnyweather.android" > <application android:allowBackup ="true" // 这个属性就是专门用于指定应用程序图标的 // 8.0 及以上系统中,会使用 mipmap-anydpi-v26 目录下的 ic_launcher.xml 文件。 // 7.0 及以下系统会使用 mipmap 相应分辨率目录下的 ic_launcher.png 图片。 android:icon ="@mipmap/ic_launcher" android:label ="@string/app_name" // 此属性是一个只适用于 Android 7.1 系统的过渡版本,很快就被 8.0 系统的新图标适配方案所替代了,不必关心它。 android:roundIcon ="@mipmap/ic_launcher_round" android:supportsRtl ="true" android:theme ="@style/AppTheme" > ... </application > </manifest >
备注
参考资料:
第一行代码(第3版)