运用手机多媒体
2021.2.24
Gary哥哥的哥哥
运用手机多媒体丰富你的程序
Android提供了一系列的相关API,使得我们在程序当中可以调用很多手机的多媒体资源,从而编写出更加丰富的应用
程序运行在Android手机上
在正式讲解之前,我们先来了解一下,如何将程序运行在Android手机上
这个很简单啦,USB连电脑,手机开发者选项开启调试,然后运行Android Studio的项目代码即可安装到手机上了
下面我们对几个常用的多媒体进行一一讲解
使用通知
这是一个很有特色的功能,连iOS5.0页引入了类似的功能
即便当应用程序不在前台运行的时候,借助通知来发送提示用户
通知渠道
Android 8.0开始,每一个通知都要属于一个相应的渠道,每个应用程序都可以自由地创建当前应用拥有哪些通知渠道,但这些渠道的掌控权在用户的手上。用户可以自由地选择这些通知渠道的重要程度,是否响铃,是否震动或者是否要关闭这个渠道
为此,我们不用再担心一些垃圾通知的骚扰了
- 下面我们来看看创建自己的通知渠道的详细步骤吧
- 首先需要一个NotificationManager对通知进行管理,可以调用Contenx的getSystemService()方法来获取
- getSystemService()
- p:接收一个字符串参数用于确定获取系统的哪个服务,这里我们传入Context.NOTIFICATION_SERVICE即可
- 写法如下
val manager=getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
- 接下来需要使用NotificationCannel类构件一个通知渠道,并调用NotificationManager的createNotificationChannel()方法完成构建
- 由于这些方法都是Android8.0新增的API,我们调用的时候还需要进行版本的判断
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
val channel = NotificationChannel(channelId, channelName, importance)
manager. createNotificationChannel(channel)
}
我这里重点提醒提醒,VERSION_CODES.O(是O不是0)
- 创建一个通知渠道至少需要知道渠道的ID,渠道名称和重要性三个删除
- 渠道的ID可以随便定义,只要保证全局唯一性即可
- 渠道名称是个用户看的,需要可以清楚地表达这个渠道的用途
- 通知重要性等级从高到低:IMPORTANCE_HIGH / DEFAULT / LOW / MIN
- 用户可以随意更改通知的重要性等级,开发者是无法干预的
通知的基本用法
通知的使用方法还是比较灵活的,可以创建在Activity,BroadcastReceiver 和Service当中(Service后面会学习到)。一般创建场景在后面两个常见,Activity里创建通知还是比较少的,因为一般只有当程序进入后台的时候才需要使用通知
我们先来学习一下创建通知的一般步骤
- 首先需要一个Builder构造器来创建Notification对象,AndroidX库提供了一些兼容之前版本的API
- AndroidX提供了一个NotificationCompat类,使用这个类来构建Notification对象就不存在兼容性的问题了
val notification = NotificationCompat.Builder(context, channelId).build()
- NotificationCompat.Builder的构造函数中,p1是一个context;p2是渠道的ID
- 需要我们在创建通知渠道时指定的渠道ID相匹配才行
- 上述代码只是创建了一个空的Notification对象,并没有什么实际的作用, 我们可以在最终的build()方法中连缀人一多的设置方法来创建一个丰富的Notification对象,先来看看一些基本的操作:
val notification=NotificationCompat.Builder(context,channelId)
.setContentTitle("This is content title")
.setContentText("This is content text").setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher_background))
.build()
这里一共调用了四个设置方法:
- 设置Title
- 设置正文内容(下拉状态栏也可以看到这部分的内容)
- 设置通知的小图标
- 注意:这里使用纯alpha图层的图片进行设置,小图标会显示在状态栏上方出现
- 设置大图标,下拉状态栏可以看到大图标
- 以上工作全部完成过后,只需要调用NotificationManager的notify()方法就可以放通知显示出来
- notify()方法接受两个参数:
- p1:id,保证为每个通知指定的id都是不相同的
- p2:Notification对象,这里我们将我们刚刚创建的Notification对象传入即可,因此显示一个通知就写成:
manager.notify(1, notification)
下面我们就以一个完整的例子NotificationTest项目来看看通知到底是长什么样的
activity_main.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Notice"
android:textAllCaps="false"
android:id="@+id/sendNotice"/>
</LinearLayout>
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.0){
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.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.ic_launcher_background))
.build()
manager.notify(1,notification)
}
}
}
下面我们要使点击这条通知之后有效果,这里涉及新的概念PendingIntent
- 和Intent有些类似
PendingIntent用法
它主要提供几个静态方法用于获取PendingIntent的实例
- getActivity()
- getBroadcast()
- getService
他们的参数都是:
- p1:Context
- 一般不用到,传入0即可
- p3:是一个Intent对象,通过这个对象构建出PendingIntent的意图
- p4:用于确定PendingIntent的行为,有FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FLAG_UPDATE_CURRENT,通常情况下传入0即可(至于上面这几个需要自行查阅文档)
下面来优化我们的NotificationTest项目
activity_notification.xml和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:textSize="24sp"
android:text="This is notification layout"/>
</RelativeLayout>
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)
//注意下面setContentIntent(pi)
//setAutoCancel(true) 点击之后自动消失
val notification = NotificationCompat.Builder(this,"normal")
.setContentTitle("This is content title")
.setContentText("This is content text")
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.ic_launcher_background))
.setContentIntent(pi)
.setAutoCancel(true)
.build()
manager.notify(1,notification)
}
}
}
关于点击后自动消失,也可以在NotificationActivity中实现
class NotificationActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_notification)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.cancel(1)//这里的1就是那个通知设定的id
}
}
进阶技巧
实际上NotificationCompat.Builder中还提到了非常丰富的API,以便我们创建出更加多样的通知效果
但我们只讲解一些比较常用的API,其他API可以在大家实践的时候自行查阅资料
setStyle()方法
这个方法允许我们构建富文本的通知内容。也就是说,通知不光可以有文字和图标,还可以包含更多的内容
p:NotificationCompat.Style参数,这个参数就是用来构建具体的富文本信息,如长文字和图片等
ex:如果按我们之前讲的那样子写,如果通知的内容文字太多,他就只会缩略尾部的方式进行显示,而这里可以做到将全部东西显示出来
sendNotice.setOnClickListener {
//构建意图
val intent=Intent(this,NotificationActivity::class.java)
val pi = PendingIntent.getActivity(this,0,intent,0)
//注意下面setContentIntent(pi)
//setAutoCancel(true) 点击之后自动消失
val notification = NotificationCompat.Builder(this,"normal")
.setContentTitle("This is content title")
// .setContentText(" 全国脱贫攻坚总结表彰大会开始,中共中央政治局常委、全国政协主席汪洋同志宣读《中共中央 国务院关于授予全国脱贫攻坚楷模荣誉称号的决定》。\n" +
// "\n" +
// " 为隆重表彰激励先进,大力弘扬民族精神、时代精神和脱贫攻坚精神,充分激发全党全国各族人民干事创业的责任感、使命感、荣誉感,汇聚更强大的力量推进全面建设社会主义现代化国家,党中央、国务院决定,授予毛相林等10名同志,河北省塞罕坝机械林场等10个集体“全国脱贫攻坚楷模”荣誉称号。")
.setStyle(NotificationCompat.BigTextStyle().bigText(" 全国脱贫攻坚总结表彰大会开始,中共中央政治局常委、全国政协主席汪洋同志宣读《中共中央 国务院关于授予全国脱贫攻坚楷模荣誉称号的决定》。\n" +
"\n" +
" 为隆重表彰激励先进,大力弘扬民族精神、时代精神和脱贫攻坚精神,充分激发全党全国各族人民干事创业的责任感、使命感、荣誉感,汇聚更强大的力量推进全面建设社会主义现代化国家,党中央、国务院决定,授予毛相林等10名同志,河北省塞罕坝机械林场等10个集体“全国脱贫攻坚楷模”荣誉称号。"))
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.ic_launcher_background))
.setContentIntent(pi)
// .setAutoCancel(true)
.build()
manager.notify(1,notification)
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qu7DDJ9R-1614257888277)(E:\kotlin-study\Studying-Kotlin\Multimedia\setStyle()].png)
除此之外还可以做到完整的显示一张图片
sendNotice.setOnClickListener {
//构建意图
val intent=Intent(this,NotificationActivity::class.java)
val pi = PendingIntent.getActivity(this,0,intent,0)
//注意下面setContentIntent(pi)
//setAutoCancel(true) 点击之后自动消失
val notification = NotificationCompat.Builder(this,"normal")
.setContentTitle("This is content title")
// .setContentText(" 全国脱贫攻坚总结表彰大会开始,中共中央政治局常委、全国政协主席汪洋同志宣读《中共中央 国务院关于授予全国脱贫攻坚楷模荣誉称号的决定》。\n" +
// "\n" +
// " 为隆重表彰激励先进,大力弘扬民族精神、时代精神和脱贫攻坚精神,充分激发全党全国各族人民干事创业的责任感、使命感、荣誉感,汇聚更强大的力量推进全面建设社会主义现代化国家,党中央、国务院决定,授予毛相林等10名同志,河北省塞罕坝机械林场等10个集体“全国脱贫攻坚楷模”荣誉称号。")
// .setStyle(NotificationCompat.BigTextStyle().bigText(" 全国脱贫攻坚总结表彰大会开始,中共中央政治局常委、全国政协主席汪洋同志宣读《中共中央 国务院关于授予全国脱贫攻坚楷模荣誉称号的决定》。\n" +
// "\n" +
// " 为隆重表彰激励先进,大力弘扬民族精神、时代精神和脱贫攻坚精神,充分激发全党全国各族人民干事创业的责任感、使命感、荣誉感,汇聚更强大的力量推进全面建设社会主义现代化国家,党中央、国务院决定,授予毛相林等10名同志,河北省塞罕坝机械林场等10个集体“全国脱贫攻坚楷模”荣誉称号。"))
.setContentText("Look, what a beautiful picture")
.setStyle(NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.drawable.ic_launcher_background)))
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(resources,R.drawable.ic_launcher_background))
.setContentIntent(pi)
// .setAutoCancel(true)
.build()
manager.notify(1,notification)
}
需要注意的是,开发者只能再创建通知渠道的时候为他制定初始的重要等级,用户不认可,用户可以自行修改,因为通知渠道一旦创建就不能通过代码修改,我们再创建一条通知渠道来测试:
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
val channel = NotificationChannel("normal","Normal",NotificationManager.IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
val channel2=NotificationChannel("important","Important",NotificationManager.IMPORTANCE_HIGH)
manager.createNotificationChannel(channel2)
}
sendNotice.setOnClickListener {
//构建意图
val intent=Intent(this,NotificationActivity::class.java)
val pi = PendingIntent.getActivity(this,0,intent,0)
//注意下面setContentIntent(pi)
//setAutoCancel(true) 点击之后自动消失
val notification = NotificationCompat.Builder(this,"important")
.setContentTitle("This is content title")
...
}
搞个high给他,发现这里已经变成一个弹出式的通知了
调用摄像头和相册
调用摄像头拍照
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="take Photo"
android:id="@+id/takePhotoBtn"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/imageView"/>
</LinearLayout>
MainActivity中:
下面代码确实比较长,详细的解析请见书本p369页
class MainActivity : AppCompatActivity() {
val takePhoto = 1
lateinit var imageUri :Uri
lateinit var outputImage: File
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
takePhotoBtn.setOnClickListener {
//创建File对象,用于存储拍照后的图片
outputImage = File(externalCacheDir,"output_image.jpg")//采用应用管理目录来缓存(避免读写SD卡的危险权限)
if(outputImage.exists()){
outputImage.delete()
}
outputImage.createNewFile()
imageUri = if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N){
//将File对象转化成一个封装过的Uri对象(FileProvider对数据进行了保护)
FileProvider.getUriForFile(this,"com.workaholiclab.cameraalbumtest.fileprovider",outputImage)
}else{
Uri.fromFile(outputImage) //该设备低于android7就调用Uri的fromFile方法将File转话为Uri对象
//这个Uri对象包含了这张图片的真实存在的路径
}
//启动相机程序
val intent =Intent("android.media.action.IMAGE_CAPTURE")
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri)//指定图片的输入地址,这里为刚刚的Uri对象
startActivityForResult(intent,takePhoto)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
takePhoto->{
if (resultCode ==Activity.RESULT_OK){
//将拍摄的照片显示出来
val bitmap= BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri))//将这张图片解析称为Bitmap对象
imageView.setImageBitmap(rotateIfRequired(bitmap))//变成ImageView,需要注意一些手机上拍照转化会发生一些旋转,需要处理一下
}
}
}
}
//照片旋转处理
private fun rotateIfRequired(bitmap: Bitmap): Bitmap {
val exif = ExifInterface(outputImage.path)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)
return when(orientation){
ExifInterface.ORIENTATION_ROTATE_90->rotateBitmap(bitmap,90)
ExifInterface.ORIENTATION_ROTATE_180->rotateBitmap(bitmap,180)
ExifInterface.ORIENTATION_ROTATE_270->rotateBitmap(bitmap,270)
else-> bitmap
}
}
private fun rotateBitmap(bitmap: Bitmap, degree: Int): Bitmap {
val matrix =Matrix()
matrix.postRotate(degree.toFloat())
val rotateBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true)
bitmap.recycle()//将不再需要的Bitmap对象回收
return rotateBitmap
}
}
下面不要忘记注册我们Provider了,FileProvider是一种特殊的Provider
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.workaholiclab.cameraalbumtest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<provider
android:authorities="com.workaholiclab.cameraalbumtest.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
name属性是固定的
authorities属性的值必须和刚才getUriForFile()方法的第二个参数一致
指定Uri的分享路径,冰鞋引用了一个@xml/file_paths资源
file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="/"/>
</paths>
name属性随便写
path属性值表示共享的具体路径,一个/表示将整个SD卡进行分享,当然你也可以这分享存放上面图片的路径
从相册中选取照片
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="take Photo"
android:id="@+id/takePhotoBtn"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="From Album"
android:id="@+id/fromAlbumBtn"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:id="@+id/imageView"/>
</LinearLayout>
class MainActivity : AppCompatActivity() {
val takePhoto = 1
lateinit var imageUri :Uri
lateinit var outputImage: File
val fromAlbum = 2
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
takePhotoBtn.setOnClickListener {
//创建File对象,用于存储拍照后的图片
outputImage = File(externalCacheDir,"output_image.jpg")//采用应用管理目录来缓存(避免读写SD卡的危险权限)
if(outputImage.exists()){
outputImage.delete()
}
outputImage.createNewFile()
imageUri = if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.N){
//将File对象转化成一个封装过的Uri对象(FileProvider对数据进行了保护)
FileProvider.getUriForFile(this,"com.workaholiclab.cameraalbumtest.fileprovider",outputImage)
}else{
Uri.fromFile(outputImage) //该设备低于android7就调用Uri的fromFile方法将File转话为Uri对象
//这个Uri对象包含了这张图片的真实存在的路径
}
//启动相机程序
val intent =Intent("android.media.action.IMAGE_CAPTURE")
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri)//指定图片的输入地址,这里为刚刚的Uri对象
startActivityForResult(intent,takePhoto)
}
//选取相册
fromAlbumBtn.setOnClickListener {
//打开文件选择器
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
//指定只显示图片
intent.type ="image/*"
startActivityForResult(intent,fromAlbum)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when(requestCode){
takePhoto->{
if (resultCode ==Activity.RESULT_OK){
//将拍摄的照片显示出来
val bitmap= BitmapFactory.decodeStream(contentResolver.openInputStream(imageUri))//将这张图片解析称为Bitmap对象
imageView.setImageBitmap(rotateIfRequired(bitmap))//变成ImageView,需要注意一些手机上拍照转化会发生一些旋转,需要处理一下
}
}
fromAlbum->{
if(resultCode == Activity.RESULT_OK && data != null){
data.data?.let {
uri ->
//将选择的图片显示
val bitmap=getBitmapFromUri(uri)
imageView.setImageBitmap(bitmap)
}
}
}
}
}
private fun getBitmapFromUri(uri: Uri) = contentResolver
.openFileDescriptor(uri,"r")?.use {
BitmapFactory.decodeFileDescriptor(it.fileDescriptor)
}
//照片旋转处理
private fun rotateIfRequired(bitmap: Bitmap): Bitmap {
val exif = ExifInterface(outputImage.path)
val orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL)
return when(orientation){
ExifInterface.ORIENTATION_ROTATE_90->rotateBitmap(bitmap,90)
ExifInterface.ORIENTATION_ROTATE_180->rotateBitmap(bitmap,180)
ExifInterface.ORIENTATION_ROTATE_270->rotateBitmap(bitmap,270)
else-> bitmap
}
}
private fun rotateBitmap(bitmap: Bitmap, degree: Int): Bitmap {
val matrix =Matrix()
matrix.postRotate(degree.toFloat())
val rotateBitmap = Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true)
bitmap.recycle()//将不再需要的Bitmap对象回收
return rotateBitmap
}
}
需要注意的是,一般的程序对加载的图片压缩,这样子是为了避免直接加载到内存崩溃,好的做法是先压缩,这里大家可以自行查阅相关资料了
播放多媒体文件
Android MediaPlayer 常用方法介绍
方法:create(Context context, Uri uri)
解释:静态方法,通过Uri创建一个多媒体播放器。方法:create(Context context, int resid)
解释:静态方法,通过资源ID创建一个多媒体播放器方法:create(Context context, Uri uri, SurfaceHolder holder)
解释:静态方法,通过Uri和指定 SurfaceHolder 【抽象类】 创建一个多媒体播放器方法: getCurrentPosition()
解释:返回 Int, 得到当前播放位置方法: getDuration()
解释:返回 Int,得到文件的时间方法:getVideoHeight()
解释:返回 Int ,得到视频的高度方法:getVideoWidth()
解释:返回 Int,得到视频的宽度方法:isLooping()
解释:返回 boolean ,是否循环播放方法:isPlaying()
解释:返回 boolean,是否正在播放方法:pause()
解释:无返回值 ,暂停方法:prepare()
解释:无返回值,准备同步方法:prepareAsync()
解释:无返回值,准备异步方法:release()
解释:无返回值,释放 MediaPlayer 对象方法:reset()
解释:无返回值,重置 MediaPlayer 对象方法:seekTo(int msec)
解释:无返回值,指定播放的位置(以毫秒为单位的时间)方法:setAudioStreamType(int streamtype)
解释:无返回值,指定流媒体的类型方法:setDataSource(String path)
解释:无返回值,设置多媒体数据来源【根据 路径】方法:setDataSource(FileDescriptor fd, long offset, long length)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】方法:setDataSource(FileDescriptor fd)
解释:无返回值,设置多媒体数据来源【根据 FileDescriptor】方法:setDataSource(Context context, Uri uri)
解释:无返回值,设置多媒体数据来源【根据 Uri】方法:setDisplay(SurfaceHolder sh)
解释:无返回值,设置用 SurfaceHolder 来显示多媒体方法:setLooping(boolean looping)
解释:无返回值,设置是否循环播放事件:setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener)
解释:监听事件,网络流媒体的缓冲监听事件:setOnCompletionListener(MediaPlayer.OnCompletionListener listener)
解释:监听事件,网络流媒体播放结束监听事件:setOnErrorListener(MediaPlayer.OnErrorListener listener)
解释:监听事件,设置错误信息监听事件:setOnVideoSizeChangedListener(MediaPlayer.OnVideoSizeChangedListener listener)
解释:监听事件,视频尺寸监听方法:setScreenOnWhilePlaying(boolean screenOn)
解释:无返回值,设置是否使用 SurfaceHolder 显示方法:setVolume(float leftVolume, float rightVolume)
解释:无返回值,设置音量方法:start()
解释:无返回值,开始播放方法:stop()
解释:无返回值,停止播放
我们先来梳理一下MediaPlayer的工作流程
- 创建MediaPlayer对象
- 调用setDataSource()方法设置音频文件的路径
- 再调用prepare()方法进入准备状态
- start(),pause(),reset()方法
我们来看一个很简单的例子吧
这部分比较简单不再展开来说了,有兴趣自己看一下书p374页即可
播放视频
VedioView和MediaPlayer很类似,其主要方法如下:
- setVideoPath:设置要播放的视频文件的位置
- start:开始或继续播放视频
- pause:暂停播放视频
- resume:将视频从头开始播放
- seekTo:从指定的位置开始播放视频
- isPlaying:判断当前是否正在播放视频
- getCurrentPosition:获取当前播放的位置
- getDuration:获取载入的视频文件的时长
- setVideoPath(String path):以文件路径的方式设置VideoView播放的视频源
- setVideoURI(Uri uri):以Uri的方式设置视频源,可以是网络Uri或本地Uri
- setMediaController(MediaController controller):设置MediaController控制器
- setOnCompletionListener(MediaPlayer.onCompletionListener l):监听播放完成的事件
- setOnErrorListener(MediaPlayer.OnErrorListener l):监听播放发生错误时候的事件
- setOnPreparedListener(MediaPlayer.OnPreparedListener l):监听视频装载完成的事件
同样,这一部分比较简单详见书本p378页即可
多媒体部分完更