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

来源:互联网 发布:杜克大学商学院知乎 编辑:程序博客网 时间:2024/05/11 14:35

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

应用场景

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

使用步骤

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

    • 拍照时返回的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

0 0
原创粉丝点击