第二行代码学习笔记——第八章:丰富你的程序——运行手机多媒体

本章要点

Android强大的多媒体功能。


8.1 将手机运行到手机上

运行程序到手机上:先通过数据线把手机连接到电脑上。然后进入设置—>开发者选项界面,勾选这个界面中的USB选项。

注意:Android 4.2 系统开始,开发者选项默认是隐藏的。进入到“关于手机”界面,连续点击最下面的版本号,就会让开发者模式显示出来。

观察Android Monitor,你就会发现刚刚连接上的手机,如图:
am

运行当前项目,这时不会将程序运行到模拟器或手机上,而是弹出选择对话框,如下:
sdt

选择我们的手机,点击OK,就将程序运行到手机上了。


8.2 使用通知

通知(Notification)是Android中比较有特色的功能,当程序向用户发送提示信息,手机上方的状态栏就会显示一个通知图标,下拉就可以看到通知的详情内容,就连IOS在5.0版本之后也加入了类似的功能。

8.2.1 通知的基本用法

通知的使用方法,它可以在活动(较少),广播接收器和服务(较多)中创建。因为只有当程序进入后台我们才需要通知。

创建通知的步骤: 首先调用Context中的getSystemService()方法获取NotificationManager对通知进行管理。getSystemService()接收字符串(Context.NOTIFICATION.SERVICE),确定系统获取的是那个服务。
获取NotificationManager的实例如下:

NotificationManager manager=(NotificationManager)
getSystemService(Context.NOTIFICATION.SERVICE);

接下来使用support-v4中提供的NotificationCompat.Builder来创建Notification对象(兼容所有Android系统),代码如下:

Notification notification=new NotificationCompat.Builder(context).build();

在build()方法之前连缀设置方法创建一个Notification对象,最基本的设置:

Notification notification=new NotificationCompat.Builder(context)
         .setContentTitle("This is content title")
         .setContentText("This is content text")
         .setWhen(System.currentTimeMillis())
         .setSmallIcon(R.drawable.small_icon)
         .setLargeIcon(BitmapFactory.decodeResource(getResources(),
             R.drawable.large_icon))
         .build();

显示通知:调用NotificationManager中的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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_send_notice"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Send notice"
        android:textAllCaps="false" />


</LinearLayout>

接下来修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_send_notice;
    private NotificationManager manager;
    private Notification notification;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_send_notice= (Button) findViewById(R.id.btn_send_notice);
        btn_send_notice.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_send_notice:
                manager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                notification=new NotificationCompat.Builder(MainActivity.this)
                        .setContentTitle("This is content title")
                        .setContentText("This is content text")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                                R.mipmap.ic_launcher))
                        .build();
                manager.notify(1,notification);
                break;
            default:
        }
    }
}

运行程序,点击Send notice按钮,你会在系统状态栏看到一个小的图标,如图:
iconn
下拉系统状态栏可看到该通知的详情信息,如图:
contentn

实现通知点击效果:PendingIntent。
Intent和PendingIntent的区别:
共同点:启动活动,启动服务以及发送广播。
不同点:Intent更加倾向于立即执行某个动作,PendingIntent更加倾向于在某个合适的时机去执行某个动作。 PendingIntent为延迟执行的Intent。

PendingIntent提供了getActivity(),getBroadcast(),getService()静态方法来获取PendingIntent的实例(根据需求)。这几个方法接收的参数都是相同的,第一个参数Context。第二个参数传入0即可。第三个参数是Intent对象,通过这个对象构建出PendingIntent的”意图”。第四个参数确定PendingIntent的行为,有FLAG_ONE_SHOT,FLAG_NO_CREATE,FLAG_CANCEL_CURRENT,FALG_UPDATE_CURRENT这4中值,一般传入0即可。

NotificationCompat.Builder。构造器连缀setContentIntent()方法传入PendingIntent对象。

点击通知启动另一个活动。

