首页 > 代码库 > 拍照、本地图片工具类(兼容至Android7.0)

拍照、本地图片工具类(兼容至Android7.0)

 

拍照、本地图片工具类:解决了4.4以上剪裁会提示“找不到文件”和6.0动态授予权限,及7.0报FileUriExposedException异常问题。
package com.hb.weex.util;

import android.Manifest;
import android.app.Activity;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.FragmentActivity;
import android.support.v4.content.FileProvider;
import android.view.View;
import android.widget.Button;
import com.hb.common.android.util.Log;
import com.hb.weex.R;
import com.hb.weex.util.ToastUtil;
import com.hb.weex.util.UploadAvatarUtil;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import kr.co.namee.permissiongen.GrantPermissionActivity;
import kr.co.namee.permissiongen.PermissionGen;
import kr.co.namee.permissiongen.internal.Utils;

/**
 * 拍照管理的类
 * Created by cjy on 2017/4/03.
 */
public class TakePhotoManager implements View.OnClickListener,GrantPermissionActivity.OnGrantedListener {
    /**
     * 权限列表:写权限 读权限 调用摄像头权限
     */
    private static final String[] PERMISSIONS = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,
            Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA};

    /* 上下文 */
    private Activity context;

    /* 对话框 */
    private Dialog mSelectPhotoDialog;

    /* 图片的存储地址-父路径 */
    private String mHeadImageFileParentPath = "";

    /* 照相机拍照后的照片 */
    private String mCameraImageFileName = "";

    /* 头像名称 */
    private String mHeadImageFileName = "";

    private Button mBtnTakePhoto;//拍照
    private Button mBtnPickPhoto;//选择本地图片
    private Button mBtnCancel;//取消

    private Uri imageUri;//原图保存地址


    public TakePhotoManager(Activity context) {
        this.context = context;
        mHeadImageFileParentPath = Environment.getExternalStorageDirectory() + File.separator+ context.getPackageName()+File.separator;
        mCameraImageFileName = "temp_camera_image.jpg";
        mHeadImageFileName = "tmp_head_image.jpg";
    }

    public void setmHeadImageFileParentPath(String mHeadImageFileParentPath) {
        this.mHeadImageFileParentPath = mHeadImageFileParentPath;
    }

    public void setmCameraImageFileName(String mCameraImageFileName) {
        this.mCameraImageFileName = mCameraImageFileName;
    }

    public void setmHeadImageFileName(String mHeadImageFileName) {
        this.mHeadImageFileName = mHeadImageFileName;
    }

    /**
     * 显示头像弹出窗
     */
    public void showSelectPhotoDialog() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//23
            //Android6.0及以上,需要动态授予权限
            // 验证所有权限是否都已经授权了
            List<String> deniedPermissions = Utils.findDeniedPermissions(this.context, PERMISSIONS);
            if (deniedPermissions.size() == 0) {
                showDialog();
            } else {
                Intent intent = new Intent(this.context, GrantPermissionActivity.class);
                intent.putExtra(GrantPermissionActivity.PARAM_PERMISSION_NAME_LIST, PERMISSIONS);
                GrantPermissionActivity.mGrantedListener = this;
                this.context.startActivity(intent);
            }
        }else{
            showDialog();
        }
    }

    private void showDialog(){
        View view = context.getLayoutInflater().inflate(R.layout.dlg_select_photo,
                null);
        mBtnTakePhoto = (Button) view.findViewById(R.id.btn_take_photo);
        mBtnPickPhoto = (Button) view.findViewById(R.id.btn_pick_photo);
        mBtnCancel = (Button) view.findViewById(R.id.btn_cancel);
        mBtnTakePhoto.setOnClickListener(this);
        mBtnPickPhoto.setOnClickListener(this);
        mBtnCancel.setOnClickListener(this);
        mSelectPhotoDialog = UploadAvatarUtil.createDialog(context, view,
                R.style.transparentFrameWindowStyle, R.style.select_photo_dialog_animstyle);
    }

    /**
     * 关闭头像弹出窗
     */
    public void hideSelectPhotoDialog() {
        if (mSelectPhotoDialog != null && mSelectPhotoDialog.isShowing()) {
            mSelectPhotoDialog.dismiss();
        }
    }

    /**
     * 拍照
     */
    public void takePhoto(Activity context, String imgUrl, String imageFileNameTemp) {
        File file = UploadAvatarUtil.getFile(imgUrl, imageFileNameTemp);
        // 打开相机
        Intent intentFromCapture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //FileUriExposedException这个异常只会在Android 7.0 + 出现,当app使用file:// url 共享给其他app时, 会抛出这个异常。
            //因为在android 6.0 + 权限需要 在运行时候检查, 其他app 可能没有读写文件的权限, 所以google在7.0的时候加上了这个限制。官方推荐使用 FileProvider 解决这个问题。
            intentFromCapture.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION ); //添加这一句表示对目标应用临时授权该Uri所代表的文件
            imageUri = FileProvider.getUriForFile(context, "com.hb.weex.accountant.fileprovider", file);//通过FileProvider创建一个content类型的Uri
        } else {
            imageUri = Uri.fromFile(file);
        }
        intentFromCapture.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
        context.startActivityForResult(intentFromCapture, UploadAvatarUtil.CAMERA_REQUEST_CODE);
    }

    /**
     * 选择本地图片
     */
    public void pickPhoto(Activity context) {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
        context.startActivityForResult(intent, UploadAvatarUtil.IMAGE_REQUEST_CODE);
    }

    public Object onActivityResult(Context context,int requestCode, int resultCode, Intent data) {
        //关闭头像弹出窗
        hideSelectPhotoDialog();
        if (resultCode != Activity.RESULT_CANCELED) {
            switch (requestCode) {
                case UploadAvatarUtil.IMAGE_REQUEST_CODE:// 选择本地图片返回
                    if (null != data) {//为了取消选取不报空指针用的
                        imageUri = data.getData();
                        startPhotoZoom(context, imageUri , 1);
                    }
                    break;
                case UploadAvatarUtil.CAMERA_REQUEST_CODE:// 拍照返回
                    if (UploadAvatarUtil.hasSdcard()) {
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//24
                            startPhotoZoom(context, imageUri, 0);
                        }else{
                            File tempFile = getFileTmp();
                            startPhotoZoom(context, Uri.fromFile(tempFile), 0);
                        }
                    } else {
                        ToastUtil.showToast(context, context.getResources().getString(R.string.find_sdcard_none));
                    }
                    break;
                case UploadAvatarUtil.CLIP_REQUEST_CODE:// 裁剪完成,删除照相机缓存的图片
                    Bitmap imageBitmap = null;
                    //获取头像文件
                    File file = getTmpHeadImage();
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){//24
                        try {
                            imageBitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(imageUri));
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        }
                    }else {
                        if (!file.exists()) {
                            return null;
                        }
                        imageBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                    }
                    if (imageBitmap == null) {
                        return null;
                    }
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
                    byte[] userImageData = http://www.mamicode.com/baos.toByteArray();"path", file.getAbsolutePath());
                    }
                    result.put("data",userImageData);
                    return result;
            }
        }
        return null;
    }

        /**
         * 裁剪图片
         *
         * @param uri from 1代表本地照片的裁剪,0代表拍照后的裁剪
         */
    public void startPhotoZoom(Context context, Uri uri, int from) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        String url = convertPath(context, uri);
        /**
         * 当满足:SDK>4.0、选择本地照片操作、裁剪框为圆形(或者说路径与之前版本有异)三个条件时
         * 进行转换操作
         */
        if (Build.VERSION.SDK_INT >= 19 && from == 1 && url != null) {
            intent.setDataAndType(Uri.fromFile(new File(url)), "image/*");
        } else {
            intent.setDataAndType(uri, "image/*");
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION );
        }

        // 设置裁剪
        intent.putExtra("crop", "true");
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX", 100);
        intent.putExtra("outputY", 100);

        Uri uriPath;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {//小于24
            uriPath = Uri.fromFile(UploadAvatarUtil.getFile(mHeadImageFileParentPath, mHeadImageFileName));
        }else{
            uriPath = imageUri;
        }
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uriPath);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("return-data", false);
        //intent.putExtra("return-data", true);
        //return-data为true时,会直接返回bitmap数据,但是大图裁剪时会出现问题。
        //设置为false比较好,但是,需要指定MediaStore.EXTRA_OUTPUT,来保存裁剪之后的图片
        ((Activity)context).startActivityForResult(intent, UploadAvatarUtil.CLIP_REQUEST_CODE);
    }


    /**
     * 删除缓存的头像
     */
    public void deleteHeadCache() {
        UploadAvatarUtil.deleteLocalImage(mHeadImageFileParentPath, mHeadImageFileName);
    }

    /**
     * 获取文件对象---照相机拍照的
     *
     * @return
     */
    public File getFileTmp() {
        return UploadAvatarUtil.getFile(mHeadImageFileParentPath, mCameraImageFileName);
    }


    /**
     * 获取裁剪之后的图片文件
     */
    public File getTmpHeadImage(){
        return UploadAvatarUtil.getFile(mHeadImageFileParentPath,mHeadImageFileName);
    }

    /**
     * 删除照相机拍照的图片
     */
    public void deleteCameraImage() {
        UploadAvatarUtil.deleteLocalImage(mHeadImageFileParentPath, mCameraImageFileName);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_take_photo:
                takePhoto(context, mHeadImageFileParentPath, mCameraImageFileName);
                break;
            case R.id.btn_pick_photo:
                pickPhoto(context);
                break;
            case R.id.btn_cancel:
                hideSelectPhotoDialog();
                break;
        }
    }


    /**
     * 将uri转换成字符串
     * 解决4.4版本以上获取到的uri是图片名称而非图片路径,导致剪裁图片时提示无法加载图片的问题
     * 详细的解决方案,请参考这篇文章
     * 当安卓的版本比较高时(如4.4),选择本地相册可能会返回“无法加载此图片”
     * 原因:正常uri是file://...而高版本是content://...
     * 所以需要一个转换操作
     *
     * @param context
     * @param uri
     * @return
     */
    public String convertPath(final Context context, final Uri uri) {
        //代表SDK4.4需要反射的类
        Class getClass = null;
        //代表两个SDK4.4里面反射的方法
        Method isMethod;
        Method getMethod;
        String stringMethod = "";


        //判断当前版本是否大于4.0
        final boolean isKitKat = Build.VERSION.SDK_INT >= 19;
        try {
            /**
             * 获取类和方法
             * 方法传参方式
             * 获取返回值
             */
            getClass = Class.forName("android.provider.DocumentsContract");
            getMethod = getClass.getMethod("getDocumentId", new Class[]{Uri.class});
            isMethod = getClass.getMethod("isDocumentUri", new Class[]{Context.class, Uri.class});
            Object isResult = isMethod.invoke(getClass, new Object[]{context, uri});
            Object getResult = getMethod.invoke(getClass, new Object[]{uri});
            stringMethod = (String) getResult;
            if (!(Boolean) isResult)
                return null;
            //当手机SDK大于4.4且路径类型与以往不同时(isShot为True时往往裁剪框是圆形,即是新类型的路径)
            if (isKitKat) {
                if (isExternalStorageDocument(uri)) {
                    final String docId = stringMethod;
                    final String[] split = docId.split(":");
                    final String type = split[0];

                    if ("primary".equalsIgnoreCase(type)) {
                        return Environment.getExternalStorageDirectory() + "/" + split[1];
                    }

                } else if (isDownloadsDocument(uri)) {
                    final String id = stringMethod;
                    final Uri contentUri = ContentUris.withAppendedId(
                            //高版本的路径不同于低版本,需要转换
                            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                    return getDataColumn(context, contentUri, null, null);
                } else if (isMediaDocument(uri)) {
                    final String docId = stringMethod;
                    final String[] split = docId.split(":");
                    final String type = split[0];

                    Uri contentUri = null;
                    if ("image".equals(type)) {
                        contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                    } else if ("video".equals(type)) {
                        contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                    } else if ("audio".equals(type)) {
                        contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                    }

                    final String selection = "_id=?";
                    final String[] selectionArgs = new String[]{
                            split[1]
                    };

                    return getDataColumn(context, contentUri, selection, selectionArgs);
                }
            } else if ("content".equalsIgnoreCase(uri.getScheme())) {
                if (isGooglePhotosUri(uri))
                    return uri.getLastPathSegment();

                return getDataColumn(context, uri, null, null);
            } else if ("file".equalsIgnoreCase(uri.getScheme())) {
                return uri.getPath();
            }

            return null;
        } catch (Exception e) {
            Log.e("error", e.toString());
        }
        return null;
    }


    //获取String类型的路径
    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {

        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return null;
    }

    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }

    @Override
    public void onGrantSuccess() {
        showDialog();
    }
}

