【Android Studio】第一行代码-第8章 运用手机多媒体 通知

〇、前情提要

第一行代码Android第八章内容中通知部分。
参考:

  1. 思维导图
    https://www.zhihu.com/question/27596017/answer/37634740
  2. 【Android Studio】第一行代码-第8章 运用手机多媒体 图片
    https://blog.csdn.net/weixin_43210113/article/details/109409159
  3. 【Android Studio】第一行代码-第8章 运用手机多媒体 音频视频
    https://blog.csdn.net/weixin_43210113/article/details/109409166

一、概要

目录

【Android Studio】第一行代码-第8章 运用手机多媒体 通知_第1张图片

思维导图

【Android Studio】第一行代码-第8章 运用手机多媒体 通知_第2张图片


二、将程序运行到手机上

API 调用多媒体资源

1.如何将程序运行到手机上

1、先通过数据线把手机连接到电脑上。
2、进人到“关于手机”界面,然后对着最下面的版本号那一栏连续点击,就会让开发者选项显示出来。
3、然后如果你使用的是Windows 操作系统,还需要在电脑上安装手机的驱动。
4、进入到设置→开发者选项界面,并在这个界面中勾选中USB调试选项。
【Android Studio】第一行代码-第8章 运用手机多媒体 通知_第3张图片

AndroidMonitor

你会发现当前是有两个设备在线
模拟器+刚连接上的手机

运行一下当前项目,这时不会直接将程序运行到模拟器或者手机上,而是会弹出一个对话框让你进行选择。


三、使用通知

当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。
发出一条通知后,手机最上方的状态栏中会显示一个通知的图标,下拉状态栏后可以看到通知的详细内容。

1.通知的基本用法

创建途径

  • 在活动里创建(少 一般只有当程序进人到后台才需要)
  • 在广播接收器里创建
  • 在服务里创建(下一章)

详细步骤

1.创建NotificationManager
NotificationManager来对通知进行管理,可以调用Context的getSystemService()方法获取到。

  • getSystemService()方法
    接收一个字符串参数用于确定获取系统的哪个服务
    传人Context.NOTIFICATION_SERVICE 即可。
  • 获取NotificationManager的实例
NotificationManager manager = (NotificationManager)getSystemService (Context . NOTIFICATION_ SERVICE);

2.Builder构造器 创建Notification对象
接下来需要使用一个Builder构造器来创建Notification对象,使用support库中提供的兼容API,NotificationCompat类创建Notification对象,可以保证我们的程序在所有Android系统版本上都能正常工作了。

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

创建了一个空的Notification对象,并没有什么实际作用,我们可以在最终的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();
  • setContentTitle()方法
    指定通知的标题内容,下拉系统状态栏就可以看到这部分内容。
  • setContentText( )方法
    指定通知的正文内容,同样下拉系统状态栏就可以看到这部分内容。
  • setWhen()方法
    指定通知被创建的时间,以毫秒为单位,当下拉系统状态栏时,这里指定的时间会显示在相应的通知上。
  • setSmallIcon()方法
    设置通知的小图标,注意只能使用纯alpha图层的图片进行设置,小图标会显示在系统状态栏上。
  • setLargeIcon( )方法
    设置通知的大图标,当下拉系统状态栏时,就可以看到设置的大图标了。

3.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"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/send_notice"
        android:layout_width="wrap_parent"
        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)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .build();
                manager.notify(1, notification);
                break;
            default:
                break;
        }
    }
}

通知栏的大小图都直接设置成了ic_launcher这张图,这样就不用再去专门准备图标了,而在实际项目中千万不要这样偷懒。

运行程序

点击Send notice按钮,系统状态栏的最左边看到一个小图标。

缺图

下拉系统状态栏看详细通知

缺图

优化项目-点击通知跳转

点击后无法查看详细通知
1.PendingIntent 和 Intent

  • 都可以去指明某一个“意图”
  • 都可以用于启动活动、启动服务以及发送广播等。
  • Intent 更加倾向于去立即执行某个动作
  • PendingIntent 更加倾向于在某个合适的时机去执行某个动作,简单地理解为延迟执行的Intent。

2.PendingIntent的静态方法
getActivity()方法、getBroadcast()方法、getService()方法

  • 第一个参数依旧是Context
  • 第二个参数一般用不到, 通常都是传入0即可
  • 第三个参数是一个Intent对象
  • 第四个参数用于确定PendingIntent的行为
    FLAG_ ONE_SHOT、FLAG_ NO_ CREATE、FLAG_ CANCEL CURRENT和FLAG_ UPDATE_ CURRENT
    通常情况下这个参数传入0就可以了

3.NotificationCompat.Builder
这个构造器还可以再连缀一个setContentIntent()方法,接收的参数是PendingIntent对象。
-> 通过PendingIntent构建出一个延迟执行的“意图”,当用户点击这条通知时就会执行相应的逻辑。

4.优化NotificationTest 项目
给刚才的通知加上点击功能,让用户点击它的时候可以启动另一个活动。
①首先需要准备好另一个活动,右击com.example.notificationtest 包 → New → Activity → Empty Activity ,新建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: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)
                        .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                        .setContentIntent(pi)
                        .build();
                manager.notify(1, notification);
                break;
            default:
                break;
        }
    }
}

  • Intent intent = new Intent(this, NotificationActivity.class);
    使用Intent表达出我们想要启动NotificationActivity的“ 意图”
  • PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
    将构建好的Intent对象传人到PendingIntent的getActivity( )方法里,以得到PendingIntent的实例
  • Notification notification = new NotificationCompat.Builder(this).setContentIntent(pi)
    接着在NotificationCompat.Builder中调用setContentIntent()方法,把它作为参数传入即可。

④重新运行一下程序,并点击Send notice按钮,依旧会发出一条通知。
⑤然后下拉系统状态栏,点击一下该通知,就会看到NotificationActivity这个活动的界面了。

缺图

继续优化项目-点击后通知图标消失

如果我们没有在代码中对该通知进行取消,它就会一直显示在系统的状态栏上。
1.解决方法

  • 在NotificationCompat.Builder中再连缀一个setAutoCancel()方法
Notification notification = new NotificationCompat.Builder(this)
	……
	.setAutoCancel(true)
	.build();

setAutoCancel()方法传人true,就表示当点击了这个通知的时候,通知会自动取消掉。

  • 显式地调用NotificationManager 的cancel()方法将它取消
public class MainActivity extends AppCompatActivity  implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(1);
    }
}

这里我们在cancel()方法中传人了1,1是创建通知的时候给每条通知指定的id。

2.通知的进阶技巧

已经掌握了创建和取消通知的方法,并且知道了如何去响应通知的点击事件。

NotificationCompat.Builder 提供了丰富的API

  • setSound()方法 播放音频
    接收一个Uri参数,在指定音频文件的时候还需要先获取到音频文件对应的URI。

比如说,每个手机的/system/media/audio/ringtones目录下都有很多的音频文件,我们可以从中随便选一个音频文件,那么在代码中就可以这样指定:

Notification notification = new NotificationCompat.Builder(this)
	……
	.setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
	.build();
  • setVibrate()方法 振动
    vibrate这个属性是一个长整型的数组,用于设置手机静止和振动的时长,以毫秒为单位。
    下标为0的值表示手机静止的时长,下标为1的值表示手机振动的时长,下标为2的值又表示手机静止的时长,以此类推。

如果想要让手机在通知到来的时候立刻振动1秒,然后静止1秒,再振动1秒,代码就可以写成:

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

声明权限,编辑AndroidManifest.xml文件,加入如下声明

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.notificationtest">
    <uses-permission android:name="android.permission.VIBRATE" />
 	……
</manifest>
  • setLights ()方法 LED灯
    第一个参数用于指定LED灯的颜色
    第二个参数用于指定LeD灯亮起的时长,以毫秒为单位
    第三个参数用于指定LED灯暗去的时长,也是以毫秒为单位

当通知到来时,如果想要实现LED灯以绿色的灯光一闪一闪的效果,就可
以写成:

Notification notification = new NotificationCompat.Builder(this)
	……
	.setLights(Color.GREEN, 1000, 1000)
	.build();
  • setDefaults ()方法 默认效果
Notification notification = new NotificationCompat.Builder(this)
	……
	.setDefaults(NotificationCompat.DEFAULT_ALL)
	.build();

注意:只能在手机上看效果,模拟器无法演示

3.通知的高级功能

  • setStyle()方法
    允许我们构建出富文本的通知内容 -> 通知中不光可以有文字和图标,还可以包含更多的东西。
    接收一个NotificationCompat.Style参数,用来构建具体的富文本信息的,如长文字、图片等。

长文字

在开始使用setStyle()方法之前,我们先来做一个试验吧,之前的通知内容都比较短,如果设置成很长的文字会是什么效果呢?

