Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 可變裁剪區及縮放裁剪圖片

Android 可變裁剪區及縮放裁剪圖片

編輯:關於Android編程

大多圖片裁剪大多兩種操作:改變裁剪區圖片不能縮放、裁剪區固定圖片縮放,兩種方法都可以裁剪到不同圖片,本次介紹的是可變裁剪區同時能縮放圖片,同時記錄自己的開發項目過程。

裁剪視圖一共三個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  1f) && (currentScale  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/ariableCilpImageView

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved