使用通知
通知的基本用法
通知(notifification)是Android系统中比较有特色的一个功能,当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。发出一条通知后,手机最上方的状态栏中会显示一个通知的图标,下拉状态栏后可以看到通知的详细内容
通知渠道是每条通知都要属于一个对应的渠道。每个应用程序都可以自由地创建当前应用拥有哪些通知渠道,但是这些通知渠道的控制权是掌握在用户手上的。用户可以自由地选择这些通知渠道的重要程度,是否响铃、是否振动或者是否要关闭这个渠道的通知
使用Android studio新建一个NotifificationTest项目
修改activity_main.xml中的代码,添加一个用于发出通知的按钮
修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
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)
manager.createNotificationChannel(channel)
}
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)
.setAutoCancel(true)
.build()
manager.notify(1, notification)
}
}
}
首先需要一个NotifificationManager对通知进行管理,可以通过调用Context的getSystemService()方法获取。getSystemService()方法接收一个字符串参数用于确定获取系统的哪个服务,这里传入Context.NOTIFICATION_SERVICE即可
接下来要使用NotificationChannel类构建一个ID为normal通知渠道,并调用NotifificationManager的createNotificationChannel()方法完成创建。由于NotificationChannel类和createNotificationChannel()方法都是Android 8.0系统中新增的API,因此在使用的时候还需要进行版本判断才可以
创建一个通知渠道至少需要渠道ID、渠道名称以及重要等级这3个参数,其中渠道ID可以随便定义,只要保证全局唯一性就可以。渠道名称是给用户看的,需要可以清楚地表达这个渠道的用途。
通知的重要等级主要有IMPORTANCE_HIGH、IMPORTANCE_DEFAULT、
IMPORTANCE_LOW、IMPORTANCE_MIN这几种,对应的重要程度依次从高到低。当然这里只是初始状态下的重要等级,用户可以随时手动更改某个通知渠道的重要等级,开发者是无法干预的
然后在“Send Notice”按钮的点击事件里完成了通知的创建工作,首先需要使用NotificationCompat类的构造器创建Notification对象;NotificationCompat.Builder的构造函数中接收两个参数:
- 第一个参数是context;
- 第二个参数是渠道ID,需要和在创建通知渠道时指定的渠道ID相匹配才行
在最终的build()方法之前可以连缀任意多的设置方法来创建一个丰富的Notification对象:
- setContentTitle()方法用于指定通知的标题内容,下拉系统状态栏就可以看到这部分内容
- setContentText()方法用于指定通知的正文内容,同样下拉系统状态栏就可以看到这部分内容
- setSmallIcon()方法用于设置通知的小图标,注意,只能使用纯alpha图层的图片进行设置,小图标会显示在系统状态栏上
- setLargeIcon()方法用于设置通知的大图标,当下拉系统状态栏时,就可以看到设置的大图标了
- setAutoCancel(true)方法可以对通知进行取消,消除系统状态上的通知图标
以上工作都完成之后,需要调用NotifificationManager的notify()方法让通知显示出来
notify()方法接收两个参数:
- 第一个参数是id,要保证为每个通知指定的id都是不同的
- 第二个参数则是Notification对象
运行程序,然后点击“Send Notice”按钮,会在系统状态栏的最左边看到一个小图标
下拉系统状态栏可以看到该通知的详细信息
实现通知的点击效果:PendingInten
它主要提供了几个静态方法用于获取PendingIntent的实例,可以根据需求来选择是使用getActivity()方法、getBroadcast()方法,还是getService()方法。这几个方法所接收的参数都是相同的:
- 第一个参数是Context上下文
- 第二个参数一般用不到,传入0即可
- 第三个参数是一个Intent对象,可以通 过这个对象构建出PendingIntent的“意图”
- 第四个参数用于确定PendingIntent的行为,有 FLAG_ONE_SHOT、FLAG_NO_CREATE、FLAG_CANCEL_CURRENT和FLAG_UPDATE_CURRENT这4种值可选,通常情况下这个参数传入0
新建新建另一个NotifificationActivity
修改activity_notifification.xml中的代码,添加一个TextView控件
修改MainActivity中的代码,给通知加入点击功能
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
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)
}
}
}
NotificationCompat.Builder构造器可以调用一个setContentIntent()方法,接收的参数正是一个PendingIntent对象。因此,这里就可以通过PendingIntent构建一个延迟执行的“意图”,当用户点击这条通知时就会执行相应的逻辑
这里用户点击的时候可以启动另一个Activity
点击一下该通知,就会打开NotifificationActivity的界面了
通知的进阶技巧
setStyle()方法
-
这个方法允许构建出富文本的通知内容
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()
这里使用了setStyle()方法替代setContentText()方法。在setStyle()方法中,创建了一个NotificationCompat.BigTextStyle对象,这个对象就是用于封装长文字信息的,只要调用它bigText()方法并将文字内容传入就可以了
再次重新运行程序并触发通知
-
除了显示长文字之外,通知里还可以显示一张大图片
val notification = NotificationCompat.Builder(this, "normal") ... .setStyle(NotificationCompat.BigPictureStyle().bigPicture( BitmapFactory.decodeResource(resources, R.drawable.big_image))) .build()
这里仍然是调用的setStyle()方法,这次在参数中创建了一个 NotificationCompat.BigPictureStyle对象,这个对象就是用于设置大图片的,然后调用它的bigPicture()方法并将图片传入
这里事先准备好了一张图片,通过BitmapFactory的decodeResource()方法将图片解析成Bitmap对象,再传入bigPicture()方法中就可以了重新运行一下程序并触发通知
播放多媒体文件
播放音频
在Android中播放音频文件一般是使用MediaPlayer类实现的,它对多种格式的音频文件提供了非常全面的控制方法,从而使播放音乐的工作变得十分简单。可用于播放网络、本地以及应用程序安装包中的音频
下表列出了MediaPlayer类中一些较为常用的控制方法
使用Android studio新建一个PlayAudioTest项目
修改activity_main.xml中的代码,添加3个按钮,分别用于对音频文件进行播放、暂停和停止操作
Android Studio允许我们在项目工程中创建一个assets目录,并在这个目录下存放任意文件和子目录,这些文件和子目录在项目打包时会一并被打包到安装文件中,然后在程序中就可以借助AssetManager这个类提供的接口对assets目录下的文件进行读取;不过assets目录必须创建在app/src/main这个目录下面。创建好目录并把提前准备好的音频放入
修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
private val mediaPlayer = MediaPlayer()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initMediaPlayer()
play.setOnClickListener {
if (!mediaPlayer.isPlaying) {
mediaPlayer.start() // 开始播放
}
}
pause.setOnClickListener {
if (mediaPlayer.isPlaying) {
mediaPlayer.pause() // 暂停播放
}
}
stop.setOnClickListener {
if (mediaPlayer.isPlaying) {
mediaPlayer.reset() // 停止播放
initMediaPlayer()
}
}
}
private fun initMediaPlayer() {
val assetManager = assets
val fd = assetManager.openFd("music.mp3")
mediaPlayer.setDataSource(fd.fileDescriptor, fd.startOffset, fd.length)
mediaPlayer.prepare()
}
override fun onDestroy() {
super.onDestroy()
mediaPlayer.stop()
mediaPlayer.release()
}
}
在类初始化的时候,就先创建了一个MediaPlayer的实例,然后在onCreate()方法中调用initMediaPlayer()方法,为MediaPlayer对象进行初始化操作
在initMediaPlayer()方法中,首先通过getAssets()方法得到了一个AssetManager的实例,AssetManager可用于读取assets目录下的任何资源
接着我们调用了openFd()方法将音频文件句柄打开,后面又依次调用了setDataSource()方法和prepare()方法,为MediaPlayer做好了播放前的准备
接下来看一下各个按钮的点击事件中的代码
- 当点击“Play”按钮时会进行判断,如果当前MediaPlayer没有正在播放音频,则调用start()方法开始播放
- 当点击“Pause”按钮时会判断,如果当前MediaPlayer正在播放音频,则调用pause()方法暂停播放
- 当点击“Stop”按钮时会判断,如果当前MediaPlayer正在播放音频,则调用reset()方法将MediaPlayer重置为刚刚创建的状态,然后重新调用一遍initMediaPlayer()方法
最后在onDestroy()方法中,还需要分别调用stop()方法和release()方法,将与MediaPlayer相关的资源释放掉
运行程序:
点击一下“Play”按钮,音乐就会响起,然后点击“Pause”按钮,音乐就会停住,再次点击“Play”按钮,会接着暂停之前的位置继续播放。这时如果点击一下“Stop”按钮,音乐也会停住,但是当再次点击“Play”按钮时,音乐就会从头开始播放了
播放视频
播放视频文件是使用VideoView类来实现的。这个类将视频的显示和控制集于一身,仅仅借助它就可以完成一个简易的视频播放器。VideoView的用法和MediaPlayer也比较类似
下表列出了VideoView类中一些较为常用的控制方法
新建PlayVideoTest项目
修改activity_main.xml中的代码,同样添加了3个按钮,分别用于控制视频的播放、暂停和重新播放。另外在按钮的下面又放置了一个VideoView,稍后的视频就将在这里显示
因为VideoView控件并不支持播放放assets目录下的视频资源,所以需要在res目录下创建一个raw目录,音频、视频之类的资源也是可以放在这里的。然后把提前准备好的mp4视频放进目录
修改MainActivity中的代码
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val uri = Uri.parse("android.resource://$packageName/${R.raw.video}")
videoView.setVideoURI(uri)
play.setOnClickListener {
if (!videoView.isPlaying) {
videoView.start() // 开始播放
}
}
pause.setOnClickListener {
if (videoView.isPlaying) {
videoView.pause() // 暂停播放
}
}
replay.setOnClickListener {
if (videoView.isPlaying) {
videoView.resume() // 重新播放
}
}
}
override fun onDestroy() {
super.onDestroy()
videoView.suspend()
}
}
和前面播放音频的代码比较类似。首先在onCreate()方法中调用了Uri.parse()方法,将raw目录下的video.mp4文件解析成了一个Uri对象,这里使用的写法是Android要求的固定写法
然后调用VideoView的setVideoURI()方法将刚才解析出来的Uri对象传入,这样VideoView就初始化完成了
各个按钮的点击事件:
- 当点击“Play”按钮时会判断,如果当前没有正在播放视频,则调用start()方法开始播放
- 当点击“Pause”按钮时会判断,如果当前视频正在播放,则调用pause()方法暂停播放
- 当点击“Replay”按钮时会判断,如果当前视频正在播放,则调用resume()方法从头播放视频
最后在onDestroy()方法中,还需要调用一下suspend()方法,将VideoView所占用的资源释放掉
运行程序,点击一下“Play”按钮,就可以看到视频已经开始播放了,点击“Pause”按钮可以暂停视频的播放,点击“Replay”按钮可以从头播放视频