Notification notification = new Notificat ionCompat. Builder(this)
	.setContentText ("Learn how to build notifications, send and sync data, and use voice actions. Get the official Android IDE and developer tools to build
apps for Android.")
	.build();

现在重新运行程序并触发通知。

缺图

通知内容是无法显示完整的,多余的部分会用省略号来代替。但如果你真的非常需要在通知当中显示一段长文字:

Notification notification = new NotificationCompat.Builder(this)
	……
	.setStyle(new NotificationCompat.BigTextStyle().bigText("Learn how to build notifications, send and sync data, and use voice actions. Get the official Android IDE and developer tools to build apps for Android."))
	.build();

创建了一个Notificat ionCompat.BigTextStyle对象,用于封装长文字信息的,我们调用它的bigText()方法并将文字内容传入就可以了。
再次重新运行程序并触发通知。

缺图

大图片

Notification notification = new NotificationCompat.Builder(this)
	……
	.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.big_image)))
	.build();
  • setStyle() 方法
    创建了一个NotificationCompat.BigPictureStyle 对象
    用于设置大图片的,调用bigPicture()方法并将图片传入
    事先准备好了一张图片,通过BitmapFactory 的decodeResource()方法将图片解析成Bitmap对象,再传人到bigPicture()方法中。

现在重新运行一下程序并触发通知

缺图

setPriority()方法 通知的重要程度

  • PRIORITY_DEFAULT
    默认的重要程度,和不设置效果是一样的
  • PRIORITY MIN
    最低的重要程度,系统可能只会在特定的场景才显示这条通知
  • PRIORITY_ LOW
    较低的重要程度,系统可能会将这类通知缩小,或改变其显示的顺序,将其排在更重要的通知之后
  • PRIORITY_ HIGH
    较高的重要程度,系统可能会将这类通知放大,或改变其显示的顺序,将其排在比较靠前的位置
  • PRIORITY_ MAX
    最高的重要程度,这类通知消息必须要让用户立刻看到,甚至需要用户做出响应操作,例如弹横幅。

具体写法如下:

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

缺图


四、调用摄像头和相册

1.调用摄像头拍照

①新建一个CameraAlbumTest项目

②修改activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <Button
        android:id="@+id/take_photo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Take Photo" />

    <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;

    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);
        picture = (ImageView) findViewById(R.id.picture);
        takePhoto.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();
                }
                if (Build.VERSION.SDK_INT < 24) {
                    imageUri = Uri.fromFile(outputImage);
                } else {
                    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);
            }
        });
    }

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

④代码分析

  1. 分别获取到Button和ImageView的实例
  2. 给Button注册上点击事件
  3. 在Button的点击事件里开始处理调用摄像头的逻辑
  • 创建File对象,用于存储拍照后的图片
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");

首先这里创建了一个File对象,用于存放摄像头拍下的图片,这里我们把图片命名为output_image.jpg,并将它存放在手机SD卡的应用关联缓存目录下。
应用关联缓存目录:指SD卡中专门用于存放当前应用缓存数据的位置,调用getExternalCacheDir()方法可以得到这个目录,具体的路径是/sdcard/Android/data//cache。
使用应用关联目录则可以跳过运行时权限处理。

  • 判断运行设备的系统版本
if (Build.VERSION.SDK_INT < 24) {
    imageUri = Uri.fromFile(outputImage);
} else {
    imageUri = FileProvider.getUriForFile(MainActivity.this, "com.example.cameraalbumtest.fileprovider", outputImage);
}

如果运行设备的系统版本低于Android 7.0,就调用Uri的fromFile()方法将File对象转换成Uri对象,这个Uri对象标识着output_image.jpg 这张图片的本地真实路径。
高于Android 7.0,就调用FileProvider的getUriForFile()方法将File对象转换成一个封装过的Uri对象。
getUriForFile()方法接收3个参数:第一个参数要求传入Context对象,第二个参数可以是任意唯一的字符串, 第三个参数则是我们刚刚创建的File对象。
之所以要进行这样一层转换,是因为从Android 7.0系统开始,直接使用本地真实路径的Uri被认为是不安全的,会抛出一个FileUriExposedException 异常。而FileProvider 则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的Uri共享给外部,从而提高了应用的安全性。

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

先构建出了一个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对象,然后把它设置到Image-View中显示出来。

⑤注册AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cameraalbumtest">
	<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <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:name="android.support.v4.content.FileProvider"
            android:authorities="com.example.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>
  • android:name=“android.support.v4.content.FileProvider”
    属性的值是固定的
  • android:authorities=“com.example.cameraalbumtest.fileprovider”
    属性的值必须要和刚才FileProvider . getUriForFile()方法中的第二个参数一致

  • 这里还在标签的内部使用来指定Uri的共享路径,并引用了一个@xml/file_paths 资源。
    还不存在,现创建。
  • 创建@xml/file_paths资源
    右击res目录→New->Directory,创建-一个xml目录。
    右击xml目录→New->File,创建一个file_ paths.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卡进行共享。
注意:在Android 4.4系统之前,访问SD卡的应用关联目录也是要声明权限
的,从4.4 系统开始不再需要权限声明。那么我们为了能够兼容老版本系统的手机,还需要在
AndroidManifest.xml中声明一下访问SD卡的权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.cameraalbumtest">
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ……
</manifest>

缺图

你可能感兴趣的:(Android,Studio,android)