美文网首页app开发Android UI相关图片
细数图片上传功能用到的知识点(裁剪篇)

细数图片上传功能用到的知识点(裁剪篇)

作者: 信马归风 | 来源:发表于2017-02-21 22:09 被阅读275次

    综述

    图片选取&拍照篇链接 细数图片上传功能用到的知识点-图片选取&拍照篇

    我们先来看最终效果


    效果图

    其中涉及到的知识点

    • 蒙版的绘制
    • 手势拖动,手势放大
    • 图片的matrix操作
    • 图片的裁剪
      下面我就对这每个知识点进行详细的说明

    蒙版的绘制

    可以看到界面上覆盖了一层半透明的蒙版,中间扣出了一个正方形的区域,为了使边界更加明显我又绘制了一个白色的正方形的线框。
    实现方式自定义View 重写ondraw方法绘图作为背景。利用PorterDuffXfermode 中的XOR模式扣掉中间的矩形区域

    PorterDuffXfermode 各种模式
           //矩形区域的坐标可根据你的需求定制
           paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
           canvas.drawRect(mleft, mtop, mright, mbottom, paint);
           paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
           paint.setColor(Color.parseColor("#FFFFFF"));
           paint.setStrokeWidth(2f);
           paint.setStyle(Paint.Style.STROKE);
           canvas.drawRect(mleft - 2, mtop - 2, mright + 2, mbottom + 2, paint);
      
    

    手势拖动与手势放大

    这里有个业务需求,无论图片怎么样变化都必须保证图片的边界在矩形区域之外。另外结合我对于图片展示的需求,我决定自定义一个处理这些业务的imageView
    先说拖动手势的获取。重写onTouchEvent方法。记录回传坐标的变化,move事件中获得x和y的偏移量。拖动所需的数据很简单就能拿到。而手势放大需要我们支持多点触控的记录。要想获取的多点触控的数据,需要根据event.getAction() & MotionEvent.ACTION_MASK 来判断action ,之后获取当actionMotionEvent.ACTION_POINTER_DOWN时的触摸事件,该事件即为第二只手指的事件。
    那么如何在move事件中区分放大和拖动呢?我们这个时候就需要一个状态量来记录当前手势状态。回调ACTION_DOWN后为拖动状态。回调ACTION_POINTER_DOWN之后则为放大状态。

    //mode 为状态量,记录手势状态
       switch (event.getAction() & MotionEvent.ACTION_MASK) {
                case MotionEvent.ACTION_DOWN:
                    //拖动
                    mode = DRAG;
                    currentMaritx.set(this.getImageMatrix());
                    startPoint.set(event.getX(), event.getY());
                    break;
                case MotionEvent.ACTION_MOVE:
                    //拖动
                    if (mode == DRAG) {
                        dragDo(event);
                    }
                    //放大
                    else if (mode == ZOOM) {
                        zoomDo(event);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    break;
    
                case MotionEvent.ACTION_POINTER_UP:
                    break;
    
                case MotionEvent.ACTION_POINTER_DOWN:
                  
                    //判断第二个手指与第一个手指的位置。设置阈值避免一个手指两个触摸点的情况。
                    startDis = distance(event);
                    if (startDis > 10f) {
                         mode = ZOOM;
                        midPoint = mid(event);
                        currentMaritx.set(this.getImageMatrix());
                    }
                    break;
            }
            return true;
    

    dragDo()函数能够拿到手指拖动偏移量,处理图片拖动事件。而zoomDo()可以根据两个点距离变化倍率获得应该放大的倍率

      //获取两个手的距离
         float endDis = distance(event);
    //当前距离除以初始距离
         float scale = endDis / startDis;
    
     private float distance(MotionEvent event) {
            float dx = event.getX(1) - event.getX(0);
            float dy = event.getY(1) - event.getY(0);
            return (float) Math.sqrt(dx * dx + dy * dy);
        }
    

    图片的matrix操作

    图片的移动和放大等操作,我们选择利用imageView的matrix属性进行改变。

        Matrix matrix=getImageMatrix();
         
      //偏移操作,参数为想x,y的偏移量 
       matrix.postTranslate(realdx, realdy);
        //缩放操作,参数为 x,y的放大量,和缩放中心
       matrixteamp.postScale(scale, scale, midPoint.x, midPoint.y);
    

    别忘了,我们有业务需求,要保证图片的边界在矩形区域之外。
    首先我们要对传入的图片进行预处理,因为图片一开始的位置就可能出现越界的情况,另外我们需要合理的放大缩小图片来让用户的裁剪功能更加的快捷。

    //图片位置预制
        private void PreDealPic() {
            int bitmapWidth = bitmap.getWidth();
            int bitmapHeight = bitmap.getHeight();
    
            if ((float) (bitmapWidth) / bitmapHeight -  ViewMaxWidth/ ViewMaxHeight > 0) {
                // 宽图 宽/高 大于视图的宽/高
                float bili = ViewMaxWidth/ bitmapWidth;
                //超宽图  宽占满后,图片上下边越界情况
                if (bitmapHeight * bili < maxHeight) {
                    //放大至
    
                    float bei = maxWidth / (float) bitmapHeight;
                    final Matrix matrix = new Matrix();
                    matrix.postScale(bei , bei );
    //将其移动到边界
                    matrix.postTranslate(left , top);
                    moveImage.setScaleType(ImageView.ScaleType.MATRIX);
                    moveImage.setImageMatrix(matrix);
    
                }
            } else {
                //长图 宽/高 小于视图的宽/高
    
                float bili = ViewMaxHeight/ bitmapHeight;
                //超长图 高占满后,图片左右边越界情况
                if (bitmapWidth * bili < maxWidth) {
                    //放大至
    
                    float bei = maxWidth /bitmapWidth;
                    Matrix matrix = new Matrix();
                    matrix.postScale(bei , bei );
                    //移动到边界
                    matrix.postTranslate(left, top);
                    moveImage.setScaleType(ImageView.ScaleType.MATRIX);
                    moveImage.setImageMatrix(matrix);
                
            }
        }
    

    我们先来说拖动,直接将偏移量传参给偏移函数即可。让用户直接拖动是无法避免越界操作的。如此我们便需要对手势后图片的坐标进行判断,若本次操作会导致越界,则不予执行。但仅仅这样是不够的,如果总是不执行越界操作的话,那么图片拖动到边界会留下一个小区域拖动不过去(拖过去就越界了)。这样的用户体验并不友好。我们应该判断,如果本次操作会导致越界,那么我们只需要将图片移动到边界即可。

    要判断是否越界,我们需要拿到当前图片四个角的坐标。正确的获取方式为

    官方文档
      //获取图片四个坐标
     RectF rectF = new RectF();
    //传递bitmap本身的宽高
            rectF.right = bitmapWidth;
            rectF.bottom = bitmapHeight;
      matrixteamp.mapRect(rectF);
    

    既然我们拿到了四个角坐标那么就可以们判断图片是否越界了

    //判断图片是否越界
        if (maxleft > left && maxtop > top && right > maxright && bottom > maxbottom) {
             //执行拖动操作
        }
       else
       {
          //修正拖动
           transLateDragToRight();
       }
    
    
        //矫正拖动位置
        private void transLateDragToRight(float left, float right, float bottom, float top, Matrix matrix, RectF currentRect) {
            float realdx = dx;
            float realdy = dy;
    
            if (maxleft < left) {
                realdx = maxleft-currentRect.left;
            }
    
            if (maxtop < top) {
                realdy = maxtop - currentRect.top;
            }
            if (right < maxright) {
                realdx = maxright-currentRect.right;
            }
            if (bottom < maxbottom) {
                realdy = maxbottom - currentRect.bottom;
            }
            matrix.postTranslate(realdx, realdy);
    
        }
    
    

    放大也是同样的道理,需要进行修正。但注意放大与拖动存在不同,如果缩小后存在有一个边小于限制大小,那么位移修正是没有意义的。我们需要对其不操作,或者再次放大。

    所以需要判断

       if (right - left >= maxright - maxleft && bottom - top >= maxbottom - maxtop)
          //矫正放大位置
    
    
        //矫正放大位置
        private void transLateToRight(float left, float right, float bottom, float top, Matrix matrix) {
            float realdx = dx;
            float realdy = dy;
    
            if (maxleft < left) {
                realdx = maxleft - left;
            }
    
            if (maxtop < top) {
                realdy = maxtop - top;
            }
            if (right < maxright) {
                realdx = -right + maxright;
            }
            if (bottom < maxbottom) {
                realdy = maxbottom - bottom;
            }
            matrix.postTranslate(realdx, realdy);
    
        }
    

    这样处理过后即便图片紧靠边界,也能够进行缩小操作。直到缩小会导致有一个边小于限制。能够提供最佳的图片裁剪操作。

    图片的裁剪

    最后我们需要处理用户点击完成后的裁剪事件,裁剪事件我选择了获取当前跟view的视图,获取截图,并根据我的限制坐标对图片进行裁剪。

          //我的根布局
          relativeLayout.setDrawingCacheEnabled(true);
                relativeLayout.buildDrawingCache();
                Bitmap bitmap = relativeLayout.getDrawingCache();
       
    // 传入left top right bottom 来创建新的图片。
                Bitmap realfinBitamp = Bitmap.createBitmap(bitmap, (int) (picCultBackground.getMleft()), (int) (picCultBackground.getMtop()), (int) (picCultBackground.getMwidth()), (int) (picCultBackground.getMheight()));
                bitmap.recycle();
                relativeLayout.setDrawingCacheEnabled(false);
    

    realfinBitamp 就是用户裁剪得到的图片。根据业务需求再对bitmap 进行处理吧。
    到这里我们已经看完了裁剪功能的所有重要知识点和代码。但还要亲自尝试才能够明白各种细节的缘由。

    细数图片上传功能用到的知识点(图片选取&拍照篇)
    细数图片上传功能用到的知识点(裁剪篇)
    细数图片上传功能用到的知识点(图片压缩篇)

    相关文章

      网友评论

      • 旋哥:大神,有没有完整的源码地址。
        信马归风: @旋哥 公司的项目,没有单独剥离出来

      本文标题:细数图片上传功能用到的知识点(裁剪篇)

      本文链接:https://www.haomeiwen.com/subject/lmhuwttx.html