相册获取、相机拍摄,裁剪圆形头像

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011394071/article/details/53456179

相册获取、相机拍摄,裁剪圆形头像

应用场景

  • 很多应用都有个人中心,个人中心就会有头像,现在一般都流行圆形头像,那么怎么设置呢

使用步骤

  • 这里参考了网上各位大神的文章,因为中途遇到几个坑,折磨了一天,快要疯了,因为本人也是菜鸟,所以被虐是必然了,当然我们看到新人遇到问题时,不要觉得那个问题是简单的,当初如果你没有遇到这种需求或没接触过那些坑时,或许也会感到不简单的。
  • 因为遇到的坑较多,忘记了参考那些文章,反正是东看看西看看,然后拼凑到一起,稍微总结一些坑
  • 废话不多,先分析一下步骤
    • 要将图片设置成圆形,需要自定义展示图片的控件
    • 获取相册的意图
    • 获取相机的意图。
    • 裁剪程序
    • 保存裁剪后的图片
    • 设置展示圆形图片
  • 遇到的坑

    • 拍照时返回的data获取不到uri,这是个坑
    • 在onActivityResult方法中,如果判断data==null,就返回,则拍照时就不能通过,这个搞不懂啥原因
    • 拍照后,想通过保存图片的路径去获取图片并裁剪,结果失败,可能直接这么获取得到的图片太大,而通过api获取相册的方式,返回的图片其实是被压缩过的,所以不用担心OOM
    • AlertDialog如果是自定义视图,在每次启动时需要将视图的父容器移除,否则挂掉。

      ViewGroup p = (ViewGroup) mView.getParent();
      if (p != null) {
          p.removeAllViewsInLayout();
      }
      
    • 读取相册时,当android大于4.4版本时需要处理,否则挂掉。

      //android大于4.4版本处理,否则挂掉
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
          String url = PhotoClipperUtil.getPath(context, uri);
          intent.setDataAndType(Uri.fromFile(new File(url)), "image/*");
      }
      

实现步骤

1.添加权限

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