创建NotificationActivity,布局起名为notification_layout。修改notification_layout.xml文件,代码如下:

<?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:text="This is notification layout"
        android:layout_centerInParent="true"
        android:textSize="24sp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</RelativeLayout>

修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_send_notice;
    private NotificationManager manager;
    private Notification notification;
    private PendingIntent pi;
    private Intent intent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_send_notice= (Button) findViewById(R.id.btn_send_notice);
        btn_send_notice.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_send_notice:
                intent=new Intent(this,NotificationActivity.class);
                pi=PendingIntent.getActivity(this,0,intent,0);
                manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                notification=new NotificationCompat.Builder(this)
                        .setContentTitle("This is content title")
                        .setContentText("This is content text")
                        .setWhen(System.currentTimeMillis())
                        .setSmallIcon(R.mipmap.ic_launcher)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                                R.mipmap.ic_launcher))
                        .setContentIntent(pi)
                        .build();
                manager.notify(1,notification);
                break;
            default:
                break;
        }
    }
}

运行程序,点击通知,就会跳转到NotificActivity这个活动界面了,如图:
nc

解决通知栏一直显示在系统状态栏的两种方案:一种是在NotificationCompat.Builder中连缀设置setAutoCancel()方法:

             notification=new NotificationCompat.Builder(this)
                        ...
                        .setAutoCancel(true)
                        .build();

另一种是显示的调用NotificationManager的cancel()方法将它取消,传入指定哪条通知的id:

manager= (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(1);

8.2.2 通知的进阶技巧

NotificationCompat.Builder 中提供了非常丰富的API,实现更加多样的效果。我们来学习比较常用的API。

setSound()方法:指在通知发出的时候播放一段音频。接收一个Uri参数指定音频文件首先要获取到音频对应的URI。指定代码如下:

Notification notification=new NotificationCompat.Builder
        ...
        .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Lyra.ogg")))
        .build();

setVibrate():指通知来的时候让手机进行振动。接收一个长整形的数组,用于设置手机静音和振动的时长(毫秒)。0下标为手机静音的时长,1下标为手机振动的时长,依此类推。手机隔一秒振动一次,代码如下:

Notification notification=new NotificationCompat.Builder
        ...
        .setVibrate(new loang[]{0,1000,0,1000})
        .build();

声明振动的权限如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hjw.notificationtest">

    <uses-permission android:name="android.permission.VIBRATE"/>
    ...
</manifest>

setLights():实现当有未接电话和短信时,此时手机处于锁屏状态,LED灯不停闪烁,提醒用户去查看。接收3个参数,第一个参数指定LED灯的颜色,第二个参数指定LED亮起时长(毫秒),第三个参数指定LED灯暗去的时长。代码如下:

Notification notification=new NotificationCompat.Builder
        ...
        .setLights(Color.GREEN,10001000)
        .build();

模拟器无法表现出振动以及LED灯闪烁的效果。

8.2.3 通知的高级功能

NotificationCompat.Builder 中更强大API的使用,构建更加丰富的通知效果。

setStyle()方法:构建出富文本的通知内容(通知中不只是有文字和图标,包含更多的东西)。接收一个NotificationCompact.Style参数(构建具体的富文本信息,如长文字,图片等)。

在使用setStyle()方法之前,设置很长的文本,代码如下:

                notification=new NotificationCompat.Builder(this)
                        ...
                        .setContentText("This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text")
                        ...
                        .build();

运行程序,点击Send notice按钮,效果如下:
sp

通知内容无法显示完整的,多余部分使用省略号代替(正常)。

通知中显示一段文字,通过setStyle()方法,代码如下:

             notification=new NotificationCompat.Builder(this)
                        ...
                      //.setContentText("")
                        .setStyle(new NotificationCompat.BigTextStyle().bigText("This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text This is content text"))
                        ...
                        .build();

NotificationCompat.BigTextStyle对象,调用bigText()方法传入文本内容。

运行程序,点击Send notice按钮,效果如下:
sn

通知里显示大图片,代码如下:

        notification=new NotificationCompat.Builder(this)
                        ...
                        .setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.drawable.mv_img)))
                        ...
                        .build();

