Android实现拍照(兼容7.0)

拍照版本之间的重要区别

6.0以下的版本:只要在AndroidManifest中注明相机的使用权限,SDCard的读写2权限,就能在正常的写功能了

6.0:6.0以后Google不再允许开发者直接或许应用的权限,需要在用户知情的情况下授予权限,这便是运行时权限。相机,读,写3个权限除了要在AndroidManifest先声明,在写功能代码的时候还要做动态申请的操作。

7.0+: 众所周知Android四大组件之一内容提供者的作用,便是在不同的应用程序之间实现数据共享, Android 从 N 版本开始禁止通过 file://Uri 的方式在不同 App 之间共享文件,如此以来7.0版本以下直接获取图片文件路径的方法行不通了,所以我们写的应用,就需要通过内容提供器的方式,来获取系统文件管理器中目标文件的路径。

6.0 以下版本调用相机拍照

权限

<uses-feature android:name="android.hardware.camera" />
    <!--相机权限-->
    <uses-permission android:name="android.permission.CAMERA" />
    <!--写入SD卡的权限:如果你希望保存相机拍照后的照片-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--读取SD卡的权限:打开相册选取图片所必须的权限-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

拍照代码

 /**
     * 拍照
     *
     * @param view
     */
    @Event(R.id.btn_photo)
    private void takePhoto(View view) {
        //启动照相机拍照
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        photoPath = Environment.getExternalStorageDirectory() + "/xyh.jpg";
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(photoPath)));
        startActivityForResult(intent, PHOTO_REQUEST);
    }

6.0 版本调用相机拍照

和6.0 以下版本相比多了一个动态申请权限,拍照代码一样

 /**
     * 拍照
     *
     * @param view
     */
    @Event(R.id.btn_photo)
    private void takePhoto(View view) {
        //启动照相机拍照
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        photoPath = Environment.getExternalStorageDirectory() + "/xyh.jpg";
        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(photoPath)));
        startActivityForResult(intent, PHOTO_REQUEST);
    }

7.0 版本调用相机拍照

在用Android 7.0版本时,出现了异常 FileUriExposedException,就是在传递uri时出错

百度了一下,说是Android 7.0后又修改了文件权限,可以使用FileProvider解决

1.在 res 目录下新建文件夹 xml 然后创建资源文件 filepaths(随意名字)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--external-path:代表的根目录: Environment.getExternalStorageDirectory()-->
    <external-path
        name="images"
        path="拍照/" /> //存放照片文件夹
</resources>
<files-path/> //代表的根目录: Context.getFilesDir()
<external-path/> //代表的根目录: Environment.getExternalStorageDirectory()
<cache-path/> //代表的根目录: getCacheDir()

2.在manifest中添加provider


        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.xiaoyehai.takephoto.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepaths" />
        </provider>

exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限。

3.调用相机拍照,图片得存储吧,存储图片又需要权限,因此动态申请权限
AndroidManifest.xml文件中:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

4.java代码



/**
 * android7.0拍照
 */
public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_PERMISSION_CODE = 559;

    private static final int REQUEST_CAMERA = 648;

    private ImageView mImageView;

    private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
    
    private File mFile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.iv);
    }

    public void doclick(View view) {
        if (Build.VERSION.SDK_INT >= 23) {
            if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE);
            } else {
                takePhoto();
            }
        } else {
            takePhoto();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == REQUEST_PERMISSION_CODE && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            takePhoto();
        } else {
            // 没有获取 到权限,从新请求,或者关闭app
            Toast.makeText(this, "需要存储权限", Toast.LENGTH_SHORT).show();
        }
    }

    private void takePhoto() {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        mFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                + "/拍照/" + System.currentTimeMillis() + ".jpg");
        if (!mFile.getParentFile().exists()) {
            mFile.getParentFile().mkdirs();
        }

        //改变Uri com.xiaoyehai.takephoto.fileprovider注意和清单文件中一致中的一致
        Uri uri = FileProvider.getUriForFile(this, "com.xiaoyehai.takephoto.fileprovider", mFile);
        //添加权限
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent, REQUEST_CAMERA);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
            Uri uri = FileProvider.getUriForFile(this, "com.xiaoyehai.takephoto.fileprovider", mFile);

            //content://com.xiaoyehai.takephoto.fileprovider/images/1533796434054.jpg
            Log.e("xyh", "onActivityResult: " + uri);

            mImageView.setImageBitmap(BitmapFactory.decodeFile(mFile.getAbsolutePath()));

            //在手机相册中显示刚拍摄的图片
            Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri contentUri = Uri.fromFile(mFile);
            mediaScanIntent.setData(contentUri);
            sendBroadcast(mediaScanIntent);
        }
    }
}


