第一行代码Android第八章内容中通知部分。
参考:
API 调用多媒体资源
1、先通过数据线把手机连接到电脑上。
2、进人到“关于手机”界面,然后对着最下面的版本号那一栏连续点击,就会让开发者选项显示出来。
3、然后如果你使用的是Windows 操作系统,还需要在电脑上安装手机的驱动。
4、进入到设置→开发者选项界面,并在这个界面中勾选中USB调试选项。
你会发现当前是有两个设备在线
模拟器+刚连接上的手机
运行一下当前项目,这时不会直接将程序运行到模拟器或者手机上,而是会弹出一个对话框让你进行选择。
当某个应用程序希望向用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知来实现。
发出一条通知后,手机最上方的状态栏中会显示一个通知的图标,下拉状态栏后可以看到通知的详细内容。
1.创建NotificationManager
NotificationManager来对通知进行管理,可以调用Context的getSystemService()方法获取到。
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();
3.NotificationManager 的notify( )方法
显示一个通知就可以写成:
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
2.PendingIntent的静态方法
getActivity()方法、getBroadcast()方法、getService()方法
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;
}
}
}
④重新运行一下程序,并点击Send notice按钮,依旧会发出一条通知。
⑤然后下拉系统状态栏,点击一下该通知,就会看到NotificationActivity这个活动的界面了。
如果我们没有在代码中对该通知进行取消,它就会一直显示在系统的状态栏上。
1.解决方法
Notification notification = new NotificationCompat.Builder(this)
……
.setAutoCancel(true)
.build();
setAutoCancel()方法传人true,就表示当点击了这个通知的时候,通知会自动取消掉。
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。
已经掌握了创建和取消通知的方法,并且知道了如何去响应通知的点击事件。
比如说,每个手机的/system/media/audio/ringtones目录下都有很多的音频文件,我们可以从中随便选一个音频文件,那么在代码中就可以这样指定:
Notification notification = new NotificationCompat.Builder(this)
……
.setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
.build();
如果想要让手机在通知到来的时候立刻振动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>
当通知到来时,如果想要实现LED灯以绿色的灯光一闪一闪的效果,就可
以写成:
Notification notification = new NotificationCompat.Builder(this)
……
.setLights(Color.GREEN, 1000, 1000)
.build();
Notification notification = new NotificationCompat.Builder(this)
……
.setDefaults(NotificationCompat.DEFAULT_ALL)
.build();
在开始使用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();
现在重新运行一下程序并触发通知
具体写法如下:
Notification notification = new NotificationCompat.Builder(this)
……
.setPriority(NotificationCompat.PRIORITY_MAX)
.build();
<?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则是用于将拍到的图片显示出来。
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;
}
}
}
File outputImage = new File(getExternalCacheDir(), "output_image.jpg");
首先这里创建了一个File对象,用于存放摄像头拍下的图片,这里我们把图片命名为output_image.jpg,并将它存放在手机SD卡的应用关联缓存目录下。
应用关联缓存目录:指SD卡中专门用于存放当前应用缓存数据的位置,调用getExternalCacheDir()方法可以得到这个目录,具体的路径是/sdcard/Android/data/
使用应用关联目录则可以跳过运行时权限处理。
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中显示出来。
<?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>
<?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>