设置大图片:NotificationCompat.BigPictureStyle对象,调用bigPicture()方法传入相应的图片(Bitmap对象),通过BitmapFactory.decodeResource()将图片解析成bitmap对象。

运行程序,点击Send notice按钮,效果如下:
mv

setPriority():设置通知的重要程度。接收一个整型参数设置通知的重要程度,5个常量值可选:PRIORITY_DEFAULT(默认的重要程度);PRIORITY_MIN(最低的重要程度,特定的场所显示这条通知);PRIORITY_LOW(较低的重要程度,缩小这类通知,改变显示顺序,排在更重要通知之后);PRIORITY_HIGH(较高的重要程度,方法这类通知,改变显示顺序,排在比较靠前的位置);PRIORITY_MAX(最高的重要程度,必须让用户立刻看到,甚至需要用户做出响应操作)。代码如下:

      notification=new NotificationCompat.Builder(this)
                        ...                                                   .setPriority(NotificationCompat.PRIORITY_MAX)
                        .build();

运行程序,点击Send notice按钮,效果如下:
pn

弹出横幅,这是一条非常重要的通知。


8.3 调用摄像头和相册

学习调用摄像头和相册方面的知识。

8.3.1 调用摄像头拍照

在应用程序调用摄像头进行拍照。

新建CameraAlbumTest项目,修改activity_main.xml中的代码如下:

<?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/btn_take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take photo"
        android:textAllCaps="false" />

    <ImageView
        android:id="@+id/iv_picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

</LinearLayout>

Button打开摄像头进行拍照,ImageView显示拍到的图片。

修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity {

    private static final int TAKE_PHOTO = 1;

    private Button btn_take_photo;
    private ImageView iv_picture;

    private Uri imageUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_take_photo= (Button) findViewById(R.id.btn_take_photo);
        iv_picture= (ImageView) findViewById(R.id.iv_picture);

        btn_take_photo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //创建File文件,用于储存拍照后的照片
                //getExternalCacheDir()关联缓存目录(专门存放缓存的目录)
                //具体路径/sdcard/Android/data/包名/cache
                //Android6.0,SD卡被列为危险权限,放在其他目录就要进行权限处理
                File outputImage=new File(getExternalCacheDir(),"output_image.jpg");

                try {
                    if (outputImage.exists()){
                        outputImage.delete();
                    }
                    outputImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                //Android7.0 调用FileProvider.getUriForFile()封装成Uri的字符串,接收3个参数,第一个:上下文对象,第二个:任意唯一字符串,第三个,File对象
                //7.0以下调用Uri.fromFile()将File对象转化Uri
                //FileProvider是一种特殊的内容提供器,对数据进行保护,选择性的封装过的Uri共享给外部,提高应用安全性。
                if (Build.VERSION.SDK_INT>=24){
                    imageUri= FileProvider.getUriForFile(MainActivity.this,"com.example.hjw.cameraalbumtest",outputImage);
                }else{
                    imageUri=Uri.fromFile(outputImage);
                }

                //启动相机
                Intent intent=new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                startActivityForResult(intent,TAKE_PHOTO);
            }
        });
    }

    @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));
                        iv_picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }
}

注册内容提供器并加入写SD卡的权限,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hjw.cameraalbumtest">
    <user-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.hjw.cameraalbumtest.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>

        </provider>
    </application>

</manifest>

Andoid系统在4.4系统之前,访问SD卡需要声明权限,4.4开始就不再需要申请了。兼容老系统,还需要声明权限。

创建@xml/file_paths,在res目录下新建文件Directory,创建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卡进行共享。

运行程序,点击Take photo按钮拍照,如下:
simg

8.3.4 从相册中选择照片