如果图片太大,会造成OOM,可以对图片进行压缩

  @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
            Uri uri = FileProvider.getUriForFile(this, "com.xiaoyehai.takephoto.fileprovider", mFile);

            //content://com.xiaoyehai.takephoto.fileprovider/images/1533796434054.jpg
            Log.e("xyh", "onActivityResult: " + uri);

            //如果图片太大,可以对图片进行压缩
            Bitmap bitmap = BitmapUtils.compressBitmap(mFile.getAbsolutePath(), mImageView.getMeasuredWidth(), mImageView.getMeasuredHeight());
            mImageView.setImageBitmap(bitmap);

            //在手机相册中显示刚拍摄的图片
            Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri contentUri = Uri.fromFile(mFile);
            mediaScanIntent.setData(contentUri);
            sendBroadcast(mediaScanIntent);
        }
    }

BitmapUtils

package com.xiaoyehai.takephoto;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;

import java.io.ByteArrayOutputStream;

/**
 * Created by xiaoyehai on 2018/3/2 0002.
 */

public class BitmapUtils {

    /**
     * Base64字符串转换成图片
     *
     * @param string
     * @return
     */
    public static Bitmap stringtoBitmap(String string) {
        Bitmap bitmap = null;
        try {
            byte[] bitmapArray;
            bitmapArray = Base64.decode(string, Base64.DEFAULT);
            bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bitmap;
    }

    /**
     * 图片转换成Base64字符串
     *
     * @param bitmap
     * @return
     */
    public static String bitmaptoString(Bitmap bitmap) {
        //将Bitmap转换成字符串
        String string = null;
        ByteArrayOutputStream bStream = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, bStream);
        byte[] bytes = bStream.toByteArray();
        string = Base64.encodeToString(bytes, Base64.DEFAULT);
        return string;
    }

    /**
     * 对资源文件中的图片进行压缩
     *
     * @param context    上下文对象
     * @param imageId    压缩图片的id
     * @param destWidth  显示图片的控件宽度
     * @param destHeight 显示图片的控件高度
     * @return
     */
    public static Bitmap compressBitmap(Context context, int imageId, int destWidth, int destHeight) {
        //创建一个可选项对象,该对象用于配置图片的处理参数
        BitmapFactory.Options options = new BitmapFactory.Options();

        //第一次采样,该属性设置为true只会加载图片的边框进来,并不会加载图片具体的像素点,也就是说不会把图片加载到内存中
        options.inJustDecodeBounds = true;

        //加载图片,该方法只是从图片文件中读取图片的宽和高信息,而没有真正的加载到内存中
        BitmapFactory.decodeResource(context.getResources(), imageId, options);

        //获取图片的宽和高
        int imageWidth = options.outWidth;
        int imageHeight = options.outHeight;

        //定义缩放比例
        int sampleSize = 1;
        while (imageWidth / sampleSize > destHeight || imageHeight / sampleSize > destWidth) {
            //如果宽高的任意一方的缩放比例没有达到要求,都继续增大缩放比例
            //sampleSize应该为2的n次幂,如果给sampleSize设置的数字不是2的n次幂,那么系统会就近取值
            sampleSize *= 2;
        }

        //第二次采样,使用缩放比例进行缩放加载图,加载器就会返回图片了
        options.inJustDecodeBounds = false;

        //设置压缩比
        options.inSampleSize = sampleSize;

        /**
         * inPreferredConfig设置图片的质量:
         * ARGB_8888:默认的图片质量,也是最好的质量,32位,4个字节
         * ARGB_4444:是ARGB_8888占用内存的一半,但质量比较低,16位,不推荐使用
         * RGB_565:不带透明度,是ARGB_8888占用内存的一半,质量不错,16位,推荐使用
         * ALPHA_8:
         */
        //设置图片质量
        options.inPreferredConfig = Bitmap.Config.RGB_565;

        //返回压缩后的图片
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), imageId, options);

        //Log.i("info", "压缩后图片的大小:" + bitmap.getByteCount());
        return bitmap;
    }

    /**
     * 对内存卡中的图片进行压缩
     *
     * @param path       图片路径
     * @param destWidth  显示图片的控件宽度
     * @param destHeight 显示图片的控件高度
     * @return
     */
    public static Bitmap compressBitmap(String path, int destWidth, int destHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        int imageWidth = options.outWidth;
        int imageHeight = options.outHeight;
        int sampleSize = 1;
        while (imageWidth / sampleSize > destHeight || imageHeight / sampleSize > destWidth) {
            sampleSize *= 2;
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);

        //Log.i("info", "压缩后图片的大小:" + bitmap.getByteCount());
        return bitmap;
    }

    /**
     * 通过网络请求返回的字节数据进行二次采样并返回bitmap
     *
     * @param bytes
     * @param destWidth  显示图片的控件宽度
     * @param destHeight 显示图片的控件高度
     * @return
     */
    public static Bitmap compressBitmap(byte[] bytes, int destWidth, int destHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
        int imageWidth = options.outWidth;
        int imageHeight = options.outHeight;
        int sampleSize = 1;
        while (imageWidth / sampleSize > destHeight || imageHeight / sampleSize > destWidth) {
            sampleSize *= 2;
        }

        options.inJustDecodeBounds = false;
        options.inSampleSize = sampleSize;
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);

        // Log.i("info", "压缩后图片的大小:" + bitmap.getByteCount());
        return bitmap;
    }
}

