100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > android圆形图片裁剪demo以及实现

android圆形图片裁剪demo以及实现

时间:2021-01-27 23:54:44

相关推荐

android圆形图片裁剪demo以及实现

前段时间做用户头像设置与上次,由于设计需要,将用户头像裁剪成圆形并设置上传.感觉裁剪成圆形图片的功能,以后很可能会用到,加之网上这一类的demo总结注释的不好,于是自己就做了demo并写好注释上传.

废话少说,先上图:

关键代码:

一:调用系统摄像头拍照后获取图片然后裁剪流程

1:调用系统摄像头拍照

/** * 打开系统摄像头拍照获取图片 */private void openCamera() {String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) {Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照 if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {imageUri = Uri.fromFile(headIconFile); } else {//FileProvider为7.0新增应用间共享文件,在7.0上暴露文件路径会报FileUriExposedException //为了适配7.0,所以需要使用FileProvider,具体使用百度一下即可 imageUri = FileProvider.getUriForFile(this,"com.channelst.headimgclip.fileprovider", headIconFile);//通过FileProvider创建一个content类型的Uri }intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); Log.e(TAG, "openCamera()---intent" + intent); }}

2.调用系统摄像头返回,进入case TAKE_PHOTO_REQUEST_CODE

@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {Log.e(TAG, "onActivityResult()---requestCode" + requestCode+ ", resultCode : " + resultCode); switch (requestCode) {case CLIP_PHOTO_BY_SYSTEM_REQUEST_CODE:Log.d(TAG,"调用系统剪辑照片后返回........."); if (resultCode == RESULT_OK) {Bitmap bm = BitmapFactory.decodeFile(headClipFile.getAbsolutePath());headImg.setImageBitmap(bm);Log.e(TAG, "onActivityResult()---bm : " + bm); } else {Log.e(TAG, "onActivityResult()---resultCode : " + resultCode); }break; case TAKE_PHOTO_REQUEST_CODE:Log.i(TAG,"拍照后返回........."); if (resultCode == RESULT_OK) {//拍照后返回,调用系统裁剪,系统裁剪无法裁剪成圆形//clipPhotoBySystem(imageUri);//调用自定义裁剪clipPhotoBySelf(headIconFile.getAbsolutePath()); }break; case CHOOSE_PHOTO_REQUEST_CODE:Log.i(TAG, "从相册选取照片后返回...."); if (resultCode == RESULT_OK) {if (data != null) {String filePath = "";Uri originalUri = data.getData(); // 获得图片的uriLog.i(TAG, "originalUri : " + originalUri);if (originalUri != null) {filePath = GetImagePath.getPath(this,originalUri);}Log.i(TAG, "filePath : " + filePath);if (filePath != null && filePath.length() > 0) {//clipPhotoBySystem(originalUri); //调用自定义裁剪 clipPhotoBySelf(filePath);}}}break; case CLIP_PHOTO_BY_SELF_REQUEST_CODE:Log.i(TAG, "从自定义切图返回.........."); if (resultCode == RESULT_OK) {Bitmap bm = BitmapFactory.decodeFile(headClipFile.getAbsolutePath());headImg.setImageBitmap(bm);Log.i(TAG, "onActivityResult()---bm : " + bm); } else {Log.i(TAG, "onActivityResult()---resultCode : " + resultCode); }break; }}

3.调用自定义裁剪方法,进入ClipPitctureActivity,如图一界面

/** * 调用自定义切图方法 * * @param filePath */protected void clipPhotoBySelf(String filePath) {Log.i(TAG, "通过自定义方式去剪辑这个照片"); //进入裁剪页面,此处用的是自定义的裁剪页面而不是调用系统裁剪 Intent intent = new Intent(this, ClipPictureActivity.class); intent.putExtra(ClipPictureActivity.IMAGE_PATH_ORIGINAL, filePath); intent.putExtra(ClipPictureActivity.IMAGE_PATH_AFTER_CROP, headClipFile.getAbsolutePath()); startActivityForResult(intent, CLIP_PHOTO_BY_SELF_REQUEST_CODE);}

4.在ClipPitctureActivity里,在主要布局加载完成后,加入一个自定义的ClipView,该View主要是在图片上方覆盖一层幕布,然后从中间抠出一个圆形

我们先来看clipView的初始化过程:

/** * 初始化截图区域,并将源图按裁剪框比例缩放 * */private void initClipView() {Intent intent = getIntent(); final String originalImgPath = intent.getStringExtra(IMAGE_PATH_ORIGINAL); croppedImagePath = intent.getStringExtra(IMAGE_PATH_AFTER_CROP); // 首先判断源文件是否存在--防止垃圾数据的影响: // 一张图片在SD卡上已经被删除,但是媒体库中还有该数据。 File file = new File(originalImgPath); if (!file.exists()) {Toast.makeText(this, "源文件在SD卡上不存在", Toast.LENGTH_SHORT).show(); finish(); return; }......//初始化截图区域自定义view clipview = new ClipView(ClipPictureActivity.this); clipview.addOnDrawCompleteListener(new ClipView.OnDrawListenerComplete() {public void onDrawComplete() {clipview.removeOnDrawCompleteListener(); int radius = (int) clipview.getRadius(); int midX = (int) clipview.getCircleCenterPX(); int midY = (int) clipview.getCircleCenterPY(); int imageWidth = bitmap.getWidth(); int imageHeight = bitmap.getHeight(); // 按裁剪框求缩放比例 float scale = (radius * 3.0f) / imageWidth; // 起始中心点 float imageMidX = imageWidth * scale / 2; float imageMidY = imageHeight * scale / 2; srcPic.setScaleType(ImageView.ScaleType.MATRIX); // 缩放 matrix.postScale(scale, scale); // 平移 matrix.postTranslate(midX - imageMidX, midY - imageMidY); srcPic.setImageMatrix(matrix); srcPic.setImageBitmap(bitmap); }}); matrix.reset(); srcLayout.addView(clipview, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));}

再来看clipView的onDraw(),如下,onDraw里是先画一个矩形幕布,然后抠出一个圆来,最后画一个白色的边框

@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas); int width = this.getWidth(); int height = this.getHeight(); //画矩形region1 canvas.clipRect(0, 0, width, height); //竖屏的时候width<height,取width的1/3作为半径 // 横屏的时候width>height,取height的1/3作为半径 int shortWidth = width<height?width:height; //画圆形region2 Path path = new Path(); circleCenterPX = (float) width/2.0f; circleCenterPY = (float) height/2.0f; radius = shortWidth/3.0f; path.addCircle(circleCenterPX, circleCenterPY, radius, W); Log.i("ClipView", "onDraw()--circleCenterPX : " + circleCenterPX + ", circleCenterPY : " + circleCenterPY + ", radius : " + radius); Log.i("ClipView", "onDraw()--width : " + width + ", height : " + height); //path.addCircle(150,150,100, W); //XOP表示补集就是全集的减去交集剩余部分,这剩余部分不用遮罩 //也就相当于从遮罩里抠出一个圆形来 canvas.clipPath(path, Region.Op.XOR); //canvas.clipRect(0,0,400,400); paint.setAlpha(((int)(255*0.4f))); canvas.drawRect(0, 0, width, height,paint); canvas.save(); canvas.restore(); // 画圆形边框 borderPaint.setStyle(Paint.Style.STROKE); borderPaint.setAntiAlias(true); borderPaint.setColor(Color.WHITE); borderPaint.setStrokeWidth(clipBorderWidth); canvas.drawCircle(circleCenterPX, circleCenterPY, radius,borderPaint); clipWidth = clipHeight = (int) (radius*2); if (listenerComplete != null) {listenerComplete.onDrawComplete(); }}

5.接下来我们看保存图片的过程.

1)获取图片中的包含圆的矩形,2)把获取的矩形选取中间的圆形,也就是getCircleBitmap()方法

/** * @return 裁剪后的图片 * @description 获取裁剪框内截图 */private Bitmap getBitmap() {try {// srcPic.getDrawingCache()获取View截图在某些情况下报错了。 // 现在用一种新的获取view中图像的方法取代getDrawingCache()方法. // 另:在使用createBitmap()增加try..catch..以防止不断生成bitmap可能导致的oom int startX = (int) (clipview.getCircleCenterPX() - clipview.getRadius()); int startY = (int) (clipview.getCircleCenterPY() - clipview.getRadius()); Log.i(TAG, "getBitmap():startX=" + startX+ ",startY=" + startY+ ",clipview.getClipWidth()=" + clipview.getClipWidth()+ ",clipview.getWidth()=" + clipview.getWidth()+ ",clipview.getCircleCenterPX()=" + clipview.getCircleCenterPX()+ ",clipview.getRadius()=" + clipview.getRadius()+ ",clipview.getCircleCenterPY()=" + clipview.getCircleCenterPY()); Bitmap finalBitmap = Bitmap.createBitmap(loadBitmapFromView(srcPic),startX, startY, clipview.getClipWidth(),clipview.getClipHeight()); // 释放资源 srcPic.destroyDrawingCache(); Log.i(TAG, "getBitmap() finalBitmap=" + finalBitmap); return getCircleBitmap(finalBitmap); } catch (OutOfMemoryError err) {Toast.makeText(this, "保存头像失败", Toast.LENGTH_SHORT).show(); Log.e(TAG, err.getMessage()); return null; } catch (Exception e) {Toast.makeText(this, "保存头像失败!", Toast.LENGTH_SHORT).show(); Log.e(TAG, e.getMessage()); return null; }}/** * @description 获取圆形裁剪框内截图 * @param bitmap src图片 * @return */public static Bitmap getCircleBitmap(Bitmap bitmap) {Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); paint.setAntiAlias(true); paint.setFilterBitmap(true); paint.setDither(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); //在画布上绘制一个圆 -1是为了去掉白色的边框 canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2 - 1, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); Log.i(TAG, "getCircleBitmap()output=" + output); return output;}

6.将圆形bitmap通过io流写入到图片文件中,如下

/** * @return void * @Title: saveMyBitmap * @Description: 保存bitmap对象到裁剪后的文件中 */public static void saveMyBitmap(File file, Bitmap mBitmap) {try {if (!file.getParentFile().exists()) {file.getParentFile().mkdirs(); }if (!file.exists()) {file.createNewFile(); }} catch (IOException e) {e.printStackTrace(); }FileOutputStream fOut = null; try {fOut = new FileOutputStream(file); } catch (FileNotFoundException e) {e.printStackTrace(); }press(pressFormat.PNG, 100, fOut); try {fOut.flush(); } catch (IOException e) {e.printStackTrace(); }try {fOut.close(); } catch (IOException e) {e.printStackTrace(); }}

7.通过setResutl,返回ok

/** * 保存图片 * 首先获取裁剪框里的图片 * 然后保存到裁剪文件croppedImagePath中 */private void saveBitmap() {if (bitmap != null) {// 取出裁剪图片 Bitmap clipBitmap = getBitmap(); Log.i(TAG, "saveBitmap() clipBitmap=" + clipBitmap); File file = new File(croppedImagePath); saveMyBitmap(file, clipBitmap); }Intent intent = new Intent(); ClipPictureActivity.this.setResult(RESULT_OK, intent); finish();}

8.这时候Mainactivity就收到CLIP_PHOTO_BY_SELF_REQUEST_CODE这个返回,在OnActivityResult即会进入该case

然后headImg这个imageView就将其显示出来.如图二

二:从图库中选择图片然后裁剪流程

从图库中选择图片然后裁剪流程和拍照获取图片裁剪流程差不多,差异只在于图片的获取方式

这里只介绍一下调用系统图库的代码:

点击"从相册选择"这个button即触发如下方法,启动系统相册

/** * 从系统图库中选择图片 */private void choosePhoto() {String state = Environment.getExternalStorageState(); if (state.equals(Environment.MEDIA_MOUNTED)) {Intent openAlbumIntent = new Intent(Intent.ACTION_GET_CONTENT); openAlbumIntent.setType(IMAGE_TYPE); startActivityForResult(openAlbumIntent, CHOOSE_PHOTO_REQUEST_CODE); }}

选择图片后返回界面即进入CHOOSE_PHOTO_REQUEST_CODE这个case,然后调用自定义裁剪,之后的过程与上面一致了.

最后,把源码上传一下,希望对别人对自己也有帮助.

demo链接

@author 北京青牛软件南方基地_码农crst_luo -11-06

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。