Android生成二维码--拍照或从相册选取图片
拍照或从相册选择图片是我们日常开发中经常使用到的,可以说是必须掌握的东西。上一篇我介绍了如何生成自定义二维码《Android生成自定义二维码》,其中logo和代替黑色色块的图片都是写死的,所以现在我们就来实现拍照或者从相册选取图片这个功能。
先看效果图:
拍照
1.启动相机程序
拍照可以直接启动系统的相机程序,代码如下
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, TAKE_PHOTO);
这里我们利用一个隐式Intent来启动相机程序,其中action类型:android.media.action.IMAGE_CAPTURE 表示启动相机应用并请求一张图片。创建了Intent对象,还需指定图片的保存路径,调用Intent的putExtra()方法并传入保存路径即可,最后调用startActivityForResult启动活动,重写onActivityResult()方法就能得到返回值。
2.指定保存路径
上面的intent中指定了保存路径,也就是代码中的imageUri。首先需要创建一个File对象用来存放图片,并使用getExternalCacheDir()方法将图片放入SD卡当前应用包名下的缓存目录中。
接着需要将File对象转换成Uri对象,需要进行版本判断,7.0以下直接调用Uri的fromFile方法,得到的是图片本地真实路径。7.0以上直接使用真实路径会被认为不安全,会抛出异常,所以需要使用特殊的内容提供器FileProvider,它是ContentProvider的一个子类,作用便是将受限的Uri转换为可共享的Uri。
既然使用内容提供器,当然需要进行注册了,AndroidManifest.xml中加入:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.xch.generateqrcode.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
meta-data标签中的内容是用来添加一个共享目录的,引用了一个resource资源,所以需要在 res/xml 目录下新建这个 xml 文件,用于存放应用需要共享的目录文件。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--path设为空值表示将整个sd卡共享-->
<external-path
name="my_images" path="" />
</paths>
接下来在代码中调用FileProvider的getUriForFile()方法生成Uri对象,需要传入三个参数,第一个参数是上下文,第二个参数便是 Manifest 文件中注册 FileProvider 时设置的 authorities 属性值,第三个参数为要共享的文件,也就是之前创建的File对象。
完整代码
/**
* 拍照
*/
private void takePhoto() {
// 创建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.xch.generateqrcode.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 {
// 读取拍照结果
logoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
} catch (Exception e) {
e.printStackTrace();
}
}
break;
default:
break;
}
}
读取拍照结果利用ContentResolver的openInputStream()方法,并传入刚才图片的保存路径即可获取图片字节流,再利用BitmapFactory的decodeStream()方法转为Bitmap格式,如果担心OOM,可先进行压缩。
最后别忘了在AndroidManifest.xml中加入权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
从相册选择图片
- 打开相册
同样用Intent,action类型为android.intent.action.GET_CONTENT ,并给intent设置type为图片,如下:
Intent intent = new Intent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent, CHOOSE_PHOTO);
- 获取结果
在回调onActivityResult即可对返回结果进行处理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case CHOOSE_PHOTO:
if (resultCode == RESULT_OK) {
// 判断手机系统版本号
if (Build.VERSION.SDK_INT >= 19) {
// 4.4及以上系统使用这个方法处理图片
handleImageOnKitKat(data);
} else {
// 4.4以下系统使用这个方法处理图片
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}
由于Android 4.4开始,不再返回图片真实的Uri,而是一个封装过的Uri,因此需要分别进行处理。Android 4.4之前直接调用getImagePath()方法通过Uri和selection就可获取真实路径,如下
/**
* 4.4版本以前,直接获取真实路径
* @param data
*/
private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null);
//显示图片
displayImage(imagePath);
} private String getImagePath(Uri uri, String selection) {
String path = null;
// 通过Uri和selection来获取真实的图片路径
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;
}
Android 4.4之后,需要解析封装过的Uri,如果Uri是document类型,则通过document id处理,如果是content类型的Uri,则使用普通方式处理,如果是file类型的Uri,直接获取图片路径即可。获取到路径后即可显示,如下
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类型的Uri,直接获取图片路径即可
imagePath = uri.getPath();
}
displayImage(imagePath); // 根据图片路径显示图片
}
显示图片
/**
* 显示图片
* @param imagePath 图片路径
*/
private void displayImage(String imagePath) {
if (imagePath != null) {
logoBitmap = BitmapFactory.decodeFile(imagePath);
// 显示图片
picture_logo.setImageBitmap(logoBitmap);
} else {
Toast.makeText(this, "获取图片失败", Toast.LENGTH_SHORT).show();
}
}
动态权限申请
由于涉及了敏感权限 WRITE_EXTERNAL_STORAGE ,所以需要进行权限的动态申请,如下
//动态权限申请
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
//打开相册
openAlbum();
}
@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 {
Toast.makeText(this, "你拒绝了权限申请,可能无法打开相册哟", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
上面的代码就是动态申请权限的流程,首先判断用户是不是已经给我们权限授权了,使用ContextCompat.checkSelfPermission()方法,第一个参数是Context,第二个参数是具体的权限名称,如果等于PackageManager.PERMISSION_GRANTED表明已授权,不等于就是没有授权。
如果已授权就直接做后面的操作,如果没有授权,需要调用ActivityCompat.requestPermissions()方法申请授权,第一个参数是当前Activity实例,第二个参数是权限数组,第三个是请求码。
用户的选择将会回调到onRequestPermissionsResult()方法中,授权结果封装在grantResults参数中,如果被授权,则打开相册,否则提示用户未打开权限无法使用。
源码已更新至GitHub,地址:https://github.com/yangxch/GenerateQRCode
Android生成二维码--拍照或从相册选取图片的更多相关文章
- Android生成二维码--保存和分享二维码图片
之前写过生成自定义二维码的两篇文章:<Android生成自定义二维码><Android生成二维码–拍照或从相册选取图片>,下面就介绍一下Android应用内如何保存以及分享二维 ...
- android生成二维码
新建项目 布局截图如下(一个输入框,一个按钮,一个imageview),输入想要的东西(文字,数字,网站链接等)然后点击按钮生成二维码,然后可以扫描识别. 首先需要一个谷歌的一个jar包 activi ...
- Android zxing 解析二维码,生成二维码极简demo
zxing 官方的代码很多,看起来很费劲,此demo只抽取了有用的部分,实现了相机预览解码,解析本地二维码,生成二维码三个功能. 简化后的结构如下: 废话少说直接上代码: BaseDecodeHand ...
- Android平台二维码之生成,扫描 & 识别
1.二维码的前世今生 “二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的:在代码编制上巧妙地利 ...
- Android实例-实现扫描二维码并生成二维码(XE8+小米5)
相关资料: 第三方资料太大没法写在博文上,请下载CSDN的程序包. 程序包下载: http://download.csdn.net/detail/zhujianqiangqq/9657186 注意事项 ...
- 玩转Android之二维码生成与识别
二维码,我们也称作QRCode,QR表示quick response即快速响应,在很多App中我们都能见到二维码的身影,最常见的莫过于微信了.那么今天我们就来看看怎么样在我们自己的App中集成二维码的 ...
- Android二维码开源项目zxing用例简化和生成二维码、条形码
上一篇讲到:Android二维码开源项目zxing编译,编译出来后有一个自带的測试程序:CaptureActivity比較复杂,我仅仅要是把一些不用的东西去掉,用看起来更方便,二维码和条形码的流行性自 ...
- android 使用开源库zxing生成二维码,扫描二维码【转】
转自:http://blog.csdn.net/qq_16064871/article/details/52422723 zxing是一个开放源码的,用Java实现的多种格式的1D/2D条码图像处理库 ...
- Android:使用ZXing生成二维码(支持加入Logo图案)
ZXing是谷歌的一个开源库.能够用来生成二维码.扫描二维码.本文所介绍的是第一部分. 首先上效果图: ZXing相关各种文件官方下载地址:https://github.com/zxing/zxing ...
随机推荐
- 652. Find Duplicate Subtrees找出重复的子树
[抄题]: 就是出现了多次的子树,可以只包括一个点. Given a binary tree, return all duplicate subtrees. For each kind of dupl ...
- 698. Partition to K Equal Sum Subsets 数组分成和相同的k组
[抄题]: Given an array of integers nums and a positive integer k, find whether it's possible to divide ...
- [leetcode]23. Merge k Sorted Lists归并k个有序链表
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. I ...
- 微软Office Online服务安装部署(三)
现在开始配置两台服务器,两台服务器的IP: Server: 10.1.3.89 Client: 10.1.3.92 1.在Client中,.打开网络属性,找到ipv4的配置,将dns 改成域控制器的 ...
- CSS 样式中的两个方法
在很多时候,我们需要LI开头空一点距离.结尾不能再有下划线了.这个效果在以前是很难实现的.但是有了下面两个选择器,非常容易做出这种东西. .slideTxtBox .bd ul > :first ...
- scrollIntoView()窗口滚动
1.某DIV窗口滚动到顶部: document.getElementById("某DIV的ID").scrollIntoView(true); 2.某DIV窗口滚动到底部: doc ...
- PYthon第十二天
1. 生成器 生成器的本质是迭代器, 最简单的生成器函数如下: def foo(x): 1-4行定义了一个简单的生成器函数 yield x+1 yield 和 return 不同, return 结束 ...
- zeromq学习记录(四)使用ZMQ_ROUTER ZMQ_DEALER
/************************************************************** 技术博客 http://www.cnblogs.com/itdef/ ...
- js使用锚点回到顶部
使用锚点链接是一种简单的返回顶部的功能实现.该实现主要在页面顶部放置一个指定名称的锚点链接,然后在页面下方放置一个返回到该锚点的链接,用户点击该链接即可返回到该锚点所在的顶部位置 <body s ...
- 1.6Eigen中系数运算Reductions, visitors and broadcasting
Eigen::Matrix2d mat; mat<<,, ,; cout<<"矩阵所有系数之和:"<<mat.sum();//1+2+3+4=1 ...