第一行代码 Ch.8

Ch.VIII:

8.2 使用通知:

由于Android 8.0使用了渠道机制, 导致没有渠道的通知无法显示, 所以, 直接看作者的新文章吧:

关于Android 8.0新增的通知栏渠道NotificationChannle の介绍与使用

使用通知notification的标准操作(适配Android 8.0+):

  1. 创建NotificationManager类对象, 用于获取系统的通知服务:

    notificationManager = (NotificationManager) 
        getSystemService(NOTIFICATION_SERVICE);
    
  2. 创建频道NotificationChannel, 并在NotificationManager中注册:

    NotificationChannel channel = new NotificationChannel(id, name, importance);
            notificationManager.createNotificationChannel(channel);
    

    渠道Channle一旦使用NotificationManager 的 createNotificationChannel()方法注册后, 就会一直在系统中有记录, 除非卸载应用重装

    所以, 后头使用NotificationCompat.Builder创建notification时, 可以直接使用上头注册好的Channel

  3. 使用建筑者模式构筑Notification类实例:

    由于通知Notification在每个API版本中更新很频繁, 所以这里使用 NotificationCompat.Builder 构筑兼容性的builder, 并在builder中设置生成的Notification的各个属性

    new NotificationCompat.Builder(MainActivity.this,
    notificationIDList.get(0))
            .setContentTitle("这是一条礼貌问候")
            .setContentText("NM$L")
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
    

    注意Android 8.0 之后, 有些设置不再有用, 需要到NotificationChannel中设置

  4. 使用NotificationCompat.Builder类的build()方法创建指定的Notification对象:

    Notification notification = builder.build();
    
  5. 使用NotificationManager的notify() 方法发出通知:

    notificationManager.notify(0, notification);
    
    • 第一个参数为ID, 只需要确保全局唯一性即可
    • 第二个参数为需要发出的Notification

注意点:

震动与Channel增删可以参考这里

  1. 如果需要设置其他与Channel相关的属性, 如震动, 就需要到Channel中设置, 并且, 相关设置还需要足够的Channel优先级才有效(如震动&弹窗)

    channel.setVibrationPattern(new long[]{0,200,1000,400,500,100,600});
    

    其他还有很多属性, 基本上API中介绍的都需要转到Channel中进行设置, 单独对Notification进行设置不再有用

  2. Channel一旦注册后, 基本上是无法delete的, 即时使用notificationManager.deleteNotificationChannel(id), 也无法删除并创建新的

    在这里插入图片描述

    删除的方法只能是通过卸载APP再重装, 亦或者不断的增大ID

  3. 所有使用相同Channel的Notification都会被系统折叠, 以减少占用的空间:

    在这里插入图片描述

  4. 用户可以到设备的权限管理中心对APP的通知权限进行调整, 主要是调整各个通道的属性

  5. 由于用户对Channel有绝对的控制权, 并且Channel创建后, 程序无法修改, 所以当程序必须需要某些通知权限时, 可以通过检测的方法, 提醒用户开始权限:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel channel = manager.getNotificationChannel("chat");
                if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
                    Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
                    intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
                    intent.putExtra(Settings.EXTRA_CHANNEL_ID, channel.getId());
                    startActivity(intent);
                    Toast.makeText(this, "请手动将通知打开", Toast.LENGTH_SHORT).show();
                }
            }
    

另外还有很多的设置点, 可以看作者大大的新博客, 下头对此有较多的例举

为Notification加上PendingIntent:

PendingIntent可以理解为延时执行的Intent, 具有与Intent类似的功能

使用PendingIntent类的静态方法获取PendingIntent对象:

  • getActivity(Context, int, Intent, int)------->跳转到一个activity组件、
  • getBroadcast(Context, int, Intent, int)------>打开一个广播组件
  • getService(Context, int, Intent, int)-------->打开一个服务组件。

其中:

  • 第一个参数为Context对象, 没啥好说的

  • 第二个参数通常传入0

  • 第三个参数为Intent对象, 用来构建将要执行的意图

  • 第四个参数有这4个static量可选:

    在这里插入图片描述

    关于几个不同的作用, 直接去看API文档

    通常传入0即可

创建了PendingIntent对象后, 使用NotificationCompat.Builder的setContentIntent()方法将PendingIntent对象加入builder中

public Builder setContentIntent(PendingIntent intent) {
            mContentIntent = intent;
            return this;
        }

而后, 可以考虑设置点击后自动删除通知:

  1.  new NotificationCompat.Builder(MainActivity.this,
                                    notificationIDList.get(1))
         .setContentTitle("这是一条推荐信息")
         .setContentText("WDNMD真就白给啊")
         .setWhen(System.currentTimeMillis())
         .setSmallIcon(R.mipmap.ic_launcher)
         .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                                                    R.mipmap.ic_launcher))
         .setContentIntent(pendingIntent)
         .setAutoCancel(true);	//使用这个设置自动关闭
    
  2. 或者是在程序中显示的进行关闭:

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

    cancel()方法接受一个Channel的全局唯一ID, 指定关闭哪个Channel

通知的进阶技巧&高级功能:

这里就是介绍NotificationCompatible.Builder中的各个设置Notification属性的方法:

这个到时候需要在查看吧

8.3调用摄像头和相册:

这里有一个更好用的框架, 直接使用这个可以非常省事!

