调用 android 自身裁剪功能
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了调用 android 自身裁剪功能相关的知识,希望对你有一定的参考价值。
利用下面代码,我实现了对图片的剪切功能,可是我想让这剪切出来的新图片在Imageview上显示出来,怎么办啊?知道的人麻烦告诉小弟下,小弟最近才接触android的。
final Intent intent = new Intent
("com.android.camera.action.CROP");
intent.setClassName("com.android.camera",
"com.android.camera.CropImage");
intent.setData( photoUri);
startActivityForResult(intent, REQUEST_CROP_IMAGE);
我是单击imageview让它跳转到图库在进行剪切的。我想剪切完后Imageview可以自动显示新的图片。
一、视频抓拍
1、基本类
Android 框架通过 Camera API 或者 camer Intent 的方式,支持 捕捉图像和视频 。相关的大类主要有以下几个:
(1) Camera 摄像时候必须调用到的类;
(2)SurfaceView 提供摄像头预览。
(3)MediaRecorder 录像时候用到的类;
(4)Intent 如果不通过 Camera 对象来操控摄像头,那么用两个intent 动作 MediaStore.ACTION_IMAGE_CAPTURE or MediaStore.ACTION_VIDEO_CAPTURE 也能够实现摄影和录像。
o MediaStore.ACTION_IMAGE_CAPTURE —— 向内置摄像头程序请求图像的意图活动类型。
o MediaStore.ACTION_VIDEO_CAPTURE —— 向内置摄像头程序请求视频的意图活动类型。
2、 Manifest 声明
在使用Camera API前,必须做出使用 Camera 硬件的声明。主要有下面 点:
(1) Camera 允许:(注意,如果是通过intent 意图来操控的,则不需要下面声明)
<uses-permission android:name="android.permission.CAMERA" />
(2)Camera 特征,比如说名字
<uses-feature android:name="android.hardware.camera" />
(3)如果你要在SD卡中保存照片和视频,那么就得开启SD卡权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
(4)录音权限
<uses-permission android:name="android.permission.RECORD_AUDIO" />
(5)如果希望在照片中插入GPS当地信息,还得开启GPS
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
3、使用方法
使用摄像头的方法有两种:
一种是编写代码比较少的,快速使用摄像头的意图Intent 方法;
另外一种就是 自动外观的摄像头用户界面,要求编写更多的代码。
4、使用Intent 快速应用摄像头:使用默认配置拍摄并保存
二、图片自由裁剪
关键代码如下:
public void imageCut(Uri uri)
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
//开启裁剪功能
intent.putExtra("crop", "true");
//设定宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//设定裁剪图片宽高
intent.putExtra("outputX", 100);
intent.putExtra("outputY", 100);
//要求返回数据
intent.putExtra("return-data", true);
startActivityForResult(intent, 100);
返回数据后直接在onActivityResult 里对返回的图片数据进行显示就行了,这里由于剪切后的图片较小,个人觉得应该不用考虑OOM的问题,问题是,在调用自带的裁剪功能的时候,由于没看源码,所以不知道在调整裁剪框的时候,显示的那副图片有没有进行压缩,如果没有进行压缩,那么当图片很大的时候,加载起来就很容易出现OOM了,明显对此由于是调用官方的API,我没有方法去解决它。唯一的办法就是不用它,而是自己去写一个,就可以避免这个问题。利用如下的代码进行压缩,很好地避免OOM问题。 参考技术A 你要重写这个onActivityResult()方法 , 因为你这是调用系统的activity,所以要在这个事件中进行取得返回数据,具体代码
if(requestCode == REQUEST_CROP_IMAGE)
Bundle extras = data.getExtras();
if(extras != null)
Bitmap b = (Bitmap) extras.get("data");
setImageView.setImageBitmap(b);
返回的数据使用bundle绑定的,将数据获取后,转换成一个位图,放进imageView就可以了本回答被提问者采纳 参考技术B startActivityForResult好像是不能跳回去的,用startActivity好像可以跳回去,尝试看看,我有几个月没用Android了...
腾讯大牛动态教学:Android 仿微信 QQ 图片裁剪,赶紧收藏起来!
前言在平时开发中,经常需要实现这样的功能,拍照 - 裁剪,相册 - 裁剪。当然,系统也有裁剪的功能,但是由于机型,系统兼容性等问题,在实际开发当中,我们通常会自己进行实现。今天,就让我们一起来看看怎样实现。
这篇博客实现的功能主要有仿微信,QQ 上传图像裁剪功能,包括拍照,从相册选取。裁剪框的样式有圆形,正方形,九宫格。
主要讲解的功能点
- 使用说明
- 整体的实现思路
- 裁剪框的实现
- 图片缩放的实现,包括放大,缩小,移动,裁剪等
我们先来看看我们实现的效果图
使用说明
有两种调用方式
第一种
第一种,使用普通的 startActivityForResult 进行调用,并重写 onActivityResult 方法,在里面根据 requestCode 进行处理
1ClipImageActivity.goToClipActivity(this,?uri);
2@Override
3protected?void?onActivityResult(int?requestCode,?int?resultCode,?Intent?intent)?{
4????switch?(requestCode)?{
5????????case?REQ_CLIP_AVATAR:??//剪切图片返回
6????????????if?(resultCode?==?RESULT_OK)?{
7????????????????final?Uri?uri?=?intent.getData();
8????????????????if?(uri?==?null)?{
9????????????????????return;
10????????????????}
11????????????????String?cropImagePath?=?FileUtil.getRealFilePathFromUri(getApplicationContext(),?uri);
12
13
14????----
15
16}
第二种
第二种调用 ClipImageActivity.goToClipActivity 方法,结果以 callBack 回调的方式返回回来,这种看起来比较直观点,个人也比较喜欢这种方法。它的实现原理是通过空白的 fragment 处理实现的,有兴趣的可以看我这一篇博客 Android Fragment 的妙用 - 优雅地申请权限和处理 onActivityResult
1ClipImageActivity.goToClipActivity(this,?uri,?new?ActivityResultHelper.Callback()?{
2????@Override
3????public?void?onActivityResult(int?resultCode,?Intent?data)?{
4
5????}
6});
整体实现思路
从上面的效果图我们可以看到,裁剪功能主要包括两大块
- 裁剪框
- 图片的缩放,移动,裁剪等
因此,为了方便日后的修改,我们将裁剪框的功能单独提取出来,图片缩放功能提出出来。即裁剪框单独一个 View。
下面,让我们一起来看看裁剪框功能的实现。
裁剪框功能的实现
裁剪框主要有两层,第一层,裁剪框的实现(包括圆形,长方形,九宫格形状),第二层,在裁剪区域上面盖上一层蒙层。
蒙层
蒙层的实现我们是通过 Xfermode 实现的
1xfermode?=?new?PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
2
3
4//通过Xfermode的DST_OUT来产生中间的透明裁剪区域,一定要另起一个Layer(层)
5canvas.saveLayer(0,?0,?this.getWidth(),?this.getHeight(),?null,?Canvas.ALL_SAVE_FLAG);
6
7//设置背景
8canvas.drawColor(Color.parseColor("#a8000000"));
9paint.setXfermode(xfermode);
圆形裁剪框的实现
绘制圆形裁剪框很容易实现,主要确定圆心和半径即可
1//中间的透明的圆
2canvas.drawCircle(this.getWidth()?/?2,?this.getHeight()?/?2,?clipRadiusWidth,?paint);
3//白色的圆边框
4canvas.drawCircle(this.getWidth()?/?2,?this.getHeight()?/?2,?clipRadiusWidth,?borderPaint);
正方形裁剪框的实现
绘制长方形的话主要要确定四个点的坐标 left ,top, right, botom。很简单
1left = mHorizontalPadding;
2top?=?this.getHeight()?/?2?-?clipWidth?/?2;
3right = this.getWidth() - mHorizontalPadding;
4botom?=?this.getHeight()?/?2?+?clipWidth?/?2;
1//绘制中间白色的矩形蒙层
2canvas.drawRect(mHorizontalPadding,?this.getHeight()?/?2?-?clipWidth?/?2,
3????????this.getWidth()?-?mHorizontalPadding,?this.getHeight()?/?2?+?clipWidth?/?2,?paint);
4//绘制白色的矩形边框
5canvas.drawRect(mHorizontalPadding,?this.getHeight()?/?2?-?clipWidth?/?2,
6????????this.getWidth()?-?mHorizontalPadding,?this.getHeight()?/?2?+?clipWidth?/?2,?borderPaint);
九宫格的
九宫格的绘制稍微繁琐一点,分三个步骤
- 绘制长方形边框
- 绘制九宫格引导线
- 绘制裁剪边框的是个直角
绘制长方形边框的这里就不说了,比较简单。
我们来看一下绘制九宫格引导线的
- 绘制竖直方向两条线
- 绘制水平方向两条线
1private?void?drawGuidelines(@NonNull?Canvas?canvas,?Rect?clipRect)?{
2
3????final?float?left?=?clipRect.left;
4????final?float?top?=?clipRect.top;
5????final?float?right?=?clipRect.right;
6????final?float?bottom?=?clipRect.bottom;
7
8????final?float?oneThirdCropWidth?=?(right?-?left)?/?3;
9
10????final?float?x1?=?left?+?oneThirdCropWidth;
11????//引导线竖直方向第一条线
12????canvas.drawLine(x1,?top,?x1,?bottom,?mGuidelinePaint);
13????final?float?x2?=?right?-?oneThirdCropWidth;
14????//引导线竖直方向第二条线
15????canvas.drawLine(x2,?top,?x2,?bottom,?mGuidelinePaint);
16
17????final?float?oneThirdCropHeight?=?(bottom?-?top)?/?3;
18
19????final?float?y1?=?top?+?oneThirdCropHeight;
20????//引导线水平方向第一条线
21????canvas.drawLine(left,?y1,?right,?y1,?mGuidelinePaint);
22????final?float?y2?=?bottom?-?oneThirdCropHeight;
23????//引导线水平方向第二条线
24????can
绘制四个直角的
1private?void?drawCorners(@NonNull?Canvas?canvas,?Rect?clipRect)?{
2
3????final?float?left?=?clipRect.left;
4????final?float?top?=?clipRect.top;
5????final?float?right?=?clipRect.right;
6????final?float?bottom?=?clipRect.bottom;
7
8????//简单的数学计算
9????final?float?lateralOffset?=?(mCornerThickness?-?mBorderThickness)?/?2f;
10????final?float?startOffset?=?mCornerThickness?-?(mBorderThickness?/?2f);
11
12????//左上角左面的短线
13????canvas.drawLine(left?-?lateralOffset,?top?-?startOffset,?left?-?lateralOffset,?top?+?mCornerLength,?mCornerPaint);
14????//左上角上面的短线
15????canvas.drawLine(left?-?startOffset,?top?-?lateralOffset,?left?+?mCornerLength,?top?-?lateralOffset,?mCornerPaint);
16
17????//右上角右面的短线
18????canvas.drawLine(right?+?lateralOffset,?top?-?startOffset,?right?+?lateralOffset,?top?+?mCornerLength,?mCornerPaint);
19????//右上角上面的短线
20????canvas.drawLine(right?+?startOffset,?top?-?lateralOffset,?right?-?mCornerLength,?top?-?lateralOffset,?mCornerPaint);
21
22????//左下角左面的短线
23????canvas.drawLine(left?-?lateralOffset,?bottom?+?startOffset,?left?-?lateralOffset,?bottom?-?mCornerLength,?mCornerPaint);
24????//左下角底部的短线
25????canvas.drawLine(left?-?startOffset,?bottom?+?lateralOffset,?left?+?mCornerLength,?bottom?+?lateralOffset,?mCornerPaint);
26
27????//右下角左面的短线
28????canvas.drawLine(right?+?lateralOffset,?bottom?+?startOffset,?right?+?lateralOffset,?bottom?-?mCornerLength,?mCornerPaint);
29????//右下角底部的短线
30????canvas.drawLine(right?+?startOffset,?bottom?+?lateralOffset,?right?-?mCornerLength,?bottom?+?lateralOffset,?mCornerPaint);
31}
图片裁剪框的实现到此讲解完毕,更多细节请参考 ClipView
图片的缩放,移动
实现原理简述
这里我们是通过 ClipViewLayout 实现的,它是 RelativeLayout 的子类,里面含有 ImageView 和 ClipView(裁剪框)。我们通过监听 ClipViewLayout 的 onTouchEvent 事件,设置 imageView 的图片矩阵。
我们先来了解一下,主要有三种模式,NONE,DRAG, ZOOM。NONE 表示初始模式,DRAG 表示拖拽模式,ZOOM 表示缩放模式
1private?static?final?int?NONE?=?0;
2//动作标志:拖动
3private?static?final?int?DRAG?=?1;
4//动作标志:缩放
5private?static?final?int?ZOOM?=?2;
当我们多个手指按下的时候,加入两个手指之间的距离超过 10,此时我们认为进入 ZOOM 模式。DRAG 模式的话当我们手指按下的时候进入。NONE 模式,当我们手机抬起的时候,进入复位模式。
1switch?(event.getAction()?&?MotionEvent.ACTION_MASK)?{
2????case?MotionEvent.ACTION_DOWN:
3????????Log.d(TAG,?"onTouchEvent:?ACTION_DOWN");
4????????mSavedMatrix.set(mMatrix);
5????????//设置开始点位置
6????????mStart.set(event.getX(),?event.getY());
7????????mode?=?DRAG;
8????????break;
9????case?MotionEvent.ACTION_POINTER_DOWN:
10????????//开始放下时候两手指间的距离
11????????mOldDist?=?spacing(event);
12????????if?(mOldDist?>?10f)?{
13????????????mSavedMatrix.set(mMatrix);
14????????????midPoint(mMid,?event);
15????????????mode?=?ZOOM;
16????????}
17????????break;
18????case?MotionEvent.ACTION_UP:
19????case?MotionEvent.ACTION_POINTER_UP:
20????????mode?=?NONE;
21????????break;
接下来我们一起来看一下,我们 action_move 的时候,我们是怎样进行移动和缩放的。
移动的话相对比较简单,首先它会计算出我们这一次 event 事件相对我们 action_down 时候 event 事件的偏移量 dx, dy。接着调用 mMatrix.postTranslate(dx, dy),进行矩阵的移动。最后,再检测是否超出边界。
1case?MotionEvent.ACTION_MOVE:
2????Log.d(TAG,?"onTouchEvent:?mode?="?+?mode);
3????if?(mode?==?DRAG)?{?//拖动
4????????mMatrix.set(mSavedMatrix);
5????????float?dx?=?event.getX()?-?mStart.x;
6????????float?dy?=?event.getY()?-?mStart.y;
7
8????????mVerticalPadding?=?mClipView.getClipRect().top;
9????????mMatrix.postTranslate(dx,?dy);
10????????//检查边界
11????????checkBorder();
12????}?
13????---
14????mImageView.setImageMatrix(mMatrix);
边界检测 主要是检查缩放,移动后的图片矩阵的 left, top,right, bottom 是否在图片裁剪框之内,如果在的话,需要对图片矩阵进行移动。确保边界不在裁剪框之内。
1/**
2?*?边界检测
3?*/
4private?void?checkBorder()?{
5????RectF?rect?=?getMatrixRectF(mMatrix);
6????float?deltaX?=?0;
7????float?deltaY?=?0;
8????int?width?=?mImageView.getWidth();
9????int?height?=?mImageView.getHeight();
10????//?如果宽或高大于屏幕,则控制范围?;?这里的0.001是因为精度丢失会产生问题,但是误差一般很小,所以我们直接加了一个0.01
11????if?(rect.width()?+?0.01?>=?width?-?2?*?mHorizontalPadding)?{
12????????//?图片矩阵的最左边?>?裁剪边框的左边
13????????if?(rect.left?>?mHorizontalPadding)?{
14????????????deltaX?=?-rect.left?+?mHorizontalPadding;
15????????}
16????????//?图片矩阵的最右边?<?裁剪边框的右边
17????????if?(rect.right?<?width?-?mHorizontalPadding)?{
18????????????deltaX?=?width?-?mHorizontalPadding?-?rect.right;
19????????}
20????}
21????if?(rect.height()?+?0.01?>=?height?-?2?*?mVerticalPadding)?{
22????????//?图片矩阵的?top?>?裁剪边框的?top
23????????if?(rect.top?>?mVerticalPadding)?{
24????????????deltaY?=?-rect.top?+?mVerticalPadding;
25????????}
26????????//?图片矩阵的?bottom?<?裁剪边框的?bottom
27????????if?(rect.bottom?<?height?-?mVerticalPadding)?{
28????????????deltaY?=?height?-?mVerticalPadding?-?rect.bottom;
29????????}
30????}
31
32????Log.i(TAG,?"checkBorder:?deltaX="?+?deltaX?+?"?deltaY?=?"?+?deltaY);
33
34????mMatrix.postTranslate(deltaX,?deltaY);
35}
裁剪功能的实现
1/**
2?*?获取剪切图
3?*/
4public?Bitmap?clip()?{
5????mImageView.setDrawingCacheEnabled(true);
6????mImageView.buildDrawingCache();
7????Rect?rect?=?mClipView.getClipRect();
8????Bitmap?cropBitmap?=?null;
9????Bitmap?zoomedCropBitmap?=?null;
10????try?{
11????????cropBitmap?=?Bitmap.createBitmap(mImageView.getDrawingCache(),?rect.left,?rect.top,?rect.width(),?rect.height());
12????????//?对图片进行压缩,这里因为?mPreViewW?与宽高是相等的,所以压缩比例是?1:1,可以根据需要自己调整
13????????zoomedCropBitmap?=?BitmapUtil.zoomBitmap(cropBitmap,?mPreViewW,?mPreViewW);
14????}?catch?(Exception?e)?{
15????????e.printStackTrace();
16????}
17????if?(cropBitmap?!=?null)?{
18????????cropBitmap.recycle();
19????}
20????//?释放资源
21????mImageView.destroyDrawingCache();
22????return?zoomedCropBitmap;
23}
题外话
这个 Demo 涉及到的 Android 技术点其实是蛮多的,可以说是麻雀虽小,五脏俱全。Android 7.0 图片拍照适配,6.0 动态权限申请,Android 使用空白 fragment 处理 onActivityResult,动态权限申请,自定义 View,View 的事件分发机制等等。
这篇博客主要是介绍个人认为比较重要的技术点,其他的可以自行取了解。最后,提供一下 demo 下载地址:https://github.com/gdutxiaoxu/clipimagea
文章不易,如果大家喜欢这篇文章,或者对你有帮助希望大家多多,点赞 , 转发 ,关注。文章会持续更新的。绝对干货!!!
以上是关于调用 android 自身裁剪功能的主要内容,如果未能解决你的问题,请参考以下文章
调用 android 系统拍照结合 android-crop 裁剪图片
Android自定义控件实例,圆形头像(图库 + 裁剪+设置),上传头像显示为圆形,附源码
使用Android系统自带裁剪功能,小图可能出现黑框的解决办法