在上一篇文章中Android中的图片裁剪(二)之开源项目介绍了一些优秀的图片裁剪开源项目,在我们实现自己的裁剪功能的时候,也可以看下这些开源项目的源码,看看大牛们都是怎么实现的。

createBitmap方法

首先要做图片裁剪的功能,要先认识Bitmap,在这个类里面,有几个方法可以帮助我们实现功能。就比如下面这一个方法

/**
 * Returns an immutable bitmap from the specified subset of the source
 * bitmap. The new bitmap may be the same object as source, or a copy may
 * have been made. It is initialized with the same density as the original
 * bitmap.
 *...
 */
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height)

好吧,方法注解太长,被我省略掉一些了,但是用起来其实挺简单的,只要传入指定的范围,它就能生成一个新的位图给你。(这里先说一句,图片的裁剪功能其实做起来不难,要做好就挺麻烦的,在实际的工具开发中免不了要自定义View这样的工作),而这边的范围一般是一个可拖动的方框,点击裁剪按钮的时候生成我们需要的图片。当然createBitmap方法还有很多其他重载实现,这里就不一一贴出来了。在具体实现的时候我们可以自定义两个View

public class CropImageView extends ImageView {

    HighlightView hv;
    //...
 
}

class HighlightView {
    //...
}

这里只是简单的提供一下思路,HighlightView用来表示裁剪框对象,封装裁剪框的一些信息啊,比如宽高和触摸状态什么的。CropImageView继承ImageView用来显示图片和处理触摸事件,在触摸事件中改变HighlightView的状态:

public boolean onTouchEvent(@NonNull MotionEvent event) {
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
            //对触摸点进行一些逻辑判断,比如摸到裁剪框的边还是其他什么的
            int edge = hv.getHit(event.getX(), event.getY());
            //...
            //然后根据逻辑判断结果设置裁剪框的状态
            hv.setMode((edge == HighlightView.MOVE)
                        ? HighlightView.ModifyMode.Move
                        : HighlightView.ModifyMode.Grow);
            //...
        break;
            //...

啦啦啦,是不是说了跟没说一样,主要是具体实现起来真的挺麻烦的,贴那么多代码出来也没啥用,我自己都要看晕了,总归自己造轮子还是挺辛苦的,而且刚造出来也肯定有一些问题的,所以才有那句名言么,不要重复造轮子,我觉得只要看懂源代码的思路,然后根据需求,在别人的轮子上进行改造,这样就快的多啦。

Matrix的基本使用

一般呢,在裁剪的工具中,在对图片裁剪前,多可以对图片进行一些缩放,平移,旋转之类的操作,这些效果都是可以通过Matrix来实现的,那这个类怎么用呢。我们先看一下官方文档中Matrix的介绍(自带梯子) ,首先看一下它的官方定义:

**The Matrix class holds a 3x3 matrix for transforming coordinates. **

直译过来呢,就是一个转换坐标的3x3矩阵,你妹啊,什么是3x3矩阵…我也不知道啊 ,高等数学这东西我早就扔了好么,不过不知道没关系,面向对象么,我先知道怎么用就可以了,有时间有闲情再去深究。我们先主要看一下Matrix类里面,我们要用到的一些方法(补充说一下,这Matrix类是在graphics包下的,不要导成opengl包下的):

/**
 * Postconcats the matrix with the specified translation.
 * M' = T(dx, dy) * M
 */
public boolean postTranslate(float dx, float dy)

/**
 * Postconcats the matrix with the specified rotation.
 * M' = R(degrees) * M
 */
public boolean postRotate(float degrees)

好啦,毕竟是文档找的到东西,我这里就只贴两个方法出来,看注解应该很容易理解方法的使用,第一个方法就是对矩阵进行左乘T(dx, dy),第二个方法就是对矩阵左乘R(degrees),通过源码我们可以看到,方法里面通过jni调用lib层用c,c++实现运算转换的。反正直白点讲,在对它进行图片变换的时候,第一个方法就是平移图片,第二个方法就是旋转图片,啦啦啦,其他方法大家可以自行查找文档,问题都不大。

图片的平移,缩放,旋转

通过重写View或者Activity的onTouch事件来进行图片的平移,缩放和旋转会比较方便,这里我们要先做一些初始化工作,比如获取图片之类的,一般我们将图片的大小控制在手机内存的1/8,防止OOM和卡顿,然后就是在onTouch事件里做具体实现:

public boolean onTouch(View v, MotionEvent event)
{

    switch (event.getAction() & MotionEvent.ACTION_MASK)
    {
        // 主点按下
        case MotionEvent.ACTION_DOWN:
            cachMatrix.set(matrix);//用来变化的矩阵
            prev.set(event.getX(), event.getY());
            mode = DRAG;
            break;
        //...
        case MotionEvent.ACTION_MOVE:
            if (mode == DRAG)//拖动
            {
                matrix.set(cachMatrix);
                matrix.postTranslate(event.getX() - prev.x, event.getY() - prev.y);
            }
            else if (mode == ZOOM)//缩放
            {
                float newDist = spacing(event);
                if (newDist > 10f)
                {
                    matrix.set(cachMatrix);
                    float tScale = newDist / dist;
                    matrix.postScale(tScale, tScale, mid.x, mid.y);
                }
            }
            break;
    }
    imgView.setImageMatrix(matrix);
    //...
    return true;
}

代码就放了部分上去,主要说明一下思路,当然在将这些功能和裁剪进行合并的时候,是要对裁剪框的位置再进行计算的,比如因为对图片缩放了,所以再返回剪裁框大小的时候是要乘上缩放值的。

public Rect getScaledCropRect(float scale) {
    return new Rect((int) (cropRect.left * scale), (int) (cropRect.top * scale),
            (int) (cropRect.right * scale), (int) (cropRect.bottom * scale));
}

总结

图片的裁剪就先写到这里,主要介绍了一些关键方法和整体的实现思路,后面有时间的话,我会把代码整理后发布到github上去的,到时候再来补链接。