zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    Servlet介绍(一)
    iOS Dev (50)用代码实现图片加圆角
    Codeforces Round #265 (Div. 2) D. Restore Cube 立方体推断
    JVM:垃圾回收机制和调优手段
    Memcachedclient-XMemcached使用
    JVM中类的卸载机制
    血型统计
    iOS 事件传递及响应过程
    java 对象参数去空格方式
    spring aop 一个挡板例子
  • 原文地址:https://www.cnblogs.com/xch-yang/p/9879819.html
Copyright © 2011-2022 走看看