实现从相册中选择照片的功能。

修改activity_main.xml中的代码如下:

<?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/btn_take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo"
        android:textAllCaps="false" />

    <Button
        android:id="@+id/btn_choose_from_album"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Choose From Album"
        android:textAllCaps="false" />

    <ImageView
        android:id="@+id/iv_picture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

</LinearLayout>

加入从相册中选择照片的逻辑,修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity {

    private static final int TAKE_PHOTO = 1;
    private static final int CHOOSE_FROM_ALBUM = 2;

    private Button btn_take_photo, btn_choose_from_album;
    private ImageView iv_picture;

    private Uri imageUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        btn_choose_from_album = (Button) findViewById(R.id.btn_choose_from_album);
        btn_choose_from_album.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_FROM_ALBUM); //打开相册
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            ...
            case CHOOSE_FROM_ALBUM:
                if (resultCode == RESULT_OK) {
                    //判断手机系统版本号
                    if (Build.VERSION.SDK_INT >= 19) {
                        //4.4以上系统处理图片
                        handleImageOnKitKat(data);
                    } else {
                        //4.4以下系统处理图片
                        handleImageBeforeKitKat(data);
                    }
                }
                break;
            default:
                break;
        }
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private void handleImageOnKitKat(Intent data) {
        String imagePath = null;
        Uri uri = data.getData();
        if (DocumentsContract.isDocumentUri(this, uri)) {
            //如果是document类型的Uri,则通过 document id处理
            String docId = DocumentsContract.getDocumentId(uri);
            if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
                String id = docId.split(":")[1]; //解析出数字格式的Id
                String selection = MediaStore.Images.Media._ID + "=" + id;
                imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
            } else if ("com.android.provides.downloads.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())) {
            //如果是content类型的Uri,则使用普通方式处理
            imagePath = getImagePath(uri, null);
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            //如果是file类型的Uri,直接获取图片路径
            imagePath = uri.getPath();
        }
        displayImage(imagePath); //根据图片路径显示图片
    }

    private void handleImageBeforeKitKat(Intent data) {
        Uri uri = data.getData();
        String imagePath = getImagePath(uri, null);
        displayImage(imagePath);
    }


    private String getImagePath(Uri uri, String selection) {
        String path = null;
        //通过Uri和selection来获取真实的图片路径
        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;
    }

    private void displayImage(String imagePath) {
        if (imagePath!=null){
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
            iv_picture.setImageBitmap(bitmap);
        }else{
            Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
        }
    }
}

运行到手机上,点击Choose From Album按钮,运行效果如下:
qx

xc

xztp

实现了调用摄像头拍照以及从相册中选择照片的功能。根据项目的需求进行适当的压缩。


8.4 播放多媒体文件

Android播放音频和视频。

8.4.1 播放音频

使用MediaPlayer类实现播放音频的功能。下表列出MediaPlayer类中较为常用的控制方法:
mp

MediaPlayer工作流程:首先创建一个MediaPlayer对象,调用setDataSource()方法设置音频文件的路径,在调用prepare()方法进入准备状态,接下来调用start()方法就可以播放音频;pause()暂停播放;reset()停止播放。

新建PlayAudioTest项目,修改main_avtivity.xml中的代码如下:

<?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/btn_play"
        android:text="Play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_pause"
        android:text="Pause"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_stop"
        android:text="Stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private Button btn_play,btn_pause,btn_stop;

    private MediaPlayer mediaPlayer=new MediaPlayer();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btn_play= (Button) findViewById(R.id.btn_play);
        btn_pause= (Button) findViewById(R.id.btn_pause);
        btn_stop= (Button) findViewById(R.id.btn_stop);

        btn_play.setOnClickListener(this);
        btn_pause.setOnClickListener(this);
        btn_stop.setOnClickListener(this);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }else{
            initMediaPlayer(); //初始化MediaPlayer
        }
    }

    private void initMediaPlayer() {

        try {
            File file=new File(Environment.getExternalStorageDirectory(),"laoshidege.mp3");
            mediaPlayer.setDataSource(file.getPath()); //指定音频文件的目录
            mediaPlayer.prepare();//准备状态
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull 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:
                break;
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_play:
                if (!mediaPlayer.isPlaying()){
                    mediaPlayer.start(); //开始播放
                }
                break;
            case R.id.btn_pause:
                if (mediaPlayer.isPlaying()){
                    mediaPlayer.pause(); //暂停播放
                }
                break;
            case R.id.btn_stop:
                if (mediaPlayer.isPlaying()){
                    mediaPlayer.reset(); //停止播放
                    initMediaPlayer();
                }
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mediaPlayer!=null){
            mediaPlayer.stop();
            mediaPlayer.release();  //释放MediaPlayer相关资源
        }
    }
}

不要忘记声明权限,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hjw.playaudiotest">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
</manifest>

运行程序到手机上,先弹出申请权限,如图:
vyy

同意授权,点击Play按钮,优美的音乐响起,然后点击Pause按钮,音乐暂停,再点击Play,就会继续播放。这时候点击stop按钮,音乐会停止,再点击Play按钮,音乐从头播放。

8.4.2 播放视频

使用VideoView类来实现视频文件的播放,它与MediaPlayer用法类似,常用方法如下图:
v

新建PlayVideoTest项目,修改activity_main中的代码如下:

<?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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn_play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Play" />

        <Button
            android:id="@+id/btn_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Pause" />

        <Button
            android:id="@+id/btn_replay"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="replay" />
    </LinearLayout>

    <VideoView
        android:id="@+id/video_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

VideoView控件用于显示视频。

接下来修改MainActivity中的代码如下:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btn_play,btn_pause,btn_relay;
    private VideoView video_View;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        video_View= (VideoView) findViewById(R.id.video_view);
        btn_play= (Button) findViewById(R.id.btn_play);
        btn_pause= (Button) findViewById(R.id.btn_pause);
        btn_relay= (Button) findViewById(R.id.btn_replay);

        btn_relay.setOnClickListener(this);
        btn_pause.setOnClickListener(this);
        btn_play.setOnClickListener(this);

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }else{
            initVideoPath();
        }
    }

    private void  initVideoPath() {
        File file=new File(Environment.getExternalStorageDirectory(),"movie.mp4");
        video_View.setVideoPath(file.getPath()); //指定视频播放路径
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if (grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
                    initVideoPath();
                }else{
                    Toast.makeText(this, "拒绝权限将无法使用程序", Toast.LENGTH_SHORT).show();
                    finish();
                }
                break;
            default:
                break;
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_play:
                if (!video_View.isPlaying()){
                    video_View.start(); //开始播放
                }
                break;
            case R.id.btn_pause:
                if (video_View.isPlaying()){
                    video_View.pause();  //暂停播放
                }
                break;
            case R.id.btn_replay:
                if (video_View.isPlaying()){
                    video_View.resume(); //重新播放
                }
                break;
            default:
                break;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (video_View!=null){
            video_View.suspend(); //释放ViewVideo资源
        }
    }
}

不要忘记声明权限,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hjw.playvideotest">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
...
</manifest>

将程序运行到手机上,先弹出申请对话框,同意授权,点击一下Play按钮,就可以看到播放视频了,如图:
bsp

点击Pause按钮暂停播放,点击Replay重头播放。

VideoView的用法和MediaPlayer相似是因为它的背后使用MediaPlayer来对视频文件进行控制的(不是一个万能的视频播放工具类)。


8.5 小结与点评

学习了Android系统中的各种多媒体技术,包括通知的使用技巧,调用摄像头进行拍照,从相册选取照片,以及播放音频和视频文件。
Android手机上调试程序的方法。

发布了18 篇原创文章 · 获赞 28 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/JiangWeiHu/article/details/71438647