文章目录
- 使用通知
- 将程序运行到手机上
- 使用通知
- 创建通知渠道
- 通知的基本用法
- 通知的进阶技巧
- setStyle()方法
- 不同重要等级的通知渠道
使用通知
将程序运行到手机上
- 在AS当中除了使用模拟器来运行我们的程序,还可以使用真机来运行我们写的程序
- 想要将程序运行到手机上,首先需要将手机通过数据线连接到我们的电脑上
- 打开自己手机的开发人员设置,打开USB调试即可,然后Android Studio就可以自动识别到你的手机
使用通知
- 通知是Android系统当中比较有特色的一个功能,当某个应用程序希望用户发出一些提示信息,而该程序又不在前台运行的时候,就饿可以借助通知来进行实现
- 发出一条通知之后,手机最上方的状态栏就会显示一个通知图标,下拉状态栏之后就可以看到通知的详细内容
- Android的通知自退出之后就大获成功,IOS系统也在5.0版本之后加入了类似的功能
创建通知渠道
- 通知这个功能最初的初衷是好的,但是在后来却被开发者玩坏了
- 每发出一条通知,都可能意味着自己的应用程序会拥有更高的打开率,因此有太多太多应用想尽办法地给用户发送通知,以博取更多的展示机会.
- 虽然Android系统允许我们将某个应用程序的通知完全屏蔽掉,以防止他一直给我们发送垃圾信息,但是在这些信息当中,也有可能存在我们想要关心的内容
- 比如说我们想要接收微博我们关注的某个人发送来的信息,但是又不想接收微博推送的花边信息,这在之前,用户是没有办法对这些信息做区分的,要么同意接收所有信息,要么屏蔽所有信息,这也是Android通知的功能痛点
- 于是Android8.0系统引入了通知渠道这个概念
- 顾名思义就是每一条通知都要有一个属于自己的渠道,每个应用程序都可以自由的创建当前应用拥有那些通知渠道,但是这些通知渠道的控制权是掌握在用户手中的,用户可以自由的选择这些通知渠道的重要程度,是否响铃,是否震动或者是否需要关闭.
- 拥有了渠道控制权之后,用户就可以对一个应用的通知进行选择性的关闭了
- 而对于每个应用来说,通知渠道的划分是十分考究的,因为通知渠道一旦创建之后就不能进行修改了,因此开发者需要仔细分析自己的应用程序一共有哪几类通知的类型,然后再去创建相应的通知渠道
- 参考一下推特的通知渠道划分
- 可以看到推特根据自己的通知类型,对通知渠道进行了非常详细的划分,这样用户的自主选择权力就比较高了.
- 要创建通知渠道,我们先要创建一个NotificationManager对通知进行管理,并且调用NotificationManager的createNotificationChannel()方法完成创建.
- 由于NotificationManager类和createNotificationChannel()方法都是Android8.0系统中新增的API,因此我们在使用的时候还需要进行版本判断才可以,写法如下:
if(Build.VERSION.SDK_INT >= Build.VERSON_CODES.O) {
val channel = NotificationChannel(channelId, channlName, importance)
manager.createNotificationChannel(channel)
}
- 创建一个通知渠道至少需要渠道ID,渠道名称以及重要等级这三个参数
- 其中渠道ID可以随便定义,只要保证全局唯一就可以了,渠道名称是给用户看的.需要清楚表达这个渠道的用途
- 通知的重要等级有:IMPORTANCE_HIGH,IMPORTANCE_DEFAULT,IMPORTANCE_LOW,IMPORTANCE_MIN这几种.
- 对应的重要程度依次由高到低,不同的重要等级会决定通知的不同行为,并且用户是随时可以更改某个通知渠道的重要等级的,这是开发者无法干预的.
通知的基本用法
-
通知的用法还是比较灵活的,既可以在Activity里面创建,也可以在BroadcastReceiver里面创建,也可以在Service里面进行创建
-
相比于在BroadcastReceiver和Service,在Activity里面创建通知的场景比较少,因为一般就是当程序进入后台的时候才会进行通知
-
但是无论在那里进行创建,整体的步骤都是相同的
-
首先需要一个Builder构造器来创建Notification对象,但是问题在于Android系统的每一个版本对通知的功能或多或少都是有一定的改动的,API不稳定的问题在通知上凸显的比较严重.
-
比如通知渠道功能在Android8.0之前就是没有的,那么如何解决该问题,就是使用AndroidX库中提供的兼容API.AndroidX库中提供了一个NotificationCompat类,使用这个类的构造器创建Notification对象就可以保证我们的程序在所有的Android系统版本上都能够正常工作了,代码如下所示.
val notification = NotificationCompat.Builder(context, channelId).build()
- NotificationCompat.Builder的构造函数当中接受两个参数
- 第一个参数是context
- 第二个参数是渠道ID,需要和我们在创建通知渠道的时候指定的渠道id匹配
- 上述代码只是创建了一个空的Notification对象,并不具有实际意义,我们可以在最终build()方法之前连缀任意多的设置方法来创建一个丰富的Notification对象,下面是一些最基本的设置
val notification = NotificationCompat.Builder(context, channelId)
.setContentTitle("This is content title")
.setContentText("This is content text")
.setSmallIcon(R.drawable.small_icon)
.setLargeIcon(BitmapFactory.decodeResource(getResource(), R.drawable.large_icon))
.build()
- 上述代码一共调用了四个设置方法分别用来指定通知的标题内容,正文内容,通知的小图标,通知的大图标等
- 以上工作都完成之后,只需要调用NotificationManager.notfy()方法就可以让通知显示出来了
- notify()方法接受两个参数,第一个参数是id,要保证为每个通知指定id都是不同的,第二个参数是Notification对象,这里我们直接将刚刚创建好的Notification对象传入即可,因此显示一个通知就可以完成了
manager.notify(1, notification)
- 创建NotificationTest项目来编写示例
- 先编写布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/sendNotice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Notice" />
</LinearLayout>
- 编辑按钮的点击事件
package com.zb.notificationtest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.graphics.BitmapFactory
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.app.NotificationCompat
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//先获取NotificationManager的实例
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//创建一个ID为normal的渠道
//创建渠道的代码只在第一次执行的时候才会创建,当下次再次执行创建代码的时候
//系统会检测到该通知渠道已经创建成功,因此不会重复创建,也不会影响执行效率
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel =
NotificationChannel("normal", "Normal", NotificationManager.IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
}
//设置按钮的点击事件
sendNotice.setOnClickListener {
val notification = NotificationCompat.Builder(this, "normal")
.setContentTitle("this is content title")
.setContentText("this is content text")
.setSmallIcon(R.drawable.small_icon)
.setLargeIcon(
BitmapFactory.decodeResource(
resources,
R.drawable.large_icon
)
)
.build()
manager.notify(1, notification)
}
}
}
- 运行项目,点击按钮就可以发现手机的通知栏给我们发送了一条通知,下拉通知栏之后就可以看到这条通知的详情.
- 但是此时点击这条通知,是没有任何反应的,但是我们日常使用的应用发送的通知一般点击之后都会跳转到具体的通知详情页面,想要实现这种效果,我们还需要在代码的相应位置进行设置,这就要涉及一个全新的PendingIntent概念了.
- PendingIntent从名字上面来看和Intent有点类似,它们确实存在不少的共同点,比如它们都可以指明一个意图,都可以用来启动一个Activity,启动Service以及发送广播等等
- 不同的是Intent倾向于立即执行某个动作,而PendingIntent倾向于某个合适的时机执行某个动作,所以可以把PendingIntent简单理解为延迟执行的Intent
- PendingIntent它主要提供了几个静态方法用于获取PendingIntent实例,可以根据需求来选择是使用getActivity()方法,getBroadcast()方法,还是getService()方法,这几个方法所接收的参数都是相同的
- 第一个参数依旧是Context
- 第二个参数一般用不到传入0即可
- 第三个参数是一个Intent对象,我们可以通过这个对象构建出PendingIntent的意图
- 第四个参数用于确定PendingIntent的行为,有FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT这4个值可以选择.
- 每种值得具体含义可以查看文档,通常情况下这个参数传入0就可以了.
- 之前NotificationCompat.Builder这个构造器还可以连缀一个setContentInent()方法,接收的参数正是一个PendingIntent对象
- 因此这里就可以通过PendingIntent构建一个延迟执行的"意图",当用户点击这条通知的时候,就会执行相应的逻辑
- 现在来优化一下NotificationTest项目,给刚才的通知加上点击的功能,让用户点击这条通知的时候,就可以启动另外一个Activity
- 首先我们需要准备好一个另外的Activity,NotificationActivity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="This is notification layout"
android:textSize="24sp" />
</RelativeLayout>
- 这样就将Notification准备好了,下面修改MainActivity中的代码
//设置按钮的点击事件
sendNotice.setOnClickListener {
val intent = Intent(this, NotificationActivity::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(
resources,
R.drawable.large_icon
)
)
.setContentIntent(pi)
.build()
manager.notify(1, notification)
}
- 可以看到这里先使用了Intent表达出我们想要启动NotificationActivity的意图,然后将构建好的Intent对象传入PendingInent的getActivity()方法当中,以得到PendingIntent的实例,接着在Notification.Builder中调用setContentIntent()方法,把它作为参数传入即可.
- 现在点击弹出的通知之后就可以跳转到我们新创建的Activity上面去了.
- 但是点击完成之后发现通知图标还没有消失,原因是如果我们没有在代码当中对该通知进行取消,它就会一直显示在该系统的状态栏之上,解决的方法有两种
- 第一种方法是:在NotificationCompat.Builder中再连缀一个setAutoCancel()方法
val notification = NotificationCompat.Builder(this, "normal")
...
.setAutoCancel(true)
.build()
-
可以看到当setAutoCancel()方法传入true之后,就表示当点击这个通知的时候,通知会自动取消
-
第二种方法是显示的调用NotificationManager的cancel()方法将它取消.
class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as
NotificationManager
manager.cancel(1)
}
- 这里我们给cancel()方法传入了1,是因为我们在创建通知的时候,给通知指定的id,因此你想要取消哪条通知再cancel()方法中传入该通知的id就可以了.
通知的进阶技巧
setStyle()方法
- 上面的示例都是通知最基本的一些用法,实际上NotificationCompat.Builder中提供了非常丰富的API,以便于我们创建出更多种的通知效果,下面是一些比较常用的API
- setStyle()方法,这个方法允许我们构建出富文本的通知内容,也就是说,通知中不光可以有文字图标,还可以包含更多的东西.setStyle()方法接收一个NotificationCompat.Style参数,这个参数就是用来构建具体富文本信息的,如长文字,图片等.
- 使用之前的方法,如果我们在内容正文当中设置了很多的内容,通知出来的效果是不会全部显示,多余的部分会使用省略号进行代替,这也很正常,因为通知本来就应该言简意赅,多余的部分放在后面的Activity当中进行展示比较合适
- 但是如果真的要在通知当中显示一段比较长的文字,Android也是支持的,通过setStyle()方法就可以做到,具体的写法如下:
.setStyle(NotificationCompat.BigTextStyle().bigText("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz" +
"zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"))
- 就是使用setStyle()方法来代替setContentText()方法,我们在方法当中创建了一个NotificationCompat.BigTextStyle对象,这个对象就是用来封装长文字信息的,只要调用它的bigText()方法就可以将文字内容传入就可以了.
- 除了显示长文字之外还可以显示一张大的图片,这次在参数中创建一个NotificationCompat.BigPictureStyle对象,这个对象就是用于设置大图片的,然后调用它的bigPicture()方法将图片进行传入
.setStyle(
NotificationCompat.BigPictureStyle().bigPicture(
BitmapFactory.decodeResource(resources, R.drawable.large_icon)
)
)
- 事先准备一张图片,通过BitmapFactory的decodeResource()方法将图片解析成Bitmap对象,再传入bigPicture()方法中就可以了
- 以上就是setStyle()方法当中比较重要的内容了
不同重要等级的通知渠道
- 通知渠道的重要等级越高,发出的通知就越容易获得用户的注意,比如高重要等级的通知渠道发出的通知可以弹出横幅,发出声音,而低重要等级的通知渠道发出的通知不仅可能会在某些情况下被隐藏,而且还有可能被改变显示顺序,将其更重要的通知后面.
- 需要注意的是,开发者只能在创建通知渠道的时候为它指定初始的重要等级,如果用户不认可这个重要等级的话,可以随时进行修改,开发者无法再进行调整和修改,因为通知渠道一旦创建就不能再通过代码进行修改了.
- 再创建一个新的通知渠道来进行测试,修改MainActiviy中的代码如下所示
val channel2 =
NotificationChannel("important", "Important", NotificationManager.IMPORTANCE_HIGH)
manager.createNotificationChannel(channel2)
- 这里将通知渠道的重要等级设置成为了高,表示这是一条非常重要的通知,要求用户必须看到
- 运行项目之后可以看到这次的通知不是在系统状态栏显示一个小图标了,而是弹出了一个横幅,并且附带了通知的详细内容,表示这是一条非常重要的通知,不管用户是在玩游戏还是在看电影,这条通知都会显示在最上方,以此引起用户的关注
- 当然在使用这个等级的通知渠道的时候,一定要特别小心,一定要确保这条通知足够的重要,否则就会引起用户的排斥感.