Android生成二维码--拍照或从相册选取图片

    拍照或从相册选择图片是咱们平常开发中常常使用到的,能够说是必须掌握的东西。上一篇我介绍了如何生成自定义二维码《Android生成自定义二维码》,其中logo和代替黑色色块的图片都是写死的,因此如今咱们就来实现拍照或者从相册选取图片这个功能。 

先看效果图:html

      

      

 

 

拍照

1.启动相机程序 

   拍照能够直接启动系统的相机程序,代码以下android

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()方法就能获得返回值。git

 

2.指定保存路径github

    上面的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对象。安全

 

   完整代码ide

/** * 拍照 */
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); }

 

获取拍照结果:ui

@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,可先进行压缩。this

 

    最后别忘了在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

相关文章
相关标签/搜索