仅为本人学习《Android编程权威指南》记录笔记,可能有诸多错误不妥之处
隐式 intent 调用相机是从《Android编程权威指南》第十六章中学习的:
其实相当于我们用 显式 intent 去启动自己的activity,不过 隐式 intent 启动的是系统应用,我们只需要在 intent 中指出要启动 activity 的功能描述,操作系统就会负责启动相应的应用。
一. 文件存储
我们可以使用 Context类 提供的文件和目录处理方法将文件保存在设备上的私有存储空间(保存SQLite数据文件),但是这只限于存储的文件供应用内部使用,如果是其他应用要读写你的文件,就没那么简单了。
ContentProvider 允许暴露内容 URI 给其他应用,这些应用就可以通过 URI下载或向其写入文件。
Google提供了一个名为 FileProvider的便利类,我们只需要配置一下参数可以了!
1. 首先,声明FileProvider为ContentProvider,并指定权限。
在AndroidMainifest.xml中添加FileProvider声明:
<activity>...</activity>
<provider
android:name="androidx.core.content.FileProvider"
//书上是android.support.v4.content.FileProvider,因为从android9.0
//(API 28)开始,android.supprot.xxx大多都迁移到了androidx中
android:authorities=" com.xxx.xxx... .fileprovider"
//com.xxx.xxx ...为应用包名
android:exported="false"
//除了你自己以及给予授权的人,其他任何人都不能使用你的FileProvider
android:grantUriPermissions="true">
//给其他应用授权,允许它们向你指定位置的URI写入文件
//这个位置信息放在intent中对外发出
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/files"/>
//关联路径描述资源即让FIleProvider能找到files.xml(还未创建)
</provider>
这里的权限是指一个位置:文件保存地 。 把 FileProvider 与指定的位置关联起来,就相当于给发出请求的其他应用一个目标地。
2. 接下来配置 FileProvider,让它知道该暴露哪些文件。新建一个xml资源文件:
res/xml/files.xml
<paths>
<files-path name="xxx" path="."/>
</paths>
这是一个描述性文件,其意义为:把私有存储空间的根路径映射为 “xxx”,这个名仅供FileProvider内部使用,不必应用它。
3. 最后指定文件的存放位置就可以了,直接通过Context类的 getFilesDir() 方法得到/data/data/包名/files目录 即activity.getApplicationContext().getFilesDir(),然后就可以直接创建文件了。
二. 使用相机
1. 启动相机当然是为了拍照上传,所以先定义REQUEST常量和照片文件对象:
private static final int REQUEST_CAMERA = 2;
private File mPhotoFile = new File(getAcitivity()
.getApplicationContext().getFilesDir(),xxxx);
//这里的Context对象用的是整个应用程序的上下文
2. 新建并启动相机Intent
final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//创建相机Intent
PackageManager packageManager = getAcitivity().getPackageManager();
//得到PackageManager对象
final boolean canTakePhoto = mPhotoFile != null &&
captureIntent.resolveActivity(packageManager) != null
//判断是否有相应相机Intent的应用
mImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (canTakePhoto) { //判断相机Intent是否可用
Uri uri = FileProvider.getUriForFile(getActivity(),
"com.xxx ... .fileprovider", mPhotoFile);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//FileProvider.getUriForFile()会把本地文件路径转换为相机可见的Uri形式
List<ResolveInfo> cameraActivities = getActivity()
.getPackageManager().queryIntentActivities(captureIntent,
PackageManager.MATCH_DEFAULT_ONLY);
//获取所有的相机Activity
for (ResolveInfo activity : cameraActivities) {
getActivity().grantUriPermission(activity.activityInfo.packageName,
uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}//为所有的能响应相机Intent的acitivity授予可以写入文件的权限
startAcitivityForResult(captureIntent,REQUEST_CAMERA);
}
}
});
3. 重写 onAcitivityResult 方法保存图片
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
if (requestCode == REQUEST_CAMERA) {
Uri uri = FileProvider.getUriForFile(getActivity(),
"com.lifeifanzs.memorableintent.fileprovider",
mPhotoFIle;
getActivity().revokeUriPermission(uri,
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//相机已经保存文件,就再次调用权限关闭文件的访问
updatePhotoView();//处理照片文件
}
}
三. 使用相册
使用相册保存图片是通过获取选择文件的 Uri 来保存的,所以先定义一个Uri类型全局变量
private Uri mImageUri;
新建相册Intent:
Intent albumIntent = new Intent(Intent.ACTION_PICK);//创建相册Intent
albumIntent.setDataAndType(MediaStore.Images
.Media.EXTERNAL_CONTENT_URI, "image/*");//设置数据和文件类型
四. chooserIntent应用选择器
新建chooserIntent
final Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);//创建选择器Intent
chooserIntent.putExtra(Intent.EXTRA_INTENT, albumIntent);//保存albumIntent
......
mImageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (canTakePhoto) { //判断相机Intent是否可用
......
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS
, new Intent[]{captureIntent});//保存相机Intent
}
startActivityForResult(chooserIntent, REQUEST_CAMERA);
});
处理intent中的数据
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode != Activity.RESULT_OK) {
return;
}
if (requestCode == REQUEST_CAMERA) {
......
if (null != data) { //如果intent中保存有数据,即打开的是相册
mImageUri = data.getData();
}
updatePhotoView();//处理照片文件
}
}
在 updatePhotView() 中的通过判断 mPhotoFile == null || !mPhotoFile.exists() 和 mImageUri==null 来决定是处理mPhotoFile还是 mImageUri
if (mPhotoFile == null || !mPhotoFile.exists()) { //如果对应文件为null或不存在
if (mImageUri!= null) { //处理mImageUri }
} else { //处理mPhotoFile }