第一行代码读书笔记 8 -- 手机多媒体

作者: 开心wonderful | 来源:发表于2017-01-17 00:47 被阅读192次

    本篇文章主要介绍以下几个知识点:

    • 通知的使用;
    • 调用系统相机拍照或调用相册选取照片;
    • 播放多媒体文件。
    图片来源于网络

    8.1 使用通知

    当某个应用程序希望向用户发送一些提示消息,而该应用程序又不在前台运行时,就可借助通知(Notification)来实现。

    8.1.1 通知的基本用法

    通知的用法灵活,既可以在活动里创建,也可在广播接收器里创建,还可在服务里创建。

    无论在哪里创建通知,整体的步骤都是相同的。接下来学习下创建通知的步骤,代码如下:

    public class NotificationActivity extends AppCompatActivity implements View.OnClickListener {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_notification);
    
            Button 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:
                    // 1. 获取 NotificationManager 实例来对通知进行管理
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    // 2. 使用 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)     // 通知的小图标
                            .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))// 通知的大图标
                            .build();
                    // 3. 显示通知. 其中notify()的两个参数:第一个是id,要保证为每个通知所指定的id都是不同的;
                    // 第二个参数是 Notification 对象
                    manager.notify(1,notification);
                    break;
    
                default:
                    break;
            }
        }
    }
    

    上述代码,界面上就一个 发送通知 按钮,在按钮的点击事件里面完成了通知的创建工作。运行程序,点击按钮效果如下:

    通知的基本用法

    接下来实现通知的点击效果,这里要用到 PendingIntent ,即在某个合适的时机去执行某个动作。PendingIntent 实例可由 getActivity()、getBroadcast()、getServices() 等方法获取,其接收的参数:第一个是 Context;第二个一般用不到传0即可;第三个是 Intent 对象;第四个用于确定 PendingIntent 的行为,一般传0即可。

    首先,准备好一个点击通知跳转的活动 NotificationTestActivity ,然后,修改 NotificationActivity 的代码如下:

    public class NotificationActivity extends AppCompatActivity implements View.OnClickListener {
    
        . . . 
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                case R.id.btn_send_notice:
                    Intent intent = new Intent(this,NotificationTestActivity.class);
                    PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
    
                    // 1. 获取 NotificationManager 实例来对通知进行管理
                    NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
                    // 2. 使用 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)     
                            .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
                            .setContentIntent(pi) // 传入pi
                            .build();
                    // 3. 显示通知
                    manager.notify(1,notification);
                    break;
    
                default:
                    break;
            }
        }
    }
    

    现在重新运行程序并点击按钮,效果如下:

    点击通知后的效果

    细心的你会发现系统状态栏上的通知图标还没消失,解决方法有两种,一种是在 NotificationCompat.Builder 中再连缀一个 setAutoCancel() 方法,如下:

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

    另外一种是显式调用 NotificationManager 的 cancel()方法,在点击通知跳转的活动 NotificationTestActivity 中添加如下代码:

    public class NotificationTestActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_notification_test);
    
            NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            manager.cancel(1); // 传入创建通知时指定的id
        }
    }
    

    8.1.2 通知的进阶用法

    上面提到了通知的基本用法,接下来介绍一些通知的其他技巧,比如:

    • 在通知发出时播放一段音频,调用 setSound() 方法:
    Notification notification = new NotificationCompat.Builder(this)
            . . .
            .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg"))) // 在音频目录下选个音频文件
            .build();
    
    • 在通知到来时让手机振动,设置 vibrate 属性:
    Notification notification = new NotificationCompat.Builder(this)
            . . .
            .setVibrate(new long[]{0,1000,1000,1000}) // 数组下标0表静止的时长,下标1表振动的时长,下标2表静止的时长,以此类推
            .build();
    

    当然别忘了声明振动权限:

    <uses-permission android:name="android.permission.VIBRATE" />
    
    • 在通知到来时显式手机 LED 灯,调用 setLights() 方法:
    Notification notification = new NotificationCompat.Builder(this)
            . . .
            .setLights(Color.GREEN,1000,1000) // 三个参数:LED 灯的颜色、灯亮时长、灯暗时长
            .build();
    
    • 当然也可直接使用默认效果,如下:
    Notification notification = new NotificationCompat.Builder(this)
            . . .
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .build();
    

    8.1.3 通知的高级功能

    上面提到了通知的进阶用法,接下来介绍一些通知的高级功能,比如:

    • NotificationCompat.Builder 类中的 setStyle() 方法
        这个方法允许我们构建出富文本的通知内容,setStyle() 方法接收一个 NotificationCompat.style 参数,这个参数用来构造具体的富文本信息,如长文字、图片等。

    在通知当中显示一段长文字,代码如下:

    Notification notification = new NotificationCompat.Builder(this)
            . . .
            // 在setStyle方法中创建NotificationCompat.BigTextStyle对象,调用其bigText()方法传入文字即可
            .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();
    

    效果如图所示:

    通知中显示图片的效果
    • NotificationCompat.Builder 类中的 setPriority() 方法
        这个方法用于设置通知的重要程度,setPriority() 方法接收一个整型参数。

    将通知的重要程度设置最高,代码如下:

    Notification notification = new NotificationCompat.Builder(this)
            . . 
            // 一共有5个常量可选:PRIORITY_DEFAULT默认、PRIORITY_MIN最低、PRIORITY_LOW较低、
            // PRIORITY_HIGH较高、PRIORITY_MAX最高
            .setPriority(NotificationCompat.PRIORITY_MAX)   
            .build();
    

    效果如图所示:

    发送一条重要通知的效果

    8.2 调用摄像头和相册

    调用摄像头拍照以及从相册中选择照片的相关代码如下:

    public class TakePhotoActivity extends AppCompatActivity {
    
        private Button btn_take_photo;   // 调用摄像头拍照
        private Button btn_select_photo; // 从相册中选照片
        private ImageView iv_show_photo; // 展示照片
    
        public static final int TAKE_PHOTO = 1;
        public static final int CHOOSE_PHOTO = 2;
        private Uri imageUri;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_take_photo);
    
            btn_take_photo = (Button) findViewById(R.id.btn_take_photo);
            btn_select_photo = (Button) findViewById(R.id.btn_select_photo);
            iv_show_photo = (ImageView) findViewById(R.id.iv_show_photo);
    
            // 调用相机拍照
            btn_take_photo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // 创建File对象,用于存储拍照后的图片
                    File outputImage = new File(getExternalCacheDir(),"output_image.jpg");
                    try{
                        if (outputImage.exists()){
                            outputImage.delete();
                        }
                        outputImage.createNewFile();
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                    // 将File对象转换成Uri对象
                    if (Build.VERSION.SDK_INT >= 24){
                        // getUriForFile() 方法接收三个参数:Context对象、任意唯一的字符串、File对象
                        imageUri = FileProvider.getUriForFile(TakePhotoActivity.this, "com.wonderful." +
                                "myfirstcode.chapter8.TakePhotoActivity.fileprovider",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);
                }
            });
    
            // 打开相册选照片
            btn_select_photo.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (ActivityCompat.checkSelfPermission(TakePhotoActivity.this,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(TakePhotoActivity.this,
                                new String[]{android.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[] permissions, int[] grantResults) {
            switch (requestCode){
                case 1:
                    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
                        openAlbum();
                    }else {
                        ToastUtils.showShort("你拒绝了权限请求");
                    }
                    break;
    
                default:
                    break;
            }
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            switch (requestCode){
                case TAKE_PHOTO:
                    if (resultCode == RESULT_OK){
                        try{
                            // 将拍摄的照片显示出来
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                            iv_show_photo.setImageBitmap(bitmap);
                        }catch (FileNotFoundException e){
                            e.printStackTrace();
                        }
                    }
    
                case CHOOSE_PHOTO:
                    if (resultCode == RESULT_OK){
                        // 判断手机系统版本号
                        if (Build.VERSION.SDK_INT >= 19){
                            // 4.4及以上系统使用这个方法处理图片
                            handleImageOnKitKat(data);
                        }else {
                            // 4.4以下系统使用这个方法处理图片
                            handleImageBeforeKitKat(data);
                        }
                    }
    
                default:
                    break;
            }
        }
    
        /**
         *  4.4及以上系统处理图片方法
         * @param data
         */
        @TargetApi(19)
        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.providers.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类型到1Uri,则直接获取图片路径即可
                imagePath = uri.getPath();
            }
            // 根据图片路径显示图片
            displayImage(imagePath);
        }
    
        /**
         *  4.4以下系统处理图片方法
         * @param data
         */
        private void handleImageBeforeKitKat(Intent data) {
            Uri uri = data.getData();
            String imagePath = getImagePath(uri,null);
            displayImage(imagePath);
        }
    
        /**
         * 通过Uri和selection来获取真实的图片路径
         * @param uri
         * @param selection
         * @return
         */
        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;
        }
    
        /**
         *  根据图片路径显示图片
         * @param imagePath
         */
        private void displayImage(String imagePath){
            if (imagePath != null){
                Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
                iv_show_photo.setImageBitmap(bitmap);
            }else {
                ToastUtils.showShort("选取图片失败");
            }
        }
    }
    

    上述代码中,调用摄像头拍照时用到了内容提供器,所以别忘了在 AndroidManifest.xml 中对内容提供器进行注册,如下:

    <!-- android:name 属性的值是固定的
         android:authorities 属性的值要和FileProvider.getUriForFile()方法中的第二个参数一致
         <meta-data>来指定Uri的共享路径 -->
    <provider
        android:authorities="com.wonderful.myfirstcode.chapter8.TakePhotoActivity.fileprovider"
        android:name="android.support.v4.content.FileProvider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/file_paths" />
     </provider>
    

    上面引用了一个 @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指定Uri共享,其name的值可随便填,path是指共享的具体路径 -->
        <external_path name = "my_images" path = ""/>
    </paths>
    

    当然也别忘了声明访问SD卡的权限:

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

    注意事项:在实际开发中最好根据项目的需求先对照片进行适当的压缩,然后再加载到内存中。

    8.3 播放多媒体文件

      在安卓中播放音频文件一般用 MediaPlayer 类来实现,播放视频文件主要用 VideoView 类来实现。以下是两个类的常用方法:

    播放多媒体文件方法

      实现播放多媒体代码相对简单,这里播放音频和视频写在一块了,代码如下:

    public class MediaPlayActivity extends AppCompatActivity implements View.OnClickListener {
    
        private Button music_play,music_pause,music_stop;// 音频播放、暂停、停止
        private Button video_play,video_pause,video_replay;// 视频播放、暂停、重新播放
    
        // 音频播放 创建MediaPlayer实例
        private MediaPlayer mediaPlayer = new MediaPlayer();
    
        // 视频播放显示
        private VideoView videoView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_media_play);
    
            initMediaPlayerView();
    
            if (ActivityCompat.checkSelfPermission(MediaPlayActivity.this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(MediaPlayActivity.this,
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            }else {
                initMediaPlayer(); // 初始化MediaPlayer
                initVideoPath(); // 初始化MediaPlayer
            }
        }
    
    
    
        @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();
                        initVideoPath();
                    }else {
                        ToastUtils.showShort("你拒绝了权限请求");
                    }
                    break;
    
                default:
                    break;
            }
        }
    
        /**
         * 初始化音频播放器
         */
        private void initMediaPlayer() {
            File file = new File(Environment.getExternalStorageDirectory(),"music.mp3");//事先放好的音频文件
            try {
                mediaPlayer.setDataSource(file.getPath()); // 指定音频文件的路径
                mediaPlayer.prepare();// 让 MediaPlayer 进入到准备状态
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 初始化视频播放器
         */
        private void initVideoPath() {
            File file = new File(Environment.getExternalStorageDirectory(),"movie.mp4");//事先放好的视频文件
            videoView.setVideoPath(file.getPath());   // 指定视频文件的路径
        }
    
    
        private void initMediaPlayerView() {
            music_play = (Button) findViewById(R.id.music_play);
            music_pause = (Button) findViewById(R.id.music_pause);
            music_stop = (Button) findViewById(R.id.music_stop);
    
            music_play.setOnClickListener(this);
            music_pause.setOnClickListener(this);
            music_stop.setOnClickListener(this);
    
            videoView = (VideoView) findViewById(R.id.video_view);
            video_play = (Button) findViewById(R.id.video_play);
            video_pause = (Button) findViewById(R.id.video_pause);
            video_replay = (Button) findViewById(R.id.video_replay);
    
            video_play.setOnClickListener(this);
            video_pause.setOnClickListener(this);
            video_replay.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()){
                // 音频播放
                case R.id.music_play:
                    if (!mediaPlayer.isPlaying()){
                        mediaPlayer.start(); // 开始播放
                    }
                    break;
                case R.id.music_pause:
                    if (!mediaPlayer.isPlaying()){
                        mediaPlayer.pause(); // 暂停播放
                    }
                    break;
                case R.id.music_stop:
                    if (!mediaPlayer.isPlaying()){
                        mediaPlayer.stop(); // 停止播放
                    }
                    break;
    
                // 视频播放
                case R.id.video_play:
                    if (!videoView.isPlaying()){
                        videoView.start(); // 开始播放
                    }
                    break;
                case R.id.video_pause:
                    if (!videoView.isPlaying()){
                        videoView.pause(); // 暂停播放
                    }
                    break;
                case R.id.video_replay:
                    if (!videoView.isPlaying()){
                        videoView.resume(); // 重新播放
                    }
                    break;
            }
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mediaPlayer != null){
                mediaPlayer.stop();
                mediaPlayer.release();// 释放资源
            }
            if (videoView != null){
                videoView.suspend();  // 释放资源
            }
        }
    }
    

      当然别忘了声明权限:

    <!-- 访问SD卡权限 -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

      本章内容相对简单,下一章将学习网络编程的知识。

    相关文章

      网友评论

        本文标题:第一行代码读书笔记 8 -- 手机多媒体

        本文链接:https://www.haomeiwen.com/subject/gqyrbttx.html