2.自定义控件

    package skxy.dev.safehttps.view;

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.PorterDuff;
    import android.graphics.PorterDuffXfermode;
    import android.graphics.Rect;
    import android.graphics.drawable.BitmapDrawable;
    import android.graphics.drawable.Drawable;
    import android.graphics.drawable.NinePatchDrawable;
    import android.util.AttributeSet;
    import android.widget.ImageView;

    import skxy.dev.safehttps.R;

    /**
     * ClassName : RoundImageView
     * Created by: skxy on 2016/12/4.
     * DES :自定义圆形图片类
     */
    public class RoundImageView extends ImageView {
        private int mBorderThickness = 0;
        private Context mContext;
        private int defaultColor = 0xFFFFFFFF;
        // 如果只有其中一个有值,则只画一个圆形边框
        private int mBorderOutsideColor = 0;
        private int mBorderInsideColor = 0;
        // 控件默认长、宽
        private int defaultWidth = 0;
        private int defaultHeight = 0;

        public RoundImageView(Context context) {
            super(context);
            mContext = context;
        }

        public RoundImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mContext = context;
            setCustomAttributes(attrs);
        }

        public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            mContext = context;
            setCustomAttributes(attrs);
        }

        private void setCustomAttributes(AttributeSet attrs) {
            TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.roundedimageview);
            mBorderThickness = a.getDimensionPixelSize(R.styleable.roundedimageview_border_thickness, 0);
            mBorderOutsideColor = a.getColor(R.styleable.roundedimageview_border_outside_color,defaultColor);
            mBorderInsideColor = a.getColor(R.styleable.roundedimageview_border_inside_color, defaultColor);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            Drawable drawable = getDrawable() ;
            if (drawable == null) {
                return;
            }
            if (getWidth() == 0 || getHeight() == 0) {
                return;
            }
            this.measure(0, 0);
            if (drawable.getClass() == NinePatchDrawable.class)
                return;
            Bitmap b = ((BitmapDrawable) drawable).getBitmap();
            Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
            if (defaultWidth == 0) {
                defaultWidth = getWidth();
            }
            if (defaultHeight == 0) {
                defaultHeight = getHeight();
            }
            int radius = 0;
            if (mBorderInsideColor != defaultColor && mBorderOutsideColor != defaultColor) {// 定义画两个边框,分别为外圆边框和内圆边框
                radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - 2 * mBorderThickness;
                // 画内圆
                drawCircleBorder(canvas, radius + mBorderThickness / 2,mBorderInsideColor);
                // 画外圆
                drawCircleBorder(canvas, radius + mBorderThickness + mBorderThickness / 2, mBorderOutsideColor);
            } else if (mBorderInsideColor != defaultColor && mBorderOutsideColor == defaultColor) {// 定义画一个边框
                radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;
                drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderInsideColor);
            } else if (mBorderInsideColor == defaultColor && mBorderOutsideColor != defaultColor) {// 定义画一个边框
                radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2 - mBorderThickness;
                drawCircleBorder(canvas, radius + mBorderThickness / 2, mBorderOutsideColor);
            } else {// 没有边框
                radius = (defaultWidth < defaultHeight ? defaultWidth : defaultHeight) / 2;
            }
            Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
            canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight / 2 - radius, null);
        }

        /**
         * 获取裁剪后的圆形图片
         * @param radius 半径
         */
        public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
            Bitmap scaledSrcBmp;
            int diameter = radius * 2;
            // 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片
            int bmpWidth = bmp.getWidth();
            int bmpHeight = bmp.getHeight();
            int squareWidth = 0, squareHeight = 0;
            int x = 0, y = 0;
            Bitmap squareBitmap;
            if (bmpHeight > bmpWidth) {// 高大于宽
                squareWidth = squareHeight = bmpWidth;
                x = 0;
                y = (bmpHeight - bmpWidth) / 2;
                // 截取正方形图片
                squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth, squareHeight);
            } else if (bmpHeight < bmpWidth) {// 宽大于高
                squareWidth = squareHeight = bmpHeight;
                x = (bmpWidth - bmpHeight) / 2;
                y = 0;
                squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,squareHeight);
            } else {
                squareBitmap = bmp;
            }
            if (squareBitmap.getWidth() != diameter || squareBitmap.getHeight() != diameter) {
                scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,diameter, true);
            } else {
                scaledSrcBmp = squareBitmap;
            }
            Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
                    scaledSrcBmp.getHeight(),
                    Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(output);

            Paint paint = new Paint();
            Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),scaledSrcBmp.getHeight());

            paint.setAntiAlias(true);
            paint.setFilterBitmap(true);
            paint.setDither(true);
            canvas.drawARGB(0, 0, 0, 0);
            canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
                    scaledSrcBmp.getHeight() / 2,
                    scaledSrcBmp.getWidth() / 2,
                    paint);
            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
            bmp = null;
            squareBitmap = null;
            scaledSrcBmp = null;
            return output;
        }

        /**
         * 边缘画圆
         */
        private void drawCircleBorder(Canvas canvas, int radius, int color) {
            Paint paint = new Paint();
            /* 去锯齿 */
            paint.setAntiAlias(true);
            paint.setFilterBitmap(true);
            paint.setDither(true);
            paint.setColor(color);
            /* 设置paint的style为STROKE:空心 */
            paint.setStyle(Paint.Style.STROKE);
            /* 设置paint的外框宽度 */
            paint.setStrokeWidth(mBorderThickness);
            canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);
        }
    }

3.自定义控件相关属性,在res/values/目录下创建attr文件

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="roundedimageview">
            <attr name="border_thickness" format="dimension" />
            <attr name="border_inside_color" format="color" />
            <attr name="border_outside_color" format="color"></attr>
        </declare-styleable>
    </resources>