在7.0 上使用FileProvider来获取图片的路径,需要以下几步设置:

(1)在Androidmainfest的Application根标签中设置provider,相机,读写文件权限

  <!-- Android7.0以上,拍照需要设置FileProvider -->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.hb.accountant.accountant.fileprovider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>

(2)在res/xml目录中创建文件file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

    <!--external-path 就是用来指定URI共享的
    name属性的值可以随便写
    path属性的值表示共享的具体位置,设置空就表示将整个SD卡进行共享
    用该路径保存的好处是,文件会随着应用的卸载而删除,file/header/为自定义路径
Android/data/com.bugull.cameratakedemo/files/header/
     -->

    <!--<external-path  name = "my-images" path = "demo"/>-->

    <paths>
        <external-path path="包名" name="camera_photos" />
    </paths>
</paths>

注:文件中涉及的其他文件如下:

  (1)动态权限授权使用第三方PermissionGen

(2)UploadAvatarUtil.java

package com.hb.weex.util;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import com.hb.common.android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;


/**
 * 上传用户头像
 * 功能如下:
 * 1、保存图片到本地、删除本地图片
 * 2、拍照、从图库中选择照片、剪裁
 * 3、创建文件目录、获取文件对象
 * 4、检查Sdcard是否存在
 * 5、创建圆角矩形图片
 * 6、创建圆角图片
 * 7、自定义从下往上显示的Dialog(模仿QQ退出效果)
 * 8、图片上绘制文字
 * Created by cjy on 2015/4/9.
 */
