Android 学习 09 DAY
【手机多媒体】通知、媒体调用
将程序运行到手机上
略
使用通知
通知的基本用法
通知的用法比较灵活,既可以在活动里创建,也可以在广播接收器里创建,相比于广播接收器和服务,在活动里创建通知的场景还是比较少的,一般只有当程序进入后台的时候我们才需要使用通知。不过,不论在哪里创建通知,整体的步骤都是相同的,下面我们就来学习一下创建通知的详细步骤。首先需要一个 NotificationManager 来对通知进行管理,可以调用 Context 的 getSystemService() 方法获取到。 getSystemService() 方法接收一个字符串参数用于确定获取系统的那个服务,这里我们传入 Context.NOTIFICATION_SERVICE 即可。因此获取 NotificationManager 的实例就可以写成:
NotificationManager manager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
接下来需要使用一个 Builder 构造器来创建 Notification 对象
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("this is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pi)
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
setWhen() 方法用于指定通知被创建的时间,一毫秒为单位。
以上工作都完成后,只需调用 NotificationManager 的 notify() 方法就能让通知显示出来了。notify() 方法接收两个参数,一个参数是 id,要保证为每个通知所指定的 id 都是不同的,第二个参数则是 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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/send_notice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send notice" />
</LinearLayout>
添加一个按钮,用于发出通知。下面修改 MainActivity.java 中的代码,如下
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendNotice = (Button) findViewById(R.id.send_notice);
sendNotice.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_notice:
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("this is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
manager.notify(1, notification);
break;
default:
break;
}
}
}
现在运行程序,点击按钮,就会看到系统状态栏中出现了一个通知。下面我们要为这个通知添加一个点击响应,这就涉及了一个新的概念:PendingIntent。
PendingIntent 于 Intent 有些相似,不同的是,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 这个构造器还可以再连缀一个 setContentIntent() 方法,接收的参数是一个 PendingIntent 对象。这里就可以通过 PendingIntent 对象构建出一个延迟执行的意图。
新建一个活动命名为 NotificationActivity 并添加一个布局命名为 notification_layout:
NotificationActivity.java
public class NotificationActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notification_layout);
NotificationManager manager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(1);
}
}
notification_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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=".NotificationActivity">
<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.java 代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button sendNotice = (Button) findViewById(R.id.send_notice);
sendNotice.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.send_notice:
Intent intent =
new Intent(this, NotificationActivity.class);
PendingIntent pi =
PendingIntent.getActivity(this, 0, intent, 0);
NotificationManager manager =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Notification notification =
new NotificationCompat.Builder(this)
.setContentTitle("this is content title")
.setContentText("This is content text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pi)
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
manager.notify(1, notification);
break;
default:
break;
}
}
}
点击响应清除通知还能在 NotificationCompat.Builder 这个构造器后面连缀一个 setAutoCancel(true) 来实现
Notification notification = new NotificationCompat.Builder(this)
...
.setAutoCancel(true)
.build();
通知的进阶技巧
实际上 NotificationCompat.Builder 中提供了丰富的 API 能够创建出更加多样的通知效果,下面将介绍一些比较常用的 API。
- setSound() 方法 :可以在通知发出的时候播放一段音频,接收一个 Uri 参数,在指定音频文件的时候还需要先获取到音频文件对应的 URI。例如:
Notification notification = new NotificationCompat.Builder(this)
...
.setSound(Uri.fromFile(
new File("/system/media/audio/ringtones/Luna.ogg")))
.build();
除了允许播放音频外,我们还可以在通知到来的时候让手机进行振动,使用的是 vibrate 这个属性。它是一个长整型的数组,用于设置手机静止和振动的时长,一毫秒为单位。下标为 0 的值表示手机静止的时长,下标为 1 的值表示手机振动的时长,下标为2的值又表示手机静止的时长,以此类推。例:让手机在通知到来的时候立刻振动1秒,然后静止1秒,再振动1秒:
Notification notification = new NotificationCompat.Builder(this)
...
.setVibrate(new long[]{ 0, 1000, 1000, 1000 })
.build();
不过,想要控制手机振动还需要声明权限。因此,还需要编辑 AndroidManifest.xml 加入以下声明:
<uses-permission android:name="android.permission.VIBRATE" />
通知的高级功能
- setStyle() 方法 :这个方法可以构建出富文本通知,也就是说通知中不光可以有文字和图标,还可以包含更多的东西。setStyle() 方法接收一个 NotificationCompat.Style 参数,这个参数就是用来构建具体的富文本信息的,如长文字、图片等。
长文字
在 setStyle() 方法中创建一个 NotificationCompat.BigTextStyle 对象,这个对象就是用于封账长文字信息的,我们调用它的 bigText() 方法并将文字传入就可以了。
Notification notification = new NotificationCompat.Builder(this)
...
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("...................................\
....................................\
...................................."))
.build();
除了显示长文字,通知里还可以显示一张大图片,具体用法也是基本相似:
Notification notification = new NotificationCompat.Builder(this)
...
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(BitmapFactory.decodeResource(getResources(),
R.drawable.big_image)))
.build();
- setPriority() 方法接收一个整型参数用于设置这条通知的重要程度,一共有5个常量值可选:
PRIORITY_DEFAULT:表示默认重要程度,和不设置是一样的;
PRIORITY_MIN:表示最低的重要程度,系统可能只会在特定场景才显示这条通知;
PRIORITY_LOW:表示较低的重要程度,系统可能会将这类通知缩小,或改变显示顺序,将其排在更重要的通知之后;
PRIORITY_HIGH:表示较高的重要程度,系统可能会将这类通知放大或改变其显示顺序,并将其排在比较靠前的位置;
PRIORITY_MAX:表示最高的重要程度,这类通知必须要让用户立刻看到,甚至需要用户做出响应;
Notification notification =
new NotificationCompat.Builder(this)
...
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
调用摄像头和相册
新建一个项目 CameraAlbumTest ,然后修改 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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/take_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Take Photo" />
<Button
android:id="@+id/choose_from_album"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Choose From Album" />
<ImageView
android:id="@+id/picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
添加了两个 Button 和一个 ImageView。Button 是分别用于打开摄像头和相册的,ImageView 是用于将图片显示出来的。然后开始编写摄像头的具体逻辑,修改 MainActivity.java 中的代码。
public class MainActivity extends AppCompatActivity {
public static final int TAKE_PHOTO = 1;
public static final int CHOOSE_PHOTO = 2;
private ImageView picture;
private Uri imageUri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button takePhoto = (Button) findViewById(R.id.take_photo);
Button chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);
picture = (ImageView) findViewById(R.id.picture);
takePhoto.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
File outputImage =
new File(getExternalCacheDir(), "output_image.jpg");
try {
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
imageUri = FileProvider.getUriForFile(
MainActivity.this,
"com.example.cameraalbumtest.fileprovider",
outputImage);
Intent intent =
new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
}
});
chooseFromAlbum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE }, 1);
} else {
openAlbum();
}
}
});
}
private void openAlbum() {
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
}
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permission, int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
openAlbum();
} else {
Toast.makeText(this, "You denied the permission",
Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case TAKE_PHOTO:
if (resultCode == RESULT_OK) {
try {
Bitmap bitmap = BitmapFactory.decodeStream(
getContentResolver().openInputStream(imageUri));
picture.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
break;
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK) {
handleImageOmKitKat(data);
}
default:
break;
}
}
@TargetApi(19)
private void handleImageOmKitKat(Intent data) {
String imagePath = null;
Uri uri = data.getData();
if (DocumentsContract.isDocumentUri(this, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
if("com.android.providers.media.documents".equals(uri.getAuthority())) {
String id = docId.split(":")[1];
String selection = MediaStore.Images.Media._ID + "=" + id;
imagePath = getImagePath(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
} else if("com.android.providers.dowloads.documents".equals(
uri.getAuthority())) {
Uri contentUri = ContentUris.withAppendedId( Uri.parse(
"content://downloads/public_downloads"), Long.valueOf(docId));
imagePath = getImagePath(contentUri, null);
}
} else if("content".equalsIgnoreCase(uri.getScheme())) {
imagePath = getImagePath(uri, null);
} else if("file".equalsIgnoreCase(uri.getScheme())) {
imagePath = uri.getPath();
}
displayImage(imagePath);
}
private void displayImage(String imagePath) {
if (imagePath != null) {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
picture.setImageBitmap(bitmap);
} else {
Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
}
}
private String getImagePath(Uri uri, String selection) {
String path = null;
Cursor cursor = getContentResolver()
.query(uri, null, selection, null, null);
if (cursor != null) {
if(cursor.moveToFirst()) {
path = cursor.getString(
cursor.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
}
return path;
}
}
上述代码中调用相机部分,首先创建了一个 File 对象用于存放摄像头拍下的照片,我们把图片命名为output_image.jpg ,并将它存放在手机应用相关联的缓存目录下。调用 getExternalCacheDir() 方法可以得到这个目录,接着调用getUrifroFile() 方法接收三个参数,第一个参数要求是 Context 对象,第二个参数可以是任意唯一的字符串,第三个参数是刚刚创建的 File 对象。
接下来构建出一个 Intent 对象,并将这个 Intent 的 action 指定为 android.media.action.IMAGE_CAPTURE在,再调用 Intent 的 putExtra() 方法指定图片的输出地址,这里填入刚刚得到的 Uri 对象,最后调用 startActivityForResult() 来启动活动。由于我们使用的是一个隐式 Intent, 系统会找出能够响应这个 Intent 的活动去启动,拍下来的照片将会输出到 output_image.jpg 中。
由于 startActivityForResult() 是会回调 onActivityResult() 的,因此可以添加一条分支,如果发现拍照成功,就可以调用 BitmapFactory 的 decodeStream() 方法将 output_image.jpg 这张照片解析成 Bitmap 对象,然后把它设置到 ImageView 中显示出来。
对于图库的使用,我们需要动态申请 WRITE_EXTERNAL_STORAGE 这个危险权限,因为需要从 SD 卡中读取照片就需要申请这个权限。WRITE_EXTERNAL_STORAGE 表示同时授权程序对 SD 卡读和写的能力。当用户授权了权限申请之后会调用 openAlbum() 方法,这里先是构建了一个 Intent 对象,并将它的 action 指定为 android.intent.action.GET_CONTENT。接着给这个 Intent 对象设置一些必要的参数,然后调用 startActivityForResult() ,这样我们在 onActivityResult() 中添加图库调用的分支,如果读取图片成功,就执行 handleImageOmKitKat() 方法,这个方法中的逻辑基本内容是解析这个封装过的 Uri 。如果返回的 Uri 是 document 类型的话,那就取出 document id 进行处理,如果不是的话,那就使用普通的方式处理。另外,如果的 authority 是 media 格式的话,document id 还需要再进行一次解析,要通过字符串分割的方式取出后半部分才能得到真正的数字 id。取出的 id 用于构建新的 Uri 和条件语句,把这些值作为参数传入到 getImagePath() 方法中就可以获取到图片的真实路径了。拿到图片的路径之后,再调用 displayImage() 方法将图片显示到界面上。
接下来需要在 AndroidManifest.xml 中注册相关权限和内容提供器
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cameraalbumtest">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<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/Theme.CameraAlbumTest">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:authorities="com.example.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>
</application>
</manifest>
其中,android:name 属性值是固定的, android:authorities 属性值必须要和刚才 FileProvider.getURIForFile() 方法中的第二个参数一致。另外在 <provider> 标签的内部使用 <meta-data> 来指定 Uri 的共享路径,并引用了一个 @xml/file_paths 资源。当然,这个资源现在是不存在的,马上来创建它
/res/xml/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>
其中,external-path 就是用来指定 Uri 共享的,name 属性可以随意填,path 属性的值表示共享的具体路径。这设置为空值就是表示将整个 SD 卡进行共享,当然你也可以仅共享我们存放 output_image.jpg 这张图片的路径。
运行程序,点击两个按钮测试,均可正常操作。
播放多媒体文件
播放音频
在 Android 中音频文件一般都是使用 MediaPlayer 类来实现的,它对多种格式的音频文件提供了非常全面的控制方法,从而使得播放音乐的工作变动十分简单。下表列出了 MediaPlayer 类中一些较为常用的控制方法。
方法名 | 功能描述 |
---|
setDataSource() | 设置要播放的音频文件的位置 | prepare() | 在开始播放之前调用这个方法完成准备工作 | start() | 开始或继续播放音频 | pause() | 暂停播放音频 | reset() | 将MediaPlayer对象重置到刚刚创建的状态 | seekTo() | 从指定的位置开始播放音频 | stop() | 停止播放音频。调用这个方法后的MediaPlayer对象无法在播放音频 | release() | 释放掉与MediaPlayer对象相关的资源 | isPlaying() | 判断当前MediaPlayer是否正在播放音频 | getDuration() | 获取载入的音频文件的时长 |
MediaPlayer 的工作流程。首先需要创建一个 MediaPlayer 对象,然后调用 setDataSource() 方法来设置音频文件的路径,再调用 pause() 方法就会暂停播放,调用 reset() 就会停止播放。
新建一个 PlayAudioTest 项目,然后修改 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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Play" />
<Button
android:id="@+id/pause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Pause" />
<Button
android:id="@+id/stop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Stop" />
</LinearLayout>
然后修改 MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MediaPlayer mediaPlayer = new MediaPlayer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button play = (Button) findViewById(R.id.play);
Button pause = (Button) findViewById(R.id.pause);
Button stop = (Button) findViewById(R.id.stop);
play.setOnClickListener(this);
pause.setOnClickListener(this);
stop.setOnClickListener(this);
if(ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
initMediaPlayer();
}
}
private void initMediaPlayer() {
try {
File file = new File(Environment.getDownloadCacheDirectory(),
"music.mp3");
mediaPlayer.setDataSource(file.getPath());
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
if(grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initMediaPlayer();
} else {
Toast.makeText(this, "拒绝权限无法使用",
Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.play:
if(!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
break;
case R.id.pause:
if(!mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
break;
case R.id.stop:
if(!mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
}
}
}
最后在 AndroidManifest.xml 中添加权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.playaudiotest">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
</manifest>
播放视频
播放视频与播放音频类似,主要是使用 VideoView 类来实现的。这个类将视频的显示和控制集于一身,使得我们仅仅借助它就可以完成一个建议的视频播放器。主要用法如下:
方法名 | 功能描述 |
---|
setVideoPath() | 设置要播放的视频文件的位置 | start() | 开始或继续播放视频 | pause() | 暂停播放视频 | seekTo() | 从指定的位置开始播放视频 | isPlaying() | 判断当前是否正在播放视频 | getDuration() | 获取载入的视频文件的时长 | suspend() | 释放资源 |
新建项目 PlayVideoTest 项目,然后修改 activity_main 中的代码
接下修改 MainActivity.java 代码
最后在 AndroidManif.xml 添加权限
最后运行程序,符合预期。
|