相册获取、相机拍摄,裁剪圆形头像
来源:互联网 发布:杜克大学商学院知乎 编辑:程序博客网 时间: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
相册获取、相机拍摄,裁剪圆形头像
应用场景
- 很多应用都有个人中心,个人中心就会有头像,现在一般都流行圆形头像,那么怎么设置呢
使用步骤
- 这里参考了网上各位大神的文章,因为中途遇到几个坑,折磨了一天,快要疯了,因为本人也是菜鸟,所以被虐是必然了,当然我们看到新人遇到问题时,不要觉得那个问题是简单的,当初如果你没有遇到这种需求或没接触过那些坑时,或许也会感到不简单的。
- 因为遇到的坑较多,忘记了参考那些文章,反正是东看看西看看,然后拼凑到一起,稍微总结一些坑
- 废话不多,先分析一下步骤
- 要将图片设置成圆形,需要自定义展示图片的控件
- 获取相册的意图
- 获取相机的意图。
- 裁剪程序
- 保存裁剪后的图片
- 设置展示圆形图片
遇到的坑
- 拍照时返回的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
- 相册获取、相机拍摄,裁剪圆形头像
- Android之圆形头像(实现相机拍摄+相册选择+图片裁剪功能)
- android圆形头像:相机裁剪+相册选择
- Android圆形头像设置(实现相机、相册选择并裁剪)兼容6.0/7.0
- android圆形头像:相机相册加载图片到圆形头像
- 相机拍摄到裁剪,相册到裁剪,高清无码
- Android上传头像代码,相机,相册,裁剪
- 调用系统相册,相机设置圆形头像
- 更换头像用相机拍摄或者从相册选择
- 实现显示圆形头像及选择相册相机修改头像
- 从相册和相机获取头像
- iOS 调用相机,获取相册,截取头像
- 获取相册图片,相机拍摄图片并上传
- Quartz2D裁剪圆形头像
- 裁剪头像为圆形
- Quartz2D裁剪圆形头像
- 更改头像 相册/相机
- 调用相机拍摄和相册
- 略谈多态
- 哈尔滨理工大学软件学院ACM程序设计全国邀请赛(网络同步赛)A Golds 最大流
- 单片机、CPU、指令集和操作系统的关系
- 去噪自动编码机
- Android的事件分发详解
- 相册获取、相机拍摄,裁剪圆形头像
- 常用的java代码段
- mv命令
- 环境变量
- 安卓 : float 计算
- Android 多窗口详解
- 栈
- request对象
- 对傅立叶变换后图像空间域与频率域中垂直现象的研究