拍照——裁剪,或者是选择图片——裁剪,是我们设置头像或上传图片时经常需要的一组操作。上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现。
背景
- 下面的需求都来自产品。
- 裁剪图片要像微信那样,拖动和放大的是图片,裁剪框不动。
- 裁剪框外的内容要有半透明黑色遮罩。
- 裁剪框下面要显示一行提示文字(这点我至今还是持保留意见的)。
在Android中,裁剪图片的控件库还是挺多的,特别是github上比较流行的几个,都已经进化到比较稳定的阶段,但比较遗憾的是它们的裁剪过程是拖动或缩放裁剪框,于是只好自己再找,看有没有现成或半成品的轮子,可以不必从零开始。
踏破铁鞋无觅处,皇天不负苦心人。我终于找到了两篇博客:《Android高仿微信头像裁剪》和《Android 高仿微信头像截取 打造不一样的自定义控件》,以及csdn上找到的前面博客所对应的一份代码,并最终实现了自己的裁剪控件。
大神的实现过程
首先先了解一下上面的高仿微信裁剪控件的实现过程。说起来也不难,主要是下面几点:
1,重写ImageView
,并监听手势事件,包括双点,两点缩放,拖动,使它成为一个实现缩放拖动图片功能的控件。
2,定义一个Matrix
成员变量,对于维护该图片的缩放、平移等矩阵数据。
3,拖动或缩放时,图片与裁剪框的相交面积一定与裁剪框相等。即图片不能拖离裁剪框。
3,在设置图片时,先根据图片的大小进行初始化的缩放平移操作,使得上面第三条的条件下图片尽可能的小。
4,每次接收到相对应的手势事件,都进行对应的矩阵计算,并将计算结果通过ImageView
的setImageMatrix
方法应用到图片上。
5,裁剪框是一个单独的控件,与ImageView
同样大,叠加到它上面显示出来。
6,用一个XXXLayout
把裁剪框和缩放封装起来。
7,裁剪时,先创建一个空的Bitmap并用其创建一个Canvas
,把缩放平移后的图片画到这个Bitmap上,并创建在裁剪框内的Bitmap
(通过调用Bitmap.createBitmap
方法)。
我的定制内容
我拿到的代码是鸿洋大神版本之后再被改动的,代码上有点乱(虽然功能上是实现的裁剪)。在原有的功能上,我希望进行的改动有:
- 合并裁剪框的内容到ImageView中
- 裁剪框可以是任意长宽比的矩形
- 裁剪框的左右外边距可以设置
- 遮罩层颜色可以设置
- 裁剪框下有提示文字(自己的产品需求)
- 后面产品又加入了一条裁剪图片的最大大小
属性定义
在上面的功能需求中,我定义了以下属性:
<declare-styleable name="ClipImageView">
<attr name="civHeight" format="integer"/>
<attr name="civWidth" format="integer"/>
<attr name="civTipText" format="string"/>
<attr name="civTipTextSize" format="dimension"/>
<attr name="civMaskColor" format="color"/>
<attr name="civClipPadding" format="dimension"/>
</declare-styleable>
其中:
civHeight
和civWidth
是裁剪框的宽高比例。civTipText
提示文字的内容civTipTextSize
提示文字的大小civMaskColor
遮罩层的颜色值civClipPadding
裁剪内边距。由于裁剪框是在控件内部的,最终我选择使用padding来说明裁剪框与我们控件边缘的距离。
成员变量
成员变量我进行了一些改动,把原本用于定义裁剪框的水平边距变量及其他没什么用的变量等给去掉了,并加入了自己的一些成员变量,最终如下:
private final int mMaskColor;//遮罩层颜色
private final Paint mPaint;//画笔
private final int mWidth;//裁剪框宽的大小(从属性上读到的整型值)
private final int mHeight;//裁剪框高的大小(同上)
private final String mTipText;//提示文字
private final int mClipPadding;//裁剪框相对于控件的内边距
private float mScaleMax = 4.0f;//图片最大缩放大小
private float mScaleMin = 2.0f;//图片最小缩放大小</