4.相关处理工具类

    package skxy.dev.safehttps;

    import android.annotation.TargetApi;
    import android.content.ContentUris;
    import android.content.Context;
    import android.database.Cursor;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.provider.DocumentsContract;
    import android.provider.MediaStore;

    /**
     * ClassName : PhotoClipperUtil
     * Created by: skxy on 2016/12/3.
     * DES :图片路径获取工具类
     */
    public class PhotoClipperUtil {
        @TargetApi(Build.VERSION_CODES.KITKAT)
        public static String getPath(final Context context, final Uri uri) {

            final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

            // DocumentProvider
            if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
                // ExternalStorageProvider
                if (isExternalStorageDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    final String[] split = docId.split(":");
                    final String type = split[0];

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

                }
                // DownloadsProvider
                else if (isDownloadsDocument(uri)) {
                    final String id = DocumentsContract.getDocumentId(uri);
                    final Uri contentUri = ContentUris.withAppendedId(
                            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                    return getDataColumn(context, contentUri, null, null);
                }
                // MediaProvider
                else if (isMediaDocument(uri)) {
                    final String docId = DocumentsContract.getDocumentId(uri);
                    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);
                }
            }
            // MediaStore (and general)
            else if ("content".equalsIgnoreCase(uri.getScheme())) {
                // Return the remote address
                if (isGooglePhotosUri(uri))
                    return uri.getLastPathSegment();

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

            return null;
        }

        /**
         * Get the value of the data column for this Uri. This is useful for
         * MediaStore Uris, and other file-based ContentProviders.
         *
         * @param context The context.
         * @param uri The Uri to query.
         * @param selection (Optional) Filter used in the query.
         * @param selectionArgs (Optional) Selection arguments used in the query.
         * @return The value of the _data column, which is typically a file path.
         */
        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;
        }


        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        public static boolean isExternalStorageDocument(Uri uri) {
            return "com.android.externalstorage.documents".equals(uri.getAuthority());
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        public static boolean isDownloadsDocument(Uri uri) {
            return "com.android.providers.downloads.documents".equals(uri.getAuthority());
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        public static boolean isMediaDocument(Uri uri) {
            return "com.android.providers.media.documents".equals(uri.getAuthority());
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is Google Photos.
         */
        public static boolean isGooglePhotosUri(Uri uri) {
            return "com.google.android.apps.photos.content".equals(uri.getAuthority());
        }
    }

5.基本准备好了,现在来设置要显示的控件布局

        <skxy.dev.safehttps.view.RoundImageView
            android:id="@+id/iv"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:layout_gravity="center"
            android:src="@mipmap/ic_launcher"/>

6.处理点击和显示的逻辑

    package skxy.dev.safehttps;

    import android.app.Activity;
    import android.content.Context;
    import android.content.Intent;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Bundle;
    import android.os.Environment;
    import android.provider.MediaStore;
    import android.support.v7.app.AlertDialog;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    import android.widget.Toast;

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

    import skxy.dev.safehttps.view.RoundImageView;

    public class PhontoActivity extends AppCompatActivity implements View.OnClickListener {

        private static final int REQUEST_CODE_FROM_PHOTO = 0;
        private static final int SELECT_CLIPPER_PIC = 1;
        private static final int RESULT_CAMERA_ONLY = 100;
        private Uri imageUri;
        public RoundImageView mImageView;
        public LinearLayout mView;
        public TextView mFromPhoto;
        public TextView mFromCamera;
        public AlertDialog mDialog;
        public String mFileName;
        public Uri mImageCropUri;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_phonto);
            initView();
            initUri();
        }

        private void initView() {
            mImageView = (RoundImageView) findViewById(R.id.iv);
            mView = (LinearLayout) View.inflate(this, R.layout.dialogview, null);
            mFromPhoto = (TextView) mView.findViewById(R.id.fromphoto);
            mFromCamera = (TextView) mView.findViewById(R.id.fromcamera);
            mImageView.setOnClickListener(this);
            mFromCamera.setOnClickListener(this);
            mFromPhoto.setOnClickListener(this);
        }

        private void initUri() {
            File file;
            File cropFile;
            //必须在点击选择相册或相机之前初始化
            if (hasSdcard()) {
                File rootFile = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera");
                if (!rootFile.exists()) {
                    rootFile.mkdir();
                }
                file = new File(rootFile + "/temp.jpg");
                cropFile = new File(rootFile + "/temp_crop.jpg");
            } else {
                //如果没有外部存储卡,只能使用临时目录
                file =new File(getCacheDir()+"/temp.jpg") ;
                cropFile = new File(getCacheDir() + "/temp_crop.jpg");
            }
            imageUri = Uri.fromFile(file);
            mImageCropUri = Uri.fromFile(cropFile);
        }


        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.iv:
                    showDialog();
                    break;
                case R.id.fromcamera:
                    takeCameraOnly();
                    break;
                case R.id.fromphoto:
                    goAlbums();
                    break;
            }
        }

        private void showDialog() {
            //因为每次点击后,这个布局视图就有了父容器,重新加载时就会附着到新的容器中,导致挂掉
            //因此每次都先将父容器清空
            ViewGroup p = (ViewGroup) mView.getParent();
            if (p != null) {
                p.removeAllViewsInLayout();
            }
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setView(mView);
            mDialog = builder.create();
            mDialog.setCanceledOnTouchOutside(true);
            mDialog.show();
        }

        //从相机获取
        private void takeCameraOnly() {
            if (hasSdcard()) {
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra("return-data", false);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
                intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
                intent.putExtra("noFaceDetection", true);
                startActivityForResult(intent, RESULT_CAMERA_ONLY);
            } else {
                Toast.makeText(PhontoActivity.this, "没有可用的外部存储设备", Toast.LENGTH_SHORT).show();
            }
        }

        //判断SD卡
        private boolean hasSdcard() {
            String state = Environment.getExternalStorageState();
            if (state.equals(Environment.MEDIA_MOUNTED)) {
                return true;
            }
            return false;
        }

        //从相册获取
        private void goAlbums() {
            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");
            startActivityForResult(intent, REQUEST_CODE_FROM_PHOTO);
        }

        /**
         * 裁剪大图
         *
         * @param context
         * @param uri
         */
        private void clipperBigPic(Context context, Uri uri) {
            if (null == uri) {
                return;
            }
            Intent intent = new Intent("com.android.camera.action.CROP");
            //android大于4.4版本处理,否则挂掉
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                String url = PhotoClipperUtil.getPath(context, uri);
                intent.setDataAndType(Uri.fromFile(new File(url)), "image/*");
            }

            //发送裁剪命令
            intent.putExtra("crop", true);
            //X方向上的比例
            intent.putExtra("aspectX", 1);
            //Y方向上的比例
            intent.putExtra("aspectY", 1);
            // 裁剪后输出图片的宽高像素
            //裁剪区的宽
            intent.putExtra("outputX", 120);
            //裁剪区的高
            intent.putExtra("outputY", 120);
            //是否保留比例
            intent.putExtra("scale", true);
            //返回数据
            intent.putExtra("return-data", true);
            intent.putExtra("noFaceDetection", true);
            //输出图片格式
            intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
            //裁剪图片保存位置
            intent.putExtra(MediaStore.EXTRA_OUTPUT,mImageCropUri);
            startActivityForResult(intent, SELECT_CLIPPER_PIC);
        }

        //处理返回的相片
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (resultCode != Activity.RESULT_OK)
                return;
            switch (requestCode) {
                case REQUEST_CODE_FROM_PHOTO://相册选择
                    //获取图片后裁剪图片
                    clipperBigPic(this, data.getData());
                    break;
                case SELECT_CLIPPER_PIC:
                    Bundle extras = data.getExtras();
                    if (extras != null) {
                        try {
                            //重新读取保存好的照片,已经裁剪过的,可以上传或直接设置到控件上
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(mImageCropUri));
                            mImageView.setImageBitmap(bitmap);
                            mDialog.dismiss();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case RESULT_CAMERA_ONLY:
                    //相机返回的data获取不到uri,这是个坑
                    clipperBigPic(this, imageUri);
                    break;

            }
        }

        /**
         * 保存图片
         *
         * @param data
         */
        private void saveBitmap(Intent data) {
            Bundle bundle = data.getExtras();
            if (bundle != null) {
                Bitmap bitmap = bundle.getParcelable("data");
                //存储图片的名字
                mFileName = System.currentTimeMillis() + ".png";
                File file = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera", mFileName);
                try {
                    file.createNewFile();
                    FileOutputStream fileOutputStream = new FileOutputStream(file);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
                    fileOutputStream.flush();
                    fileOutputStream.close();
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

7.最终效果,这里没有做Aandroid6.0的权限适配,需要的自己添加一下。



8.最后感谢那些大神提供的文章,这里列举几篇

http://blog.csdn.net/chenguang79/article/details/52230507

http://blog.csdn.net/harvic880925/article/details/43163175

http://www.2cto.com/kf/201401/270144.html

猜你喜欢

转载自blog.csdn.net/u011394071/article/details/53456179