public class UploadAvatarUtil {
    public static final int IMAGE_REQUEST_CODE = 0;  //选择本地图片
    public static final int CAMERA_REQUEST_CODE = 1; //拍照
    public static final int CLIP_REQUEST_CODE = 2;   //裁剪

    private static final int DEFAULT_TEXT_SIZE = 32;//默认的字体大小

    /**
     * 检查是否存在sdcard
     *
     * @return
     */
    public static boolean hasSdcard() {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 创建文件目录
     *
     * @param filePath 文件路径
     */
    public static File makeFileDir(String filePath) {
        File file = null;
        try {
            file = new File(filePath);
            if (!file.exists()) {
                file.mkdirs();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 获取文件对象
     *
     * @param filePath 文件路径
     * @param fileName 文件名称
     * @return
     */
    public static File getFile(String filePath, String fileName) {
        File file = null;
        //创建文件目录
        makeFileDir(filePath);
        try {
            file = new File(filePath + fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return file;
    }

    /**
     * 保存图片到本地
     *
     * @param imgUrl        图片路径
     * @param imageFileName 图片名称
     * @param bitmap
     */
    public static void saveLocalImage(String imgUrl, String imageFileName, Bitmap bitmap) {
        try {
            File f = getFile(imgUrl, imageFileName);
            FileOutputStream fOut = new FileOutputStream(f);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
            fOut.flush();
            fOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除本地图片
     *
     * @param imgUrl        图片路径
     * @param imageFileName 图片名称
     */
    public static void deleteLocalImage(String imgUrl, String imageFileName) {
        File f = getFile(imgUrl, imageFileName);
        if (f.isFile() && f.exists()) {
            f.delete();
        }
    }

    /**
     * 绘制圆角矩形图片
     * 圆形 x=120,y=120,outerRadiusRat=60
     *
     * @param x
     * @param y
     * @param image
     * @param outerRadiusRat
     * @return
     */
    public static Bitmap createFramedPhoto(int x, int y, Bitmap image,
                                           float outerRadiusRat) {
        // 根据源文件新建一个darwable对象
        Drawable imageDrawable = new BitmapDrawable(image);

        // 新建一个新的输出图片
        Bitmap output = Bitmap.createBitmap(x, y, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        // 新建一个矩形
        RectF outerRect = new RectF(0, 0, x, y);

        // 产生一个白色的圆角矩形
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.RED);
        canvas.drawRoundRect(outerRect, outerRadiusRat, outerRadiusRat, paint);

        // 将源图片绘制到这个圆角矩形上
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        imageDrawable.setBounds(0, 0, x, y);
        canvas.saveLayer(outerRect, paint, Canvas.ALL_SAVE_FLAG);
        imageDrawable.draw(canvas);
        canvas.restore();

        return output;
    }

    /**
     * 选择本地图片
     */
    public static void pickPhoto(Context context) {
        Intent intentFromGallery = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intentFromGallery.setType("image/*"); // 设置文件类型
        intentFromGallery.setAction(Intent.ACTION_GET_CONTENT);
        ((Activity) context).startActivityForResult(intentFromGallery, IMAGE_REQUEST_CODE);
    }

    /**
     * 拍照
     */
    public static void takePhoto(Context context, String imgUrl, String imageFileNameTemp) {
        // 打开相机
        Intent intentFromCapture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 判断存储卡是否可以用,存储缓存图片
        if (hasSdcard()) {
            intentFromCapture.putExtra(MediaStore.EXTRA_OUTPUT,
                    Uri.fromFile(getFile(imgUrl, imageFileNameTemp)));
        }
        ((Activity) context).startActivityForResult(intentFromCapture, CAMERA_REQUEST_CODE);
    }

    /**
     * 裁剪图片
     *
     * @param uri
     */
    public static void startPhotoZoom(Context context, Uri uri) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        // 设置裁剪
        intent.putExtra("crop", "true");
        // aspectX aspectY 是宽高的比例
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // outputX outputY 是裁剪图片宽高
        intent.putExtra("outputX", 200);
        intent.putExtra("outputY", 200);
        intent.putExtra("return-data", true);
//        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//        intent.putExtra("return-data", false);//设置为不返回数据
        ((Activity) context).startActivityForResult(intent, CLIP_REQUEST_CODE);
    }

    /**
     * 创建指定样式、指定动画的Dialog
     *
     * @param context
     * @param view        Dialog的布局
     * @param styleId     Dialog的样式
     * @param animStyleId Dialog的动画样式
     * @return
     */
    public static Dialog createDialog(Context context, View view, int styleId, int animStyleId) {
        Dialog mSelectPhotoDialog = new Dialog(context, styleId);
        mSelectPhotoDialog.setContentView(view);
        Window window = mSelectPhotoDialog.getWindow();
        // 设置显示动画
        window.setWindowAnimations(animStyleId);
        WindowManager.LayoutParams wl = window.getAttributes();
        wl.x = 0;
        wl.y = ((Activity) context).getWindowManager().getDefaultDisplay().getHeight();
        // 以下这两句是为了保证按钮可以水平满屏
        wl.width = ViewGroup.LayoutParams.MATCH_PARENT;
        wl.height = ViewGroup.LayoutParams.WRAP_CONTENT;
        // 设置显示位置
        mSelectPhotoDialog.onWindowAttributesChanged(wl);
        // 设置点击外围解散
        mSelectPhotoDialog.setCanceledOnTouchOutside(true);
        mSelectPhotoDialog.show();

        return mSelectPhotoDialog;
    }

    /**
     * 第1种方法:图片上绘制文字
     *
     * @param context
     * @param drawableId
     * @param text
     * @param color
     * @return
     */
    public static Bitmap drawTextAtBitmap(Context context, int drawableId, String text, int color, float density) {
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), drawableId);

        int[] size = getBitmapSize(context, drawableId);
//        Log.e("density", density + "");
        int x = size[0];
        int y = size[1];
//        int x = (int) (size[0] * density);
//        int y = (int) (size[1] * density);
//        Log.e("x", x + "");
//        Log.e("y", y + "");
//        bitmap = getScaleBitmap(context,bitmap);

        // 创建一个和原图同样大小的位图
        Bitmap newbit = Bitmap.createBitmap(x, y, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(newbit);
        Paint paint = new Paint();
        // 贴图 在原始位置0,0插入原图
        canvas.drawBitmap(bitmap, 0, 0, paint);
        paint.setColor(color);
        int textSize = DEFAULT_TEXT_SIZE;
        if (density >= 3) {
            textSize *= (density + 1);
        } else if (density >= 2) {
            textSize *= (density + 0.1);
        }
        paint.setTextSize(textSize);
        paint.setTextAlign(Paint.Align.CENTER);
        // 在原图指定位置写上字
//        canvas.drawText(text, x / 2 - 10 * density, y / 2, paint);
        canvas.drawText(text, x / 2, y / 2 + 20 * (density-1) + 2, paint);

        canvas.save(Canvas.ALL_SAVE_FLAG);

        // 存储
        canvas.restore();
        return newbit;
    }

    /**
     * 图片叠加
     *
     * @param bitmap1
     * @param bitmap2
     * @return
     */
    public static Bitmap layerBitmap(Bitmap bitmap1, Bitmap bitmap2) {

        // copy 防止出现Immutable bitmap passed to Canvas constructor错误
        bitmap1 = bitmap1.copy(Bitmap.Config.ARGB_8888, true);

        Bitmap newBitmap = null;

        newBitmap = Bitmap.createBitmap(bitmap1);
        Canvas canvas = new Canvas(newBitmap);
        Paint paint = new Paint();

        int w = bitmap1.getWidth();
        int h = bitmap1.getHeight();

        int w_2 = bitmap2.getWidth();
        int h_2 = bitmap2.getHeight();

        paint.setColor(Color.GRAY);
        paint.setAlpha(125);
        canvas.drawRect(0, 0, bitmap1.getWidth(), bitmap1.getHeight(), paint);

        paint = new Paint();
        canvas.drawBitmap(bitmap2, Math.abs(w - w_2) / 2,
                Math.abs(h - h_2) / 2, paint);
        canvas.save(Canvas.ALL_SAVE_FLAG);
        // 存储新合成的图片
        canvas.restore();

        return newBitmap;


    }

    /**
     * 图片叠加
     *
     * @param bitmap1
     * @param bitmap2
     * @return
     */
    public static LayerDrawable layerDrawable(Bitmap bitmap1, Bitmap bitmap2) {
        Drawable[] array = new Drawable[2];
        array[0] = new BitmapDrawable(bitmap1);
        array[1] = new BitmapDrawable(bitmap2);
        LayerDrawable la = new LayerDrawable(array);
        // 其中第一个参数为层的索引号,后面的四个参数分别为left、top、right和bottom
        la.setLayerInset(0, 0, 0, 0, 0);
        la.setLayerInset(1, 10, 10, 10, 10);

        return la;
    }

    /**
     * 获取Bitmap的宽高
     *
     * @param context
     * @param drawableId 图片id
     * @return
     */
    private static int[] getBitmapSize(Context context, int drawableId) {
        int[] size = new int[2];
        BitmapFactory.Options opts = new BitmapFactory.Options();
        opts.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(context.getResources(), drawableId, opts);
        int width = opts.outWidth;
        int height = opts.outHeight;
        size[0] = width;
        size[1] = height;
        return size;
    }


    /**
     * 等比例缩放
     *
     * @param context
     * @param bitmap
     * @return
     */
    public static Bitmap getScaleBitmap(Context context, Bitmap bitmap) {
        int[] screen = ScreenPixelsUtil.getScreenPixels(context);
        Matrix matrix = new Matrix();
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        Log.e("Width", width + "");
        Log.e("Height", height + "");
        //屏幕宽度/图片宽度
        float w = screen[0] / width;
        float h = screen[1] / height;
        matrix.postScale(w, h);// 获取缩放比例
        // 根据缩放比例获取新的位图
        Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
        return newbmp;
    }

}

(3)资源文件:

 dlg_select_photo.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/pop_layout"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:background="@color/transparent"
              android:gravity="bottom"
              android:layout_gravity="center_horizontal"
              android:paddingLeft="10dp"
              android:paddingRight="10dp"
              android:paddingBottom="10dp"
        >
    <LinearLayout
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="wrap_content"
                  android:background="@drawable/setting_dialog"
    >
    <Button
            android:id="@+id/btn_take_photo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/take_photo"
            android:paddingTop="14dp"
            android:paddingBottom="14dp"
            android:layout_marginTop="2dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:textSize="17dp"
            android:textColor="#333333"
            android:background="@color/white"/>

    <View android:layout_width="match_parent"
          android:layout_height="0.5dp"
          android:background="@color/dropdownmenu_line"/>
    <Button
            android:id="@+id/btn_pick_photo"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/pick_photo"
            android:paddingTop="14dp"
            android:paddingBottom="14dp"
            android:layout_marginLeft="4dp"
            android:layout_marginRight="4dp"
            android:textSize="17dp"
            android:textColor="#333333"
            android:background="@color/white"/>
        </LinearLayout>
    <Button
            android:id="@+id/btn_cancel"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="取消"
            android:paddingTop="14dp"
            android:paddingBottom="14dp"
            android:textSize="17dp"
            android:textColor="@color/gray"
            android:background="@drawable/setting_dialog"
            android:layout_marginTop="16dp"/>

</LinearLayout>

styles.xml

 <!-- 头像弹出窗的动画效果 -->
    <style name="select_photo_dialog_animstyle">
        <item name="android:windowEnterAnimation">@anim/photo_dialog_in_anim</item>
        <item name="android:windowExitAnimation">@anim/photo_dialog_out_anim</item>
    </style>

drawable

//photo_dialog_in_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="500"
        android:fromXDelta="0"
        android:fromYDelta="1000"
        android:toXDelta="0"
        android:toYDelta="0" />

</set>


//photo_dialog_out_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

    <translate
        android:duration="500"
        android:fromXDelta="0"
        android:fromYDelta="0"
        android:toXDelta="0"
        android:toYDelta="1000" />

</set>

  

  

  

  

拍照、本地图片工具类(兼容至Android7.0)