Android 可变裁剪区及缩放裁剪图片

来源:互联网 发布:mac照片无法访问iphone 编辑:程序博客网 时间:2024/05/24 15:38

大多图片裁剪大多两种操作:改变裁剪区图片不能缩放、裁剪区固定图片缩放,两种方法都可以裁剪到不同图片,本次介绍的是可变裁剪区同时能缩放图片,同时记录自己的开发项目过程。

裁剪视图一共三个view,最底层的缩放CilpImageView ,中间是可变裁剪区CilpBorderView,还有最顶层的CilpTouchView。监听CilpTouchView的OnTouch事件,通过判断down手势是否落在拉伸裁剪区的按钮内分发给CilpBorderView或者CilpImageView 。
Options类是默认裁剪配置。

CilpBorderView类:

public class CilpBorderView extends View {    private int borderColor = Color.parseColor("#FFFFFF");    private int outSideColor = Color.parseColor("#20000000");    private float borderWidth = 2;    private float lineWidth = 1;    private Rect[] rects=new Rect[2];    private Paint cutPaint;    private Paint outSidePaint;    private RectF cilpRectF;    //图片右坐标    private int iconRight=0;    //图片左坐标    private int iconLeft=0;    private Bitmap bitmap;    private int iconOffset;    private int width=0;    private int height=0;    private int verLine1;    private int verLine2;    private Options options;    private static final int TOP_ICON_ACTION=1;    private static final int BOTTOM_ICON_ACTION=2;    private int action=-1;    private float actionY;    public CilpBorderView(Context context) {        super(context);        initView();    }    public CilpBorderView(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public CilpBorderView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }    private void initView() {        cutPaint = new Paint();        cutPaint.setColor(borderColor);        cutPaint.setStrokeWidth(borderWidth);        cutPaint.setStyle(Paint.Style.STROKE);        outSidePaint=new Paint();        outSidePaint.setAntiAlias(true);        outSidePaint.setColor(outSideColor);        outSidePaint.setStyle(Paint.Style.FILL);        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_crop_drag_y);        iconOffset = bitmap.getWidth()/2;        options = new Options();    }    @Override    protected void onDraw(Canvas canvas) {        if (cilpRectF == null) {            cilpRectF = new RectF(options.paddingWidth, (getHeight() - options.cilpHeight) / 2, getWidth() - options.paddingWidth, (getHeight() + options.cilpHeight) / 2);            verLine1 = (int) (cilpRectF.left + cilpRectF.width() / 3);            verLine2 = (int) (cilpRectF.left + cilpRectF.width() * 2 / 3);        }        if (width == 0)            width = getWidth();        if (height == 0)            height = getHeight();        canvas.save();        drawLine(canvas);        drawRound(canvas);        drawIcon(canvas);        canvas.restore();        super.onDraw(canvas);    }    private void drawIcon(Canvas canvas) {        if (iconLeft == 0 && iconRight == 0) {            iconLeft = width / 2 - iconOffset;            iconRight=width / 2 + iconOffset;        }        canvas.drawBitmap(bitmap, iconLeft, cilpRectF.top-iconOffset, null);        canvas.drawBitmap(bitmap, iconLeft, cilpRectF.bottom-iconOffset, null);        Rect rect=new Rect(iconLeft-options.iconClick, (int)(cilpRectF.top-iconOffset)-options.iconClick,iconRight+options.iconClick,                 (int)(cilpRectF.top+iconOffset)+options.iconClick);        rects[0]=rect;        rect=new Rect(iconLeft-options.iconClick,(int)(cilpRectF.bottom-iconOffset)-options.iconClick,iconRight+options.iconClick,                (int)(cilpRectF.bottom+iconOffset)+options.iconClick);        rects[1]=rect;    }    private void drawLine(Canvas canvas){        cutPaint.setStrokeWidth(lineWidth);        float p = cilpRectF.top + cilpRectF.height() / 3;        //横线        canvas.drawLine(options.paddingWidth, p, width-options.paddingWidth, p, cutPaint);        p = cilpRectF.top + cilpRectF.height() * 2 / 3;        canvas.drawLine(options.paddingWidth, p, width-options.paddingWidth, p, cutPaint);        //竖线        canvas.drawLine(verLine1, cilpRectF.top, verLine1, cilpRectF.bottom, cutPaint);        canvas.drawLine(verLine2, cilpRectF.top, verLine2, cilpRectF.bottom, cutPaint);    }    private void drawRound(Canvas canvas) {        //绘制边框        cutPaint.setStrokeWidth(borderWidth);        canvas.drawRect(cilpRectF, cutPaint);        //绘制外区域        //左中框        canvas.drawRect(0, cilpRectF.top, options.paddingWidth, cilpRectF.bottom, outSidePaint);        //上框        canvas.drawRect(0, 0, width, cilpRectF.top, outSidePaint);        //右中框        canvas.drawRect(cilpRectF.right, cilpRectF.top, width, cilpRectF.bottom, outSidePaint);        //下框        canvas.drawRect(0, cilpRectF.bottom, width, height, outSidePaint);    }    public void setOptions(Options options) {        this.options = options;    }    /**     * 根据手势做拉伸     */    public boolean iconOntouch(MotionEvent event,RectF imgRect){        switch (event.getAction()){            case MotionEvent.ACTION_DOWN:                if (rects[0].contains((int)event.getX(),(int)event.getY())) {                    action=TOP_ICON_ACTION;                }                if (rects[1].contains((int)event.getX(),(int)event.getY())) {                    action=BOTTOM_ICON_ACTION;                }                actionY=event.getY();                break;            case MotionEvent.ACTION_MOVE:                float y=actionY-event.getY();                switch (action){                    case TOP_ICON_ACTION:                        cilpRectF.top=cilpRectF.top-y;                        break;                    case BOTTOM_ICON_ACTION:                        cilpRectF.bottom=cilpRectF.bottom-y;                        break;                }                checkBroad(imgRect);                actionY=event.getY();                invalidate();                break;            case MotionEvent.ACTION_UP:                action=-1;                break;        }        return true;    }    /**     * @description 边界校验     * @param imgRect     */    private void checkBroad(RectF imgRect) {        if ((cilpRectF.bottom-cilpRectF.top) < options.min_height){//高度少于最小高度            switch (action) {                case TOP_ICON_ACTION:                    cilpRectF.top=cilpRectF.bottom - options.min_height;                    break;                case BOTTOM_ICON_ACTION:                    cilpRectF.bottom=cilpRectF.top + options.min_height;                    break;            }        } else if ((cilpRectF.bottom-cilpRectF.top) > options.max_height){//高度大于最大高度            switch (action) {                case TOP_ICON_ACTION:                    cilpRectF.top=cilpRectF.bottom - options.max_height;                    break;                case BOTTOM_ICON_ACTION:                    cilpRectF.bottom=cilpRectF.top + options.max_height;                    break;            }        }        if (cilpRectF.top < options.paddingHeight) {            cilpRectF.top = options.paddingHeight;        }        if (cilpRectF.bottom > height-options.paddingHeight){            cilpRectF.bottom = height-options.paddingHeight;        }        if ( cilpRectF.top < imgRect.top) {            cilpRectF.top = imgRect.top;        }        if ( cilpRectF.bottom > imgRect.bottom) {            cilpRectF.bottom = imgRect.bottom;        }    }    //判断手势down事件是否落在拉伸按钮区域内    public boolean isIconClick(MotionEvent event){        if (rects[0].contains((int)event.getX(), (int)event.getY())) {            System.out.println("点击顶部图标");            action=TOP_ICON_ACTION;            return true;        }        if (rects[1].contains((int)event.getX(), (int)event.getY())) {            System.out.println("点击底部图标");            action=BOTTOM_ICON_ACTION;            return true;        }        actionY=event.getY();        return false;    }    public RectF getCilpRectF() {        return cilpRectF;    }    public void setCilpRectF(RectF cilpRectF) {        this.cilpRectF = cilpRectF;        invalidate();    }

CilpImageView 类:

public class CilpImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,ViewTreeObserver.OnGlobalLayoutListener{    private static float SCALE_MID = 0.1f;    /**     * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小0     */    private float initScale = 1.0f;    /**     * 缩放的手势检测     */    private ScaleGestureDetector scaleGestureDetector = null;    private final Matrix scaleMatrix = new Matrix();    private final float[] matrixValues = new float[9];    /**     * 用于双击缩放     */    private GestureDetector gestureDetector;    //是否自动缩放任务    private boolean isAutoScale;    private float mLastX;    private float mLastY;    private float centerX;    private float centerY;    //图片原始宽高    private int drawableW;    private int drawableH;    private boolean isCanDrag;    private int lastPointerCount;    private RectF borderRectF;    private CilpRectFChangeListener cilpRectFChangeListener;    private Options options;    public CilpImageView(Context context) {        super(context);        initView(context);    }    public CilpImageView(Context context, AttributeSet attrs) {        super(context, attrs);        initView(context);    }    public CilpImageView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView(context);    }    private void initView(Context context) {        options = new Options();        setScaleType(ScaleType.MATRIX);        setBackgroundColor(Color.BLACK);        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {            @Override            public boolean onDoubleTap(MotionEvent e) {//双击                if (isAutoScale)                    return true;                float x=e.getX();                float y=e.getY();                if (getScale() != initScale) {                    CilpImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y),16);                } else {                    CilpImageView.this.postDelayed(new AutoScaleRunnable(initScale * 2, x, y),16);                }                isAutoScale=true;                return true;            }        });        scaleGestureDetector=new ScaleGestureDetector(context,this);    }    /**     * 获得当前的缩放比例     *     * @return     */    public final float getScale() {        scaleMatrix.getValues(matrixValues);        return matrixValues[Matrix.MSCALE_X];    }    /**     * 边界校验     */    private void checkBorder(){        RectF rectF=getMatrixRectF();        float deltaX = 0;        float deltaY = 0;        int width=getWidth();        // 如果宽或高大于屏幕,则控制范围,这里的0.001是因为精度丢失会产生问题,但是误差一般很小,所以直接加了一个0.01        if (rectF.width() + 0.01 >= width - 2 * options.paddingWidth) {            if (rectF.left > options.paddingWidth) {                deltaX = -rectF.left + options.paddingWidth;            }            if (rectF.right < width - options.paddingWidth){                deltaX = width - options.paddingWidth - rectF.right;            }        }        if (rectF.height() +0.01 >= borderRectF.height()){            if (rectF.top > borderRectF.top){                deltaY = -rectF.top + borderRectF.top;            }            if (rectF.bottom < borderRectF.bottom){                deltaY = borderRectF.bottom-rectF.bottom;            }        }        scaleMatrix.postTranslate(deltaX,deltaY);    }    /**     * 根据当前图片的Matrix获得图片的范围     */    public RectF getMatrixRectF() {        Matrix matrix = scaleMatrix;        RectF rect = new RectF();        Drawable d = getDrawable();        if (null != d) {            rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());            matrix.mapRect(rect);        }        return rect;    }    @Override    public boolean onScale(ScaleGestureDetector detector) {        if (getDrawable() == null) {            return true;        }        float scaleFactor=detector.getScaleFactor();        scaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY());        checkBorder();        setImageMatrix(scaleMatrix);        setCilpRectFIfNeed();        return true;    }    @Override    public boolean onScaleBegin(ScaleGestureDetector detector) {        return true;    }    @Override    public void onScaleEnd(ScaleGestureDetector detector) { }    /**     * 根据手势做缩放     */    public boolean onImageTouch(MotionEvent event, RectF borderRect) {        this.borderRectF=borderRect;        if (gestureDetector.onTouchEvent(event)){            return true;        }        scaleGestureDetector.onTouchEvent(event);        float x = 0, y = 0;        // 拿到触摸点的个数        final int pointerCount = event.getPointerCount();        // 得到多个触摸点的x与y        for (int i = 0; i < pointerCount; i++) {            x += event.getX(i);            y += event.getY(i);        }        x = x / pointerCount;        y = y / pointerCount;        /**         * 每当触摸点发生变化时,重置mLasX , mLastY         */        if (pointerCount != lastPointerCount) {            isCanDrag = false;            mLastX = x;            mLastY = y;        }        lastPointerCount = pointerCount;        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            resetMidScale();            mLastX = x;            mLastY = y;            break;        case MotionEvent.ACTION_MOVE:            float dx = x - mLastX;            float dy = y - mLastY;            if (!isCanDrag) {                isCanDrag = isCanDrag(dx, dy);            }            if (isCanDrag) {                if (getDrawable() != null) {                    RectF rectF = getMatrixRectF();                    // 如果宽度小于屏幕宽度,则禁止左右移动                    if (rectF.width() <= borderRectF.width()) {                        dx = 0;                    }                    // 如果高度小于屏幕高度,则禁止上下移动                    if (rectF.height() <= borderRectF.height()) {                        dy = 0;                    }                    scaleMatrix.postTranslate(dx, dy);                    checkBorder();                    setImageMatrix(scaleMatrix);                }            }            mLastX = x;            mLastY = y;            break;        case MotionEvent.ACTION_UP:            if (SCALE_MID>getScale()) {                postDelayed(                        new AutoScaleRunnable(SCALE_MID, getWidth()/2, event.getY()), 1);            }            break;        case MotionEvent.ACTION_CANCEL:            lastPointerCount = 0;            break;        }        return true;    }    /**     * 是否是拖动行为     */    private boolean isCanDrag(float dx, float dy) {        return Math.sqrt((dx * dx) + (dy * dy)) >= 0;    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        getViewTreeObserver().addOnGlobalLayoutListener(this);    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {            getViewTreeObserver().removeOnGlobalLayoutListener(this);        } else {            getViewTreeObserver().removeGlobalOnLayoutListener(this);        }    }    @Override    public void onGlobalLayout() {        if (0!=getHeight()) {            if (borderRectF==null)                borderRectF=new RectF(options.paddingWidth,(getHeight() - options.cilpHeight)/2,                        getWidth()-options.paddingWidth,(getHeight() + options.cilpHeight)/2);            int height=getHeight();            Drawable d = getDrawable();            if (d == null)                return;            int width = getWidth();            // 拿到图片的宽和高            drawableW = d.getIntrinsicWidth();            drawableH = d.getIntrinsicHeight();            float scale ;            scale= borderRectF.width() /drawableW;            scaleMatrix.reset();            initScale = scale;            SCALE_MID=initScale;            centerX=(width - (borderRectF.width())) / 2;            centerY=(height - drawableH*scale) / 2;            scaleMatrix.postTranslate(centerX,  centerY);            scaleMatrix.postScale(scale, scale,centerX,centerY);            // 图片移动至屏幕中心            setImageMatrix(scaleMatrix);            setCilpRectFIfNeed();        }    }    //校验裁剪边界    private void setCilpRectFIfNeed(){        RectF rectF=getMatrixRectF();        if (cilpRectFChangeListener!=null && (rectF.height() < borderRectF.height())){            borderRectF.top = rectF.top;            borderRectF.bottom = rectF.bottom;            rectF.right = borderRectF.right;            rectF.left = borderRectF.left;            cilpRectFChangeListener.onChange(rectF);        }    }    /**     * 计算最小缩放值     */    private void resetMidScale(){        int distanceW = (int) (drawableW - borderRectF.width());        int distanceH = (int) (drawableH - borderRectF.height());        if (distanceH < distanceW) {//按高度算最小缩放比例            SCALE_MID= borderRectF.height() /drawableH;        } else {            SCALE_MID= borderRectF.width() /drawableW;        }    }    /**     * 剪切图片,返回剪切后的bitmap对象     *     * @return     */    public Bitmap clip(RectF rectF) {        Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(),                Bitmap.Config.ARGB_8888);        Canvas canvas = new Canvas(bitmap);        draw(canvas);        bitmap=Bitmap.createBitmap(bitmap, (int)rectF.left,                (int)rectF.top,(int)rectF.width(),(int)rectF.height());        return bitmap;    }    /**     * 自动缩放任务     */    private class AutoScaleRunnable implements Runnable{        static final float BIGGER=1.07f;        static final float SMALLER=0.93f;        private float tarScale;        private float tmpScale;        /**         * 缩放的中中心         */        private float x;        private float y;        AutoScaleRunnable(float tarScale, float x, float y) {            this.tarScale=tarScale;            this.x=x;            this.y=y;            if (getScale() < tarScale){                tmpScale=BIGGER;            } else {                tmpScale=SMALLER;            }        }        @Override        public void run() {            scaleMatrix.postScale(tmpScale, tmpScale, x, y);            checkBorder();            setImageMatrix(scaleMatrix);            final float currentScale =  getScale();            // 如果值在合法范围内,继续缩放            if ( ((tmpScale > 1f) && (currentScale < tarScale)) ||                    ((tmpScale < 1f) && (tarScale <currentScale))){                final float nextScale = tmpScale * currentScale;//下次的缩放比例                if (((tmpScale > 1f) && (currentScale <tarScale))){//放大                    if (nextScale > tarScale){                        tmpScale = tarScale / currentScale;                    }                }                if (((tmpScale < 1f) && (currentScale > tarScale))){//缩小                    if (nextScale < tarScale){                        tmpScale = tarScale / currentScale;                    }                }                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {                    CilpImageView.this.postOnAnimation(this);                }else{                    CilpImageView.this.postDelayed(this,1);                }            } else {                setCilpRectFIfNeed();                isAutoScale=false;            }        }    }    public void setCilpRectFChangeListener(CilpRectFChangeListener cilpRectFChangeListener) {        this.cilpRectFChangeListener = cilpRectFChangeListener;    }    public void setOptions(Options options) {        this.options = options;    }    public interface CilpRectFChangeListener{        void onChange(RectF rectF);    }}

CilpTouchView类:

public class CilpTouchView extends View implements View.OnTouchListener{    private CilpImageView imageView;    private CilpBorderView borderView;    private boolean iconClick;    private RectF changeRect;    public CilpTouchView(Context context, CilpBorderView borderView, final CilpImageView imageView) {        super(context);        if (borderView == null || imageView == null) {            throw new NullPointerException("view is null");        }        this.borderView=borderView;        this.imageView=imageView;        imageView.setCilpRectFChangeListener(new CilpImageView.CilpRectFChangeListener() {            @Override            public void onChange(RectF rectF) {                CilpTouchView.this.borderView.setCilpRectF(rectF);            }        });        setOnTouchListener(this);    }    @Override    public boolean onTouch(View v, MotionEvent event) {        if (event.getAction() == MotionEvent.ACTION_DOWN) {            if (borderView.isIconClick(event)){                iconClick = true;                changeRect= imageView.getMatrixRectF();            } else {                iconClick = false;                changeRect=borderView.getCilpRectF();            }        }        if (iconClick){            borderView.iconOntouch(event, changeRect);        } else {            imageView.onImageTouch(event, changeRect);        }        if (event.getAction() == MotionEvent.ACTION_UP) {            iconClick=false;        }        return true;    }}

效果图:
这里写图片描述

例子下载:https://github.com/gdflk/VariableCilpImageView

0 0
原创粉丝点击