然后下头的就都是狗屁!!!


  1. 首先是动态申请权限, 如果Android 版本>=6.0 就需要动态权限申请:

    和之前介绍的一样, 这里就不重复了

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(MainActivity.this,
                                              Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this,
                                              new String[]{Manifest.permission.CAMERA}, 1);
            Toast.makeText(MainActivity.this, "请打开相机权限", Toast.LENGTH_SHORT).show();
        } else {
            openCamera();
            Toast.makeText(MainActivity.this, "已获取相机权限", Toast.LENGTH_SHORT).show();
        }
    
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 1: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(this, "允许使用相机", Toast.LENGTH_SHORT).show();
                    openCamera();
                } else {
                    Toast.makeText(this, "拒绝使用相机", Toast.LENGTH_SHORT).show();
                }
                break;
            }
            case 2: {
                break;
            }
            default:
        }
    }
    
  2. 而后是为相机拍摄的照片创建一个储存文件:

    首先需要了解一下File类, 这玩意在之前的Java学习中没看到…

    这里使用File类的父路径&子路径的构造函数:

    File outputImage = new File(getExternalCacheDir(), "outputImage.jpg");
    

    其中getExternalCacheDir()方法获取当前APP在SD卡中的数据缓存路径, 具体的路径是/sdcard/Android/data/<package name>/cache

    此路径不需要申请SD卡读写权限

    如果选择其他目录, 需要申请动态权限:

    READ_EXTERNAL_STORAGE

    WRITE_EXTRANAL_STORAGE

  3. 而后, 获取储存文件的Uri:

    而后, 由于Android 7.0 又对权限进行了限制, 所以需要一定的额外操作:

    主要是7.0之后, 认为提供真实路径的Uri是不安全的, 需要使用特殊的内容提供器FileProvider对Uri进行一定的封装, 并选择性的提供给外部, 提高应用的安全性:

    if (Build.VERSION.SDK_INT >= 24) {
        //            imageUri = Uri.fromFile(outputImage);
        imageUri = FileProvider.getUriForFile(this, "com.example.cameraalbumdemo.fileProvider",
                                              outputImage);
    } else {
        imageUri = Uri.fromFile(outputImage);
    }
    

    注意这里使用的不是android.support.v4.content.FileProvider, 转而使用androidx.core.content.FileProvider, 由于前者不知道啥原因找不到…, 所以改用后者, 效果相同

    <provider
              android:name="androidx.core.content.FileProvider"
              android:authorities="com.example.cameraalbumdemo.fileProvider"
              android:exported="false"
              android:grantUriPermissions="true">
        <meta-data
                   android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/file_paths" />
    </provider>
    
    • android:name属性的值是固定的
    • android:authorities属性的值必须要和刚才FileProvider.getUriForFile()方法中的第二个参数一致
    • 另外,这里还在<provider>标签的内部使用<meta-data>来指定Uri的共享路径,并引用了一个@xml/file_paths资源。当然,这个资源现在还是不存在的,下面我们就来创建它。

    添加一个xml文件, 注意这个文件的文件名需要与提供的路径资源相同

    首先要在res的文件夹下建立一个xml的文件夹,再建立一个file_paths.xml,如图所示:

    在这里插入图片描述

    file_path.xm的内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <external-path name="external_files" path=""/>
    </paths>
    

    其中,external-path 就是用来指定Uri共享的,name属性的值可以随便填,path属性的值表示共享的具体路径。这里设置空值就表示将整个SD卡进行共享,当然你也可以仅共享我们存放output image.jpg这张图片的路径。

  4. 使用Intent打开相机:

    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, TAKE_PHOTO);
    }
    

    这里与文中不同, 文中使用的是

    Intent intent=new Intent("android.media.action.IMAGE_CAPTURE"); 
    intent. putExtra(MediaStore. EXTRA OUTPUT, imageUri);
    startActivityForResult(intent, TAKE_PHOTO);
    

    主要区别为使用MediaStore.ACTION_IMAGE_CAPTURE, 基本上都是使用这个了, 直接使用String的android.media.action.IMAGE_CAPTURE很少

    而后是后头启用相机的方式, 实际上就是多个判定, 保证获取到了相机…

    详见API文档

  5. 最后是处理回调

    主要做的是将储存的图片重新读取, 并显示到ImageView上:

    @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));
                            imageView.setImageBitmap(bitmap);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }else{
                        Toast.makeText(this,"已取消拍摄",Toast.LENGTH_SHORT).show();
                    }
                    break;
                }
                default:
            }
        }
    

    其中使用的方法在Ch.7访问其他程序中的数据中有学到:

    • 首先是判断是否完成了拍摄, 如果在使用相机时back取消拍摄, resultCode就是RESULT_CANCELD了

    • 而后是开一个输入流, 指向储存的照片文件, 并解析为bitmap显示在ImageView上

8.4 播放多媒体文件:

播放音频文件:

这里就是使用Android MediaPlayer进行多媒体的播放

这里对MediaPlayer进行了非常详细的解释, 基本上不需要看书了

常用的MediaPlayer方法:

在这里插入图片描述

而后, 对于Path路径的获取, 这里使用一个轻量化的FilePicker框架, 上手非常简单, 并且其中也有较多的说明与DEMO供高级功能, 基本上没啥特殊需求的话, 完全够用

播放视频文件:

这里使用VideoView进行视频播放, 使用方法与MediaPlayer有点类似:

常用方法:

在这里插入图片描述

上手非常简单, 并且其中也有较多的说明与DEMO供高级功能, 基本上没啥特殊需求的话, 完全够用](https://github.com/rosuH/AndroidFilePicker)

播放视频文件:

这里使用VideoView进行视频播放, 使用方法与MediaPlayer有点类似:

常用方法:

在这里插入图片描述

常用方法也是很简单, 初始化后, 用setVideoPath() 方法设置个路径就可以和MediaPlayer一样用start()播放了, 更多高级用法看这里, 现在暂时用不到, 就放过吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值