最终版本:兼容代码

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_PERMISSION_CODE = 559;

    private static final int REQUEST_CAMERA = 648;

    //注意和清单文件中的一致
    private String authority = "com.xiaoyehai.demo.fileprovider";

    private ImageView mImageView;

    private String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};

    private File mImageFile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mImageView = (ImageView) findViewById(R.id.iv);

        //申请权限
        if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE);
        }
    }

    /**
     * 拍照
     *
     * @param view
     */
    public void takePhoto(View view) {

        //创建存储照片的文件
        mImageFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                + "/拍照/" + System.currentTimeMillis() + ".jpg");

        // /storage/emulated/0/拍照/1571752870386.jpg
        Log.e("xyh", "takePhoto: " + mImageFile);


        if (!mImageFile.getParentFile().exists()) {
            mImageFile.getParentFile().mkdirs();
        }

        Uri uri;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //Android 7.0及以上获取文件 Uri
            uri = FileProvider.getUriForFile(this, authority, mImageFile);
        } else {
            //Android 7.0以前获取文件 Uri
            uri = Uri.fromFile(mImageFile);
        }

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(intent, REQUEST_CAMERA);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CAMERA && resultCode == RESULT_OK) {
            Uri uri = FileProvider.getUriForFile(this, authority, mImageFile);

            //content://com.xiaoyehai.demo.fileprovider/images/1571752746254.jpg
            Log.e("xyh", "onActivityResult: " + uri);

            Bitmap bitmap = BitmapFactory.decodeFile(mImageFile.getAbsolutePath());
            Log.e("xyh", "压缩前图片大小:" + bitmap.getByteCount());

            //如果图片太大,会造成OOM,可以对图片进行压缩
            //使用Luban图片压缩框架对图片进行压缩
            Luban.with(this)
                    .load(mImageFile)
                    .ignoreBy(100)
                    .setTargetDir(getPath())
                    .filter(new CompressionPredicate() {
                        @Override
                        public boolean apply(String path) {
                            return !(TextUtils.isEmpty(path) || path.toLowerCase().endsWith(".gif"));
                        }
                    })
                    .setCompressListener(new OnCompressListener() {
                        @Override
                        public void onStart() {
                            // TODO 压缩开始前调用,可以在方法内启动 loading UI
                        }

                        @Override
                        public void onSuccess(File file) {
                            // TODO 压缩成功后调用,返回压缩后的图片文件
                            Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                            Log.e("xyh", "压缩后图片大小:" + bitmap.getByteCount());
                            mImageView.setImageBitmap(bitmap);
                        }

                        @Override
                        public void onError(Throwable e) {
                            // TODO 当压缩过程出现问题时调用
                            Log.e("xyh", "onError: " + e.getMessage());
                        }
                    }).launch();


            //在手机相册中显示刚拍摄的图片
            Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
            Uri contentUri = Uri.fromFile(mImageFile);
            mediaScanIntent.setData(contentUri);
            sendBroadcast(mediaScanIntent);
        }
    }

    private String getPath() {
        // /storage/emulated/0/拍照
        String path = mImageFile.getParentFile().getAbsolutePath();
        Log.e("xyh", "getPath: " + path);
        return path;
    }
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值