1. 程式人生 > >android 圖片任意裁剪(自定義裁剪,相簿)

android 圖片任意裁剪(自定義裁剪,相簿)

執行效果

執行效果圖

提醒:許可權在6.0及以上需申請許可權(圖上使用的是6.0以下手機)

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

關鍵1:怎麼把需要裁剪的圖片匯入到自定義的裁剪控制元件

相簿中選中的圖片地址獲取

//獲取相簿中圖片的路徑  返回圖片路徑
 private String  initPA(Intent data){
        //通過ContentResolver介面訪問ContentProvider所提供的資料
        ContentResolver resolver=getContentResolver();
Uri uri=data.getData(); try { //顯示得到的bitmap圖片 Bitmap bm= MediaStore.Images.Media.getBitmap(resolver,uri); }catch (IOException e){ e.printStackTrace(); } String[] proj={MediaStore.Images.Media.DATA}; Cursor cursor=managedQuery(uri,proj,null,null,null);
int column_index=cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path=cursor.getString(column_index); Log.e("path>>",path); return path; }

關鍵二:怎麼儲存

需要自己定義個資料夾把繪製好的圖片儲存到資料夾中

//儲存圖片
    public void saveBitmap(Context context,Bitmap bmp) {
        File appDir = new File(Environment.getExternalStorageDirectory
(), "Boohee"); if (!appDir.exists()) { appDir.mkdir(); } String fileName = System.currentTimeMillis() + ".jpg"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 其次把檔案插入到系統圖庫 try { MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null); } catch (FileNotFoundException e) { e.printStackTrace(); } // 最後通知相簿更新 context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path))); }

補充:attrs程式碼

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="Life_CropImage">
        <attr name="life_Crop_ratio" format="float"/>
    </declare-styleable>
</resources>

xml全部程式碼

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="wz.com.mycut.MainActivity">

    <wz.com.mycut.SeniorCropImageView
        android:id="@+id/my_crop"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <Button
        android:text="Button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/button" />
</RelativeLayout>

MainActivity全部程式碼

package wz.com.mycut;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    //地址
    String path="http://p4.qhimg.com/t01b090f22c87857c1c.jpg";
    //裁剪框
    SeniorCropImageView mseniorCropImageView;
    MainActivity mActivity;
    ProgressBar pb;
    //圖片資源
    ImageView img;
    //裁剪後的圖片
    Bitmap bitmap=null;
    //儲存圖片按鈕
    Button button=null;
    //相簿跳轉需要的引數
    private final String IMAGE_TYPE="image/*";
    private final int IMAGE_CODE=111;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //選擇圖片
        initDialog();
        //初始化介面元件
        initView();

        //設定裁剪框的外邊距(padding)
        mseniorCropImageView.setCropRectPadding(0f);
        //儲存裁剪後的圖片
        bitmap=mseniorCropImageView.saveCrop();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                saveBitmap(MainActivity.this,mseniorCropImageView.saveCrop());
                Toast.makeText(MainActivity.this,"圖片儲存成功",Toast.LENGTH_LONG).show();
            }
        });
    }

    //初始化介面元件
    private void initView(){
        button=(Button) this.findViewById(R.id.button);
        //例項化裁剪物件
        mseniorCropImageView =(SeniorCropImageView) this.findViewById(R.id.my_crop);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if(data==null){
            Toast.makeText(this,"沒有獲取到返回的圖片",Toast.LENGTH_LONG).show();
        }
        if (resultCode==Activity.RESULT_OK){
            if (requestCode==IMAGE_CODE){
                path=initPA(data);
                //獲取圖片路徑
                //裁剪框顯示圖片
                mseniorCropImageView.setImagePath(path);
            }
        }
    }

    //對話方塊選擇圖片
    private void initDialog(){
        //宣告對話方塊
        AlertDialog.Builder dialog=new AlertDialog.Builder(this);
        //對話方塊顯示的列表文字資源
        final String[] name={"相簿","相機"};
        //設定標題
        dialog.setTitle("圖片選擇");
        dialog.setItems(name, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                switch (i){
                    case 0:
                        //跳轉到相簿
                        initPic();
//                        Toast.makeText(MainActivity.this,"你點選了相簿",Toast.LENGTH_LONG).show();
                        break;
                    case  1:
                        //跳轉到相機
                        Toast.makeText(MainActivity.this,"你點選了相機",Toast.LENGTH_LONG).show();

                        break;
                }
            }
        });
        //顯示
        dialog.show();
    }

    //跳轉到相簿,選擇圖片
    private void initPic() {
        Intent pintent=new Intent(Intent.ACTION_GET_CONTENT);
        pintent.setType(IMAGE_TYPE);
        startActivityForResult(pintent,IMAGE_CODE);
    }

    //獲取相簿中圖片的路徑  返回圖片路徑
    private String  initPA(Intent data){
        //通過ContentResolver介面訪問ContentProvider所提供的資料
        ContentResolver resolver=getContentResolver();
        Uri uri=data.getData();
        try {
            //顯示得到的bitmap圖片
            Bitmap bm= MediaStore.Images.Media.getBitmap(resolver,uri);
        }catch (IOException e){
            e.printStackTrace();
        }
        String[] proj={MediaStore.Images.Media.DATA};
        Cursor cursor=managedQuery(uri,proj,null,null,null);
        int column_index=cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        String path=cursor.getString(column_index);
        Log.e("path>>",path);
        return path;
    }

    //儲存圖片
    public void saveBitmap(Context context,Bitmap bmp) {
        File appDir = new File(Environment.getExternalStorageDirectory(), "Boohee");
        if (!appDir.exists()) {
            appDir.mkdir();
        }
        String fileName = System.currentTimeMillis() + ".jpg";
        File file = new File(appDir, fileName);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bmp.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.flush();
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 其次把檔案插入到系統圖庫
        try {
            MediaStore.Images.Media.insertImage(context.getContentResolver(),
                    file.getAbsolutePath(), fileName, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        // 最後通知相簿更新
        context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));
    }
}

主要的自定義控制元件程式碼(魏成林的Android 以任意比例裁剪圖片中的程式碼)

package wz.com.mycut;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;

/**
 * Created by lenovo on 2018/1/13.
 */
public class SeniorCropImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,
        View.OnLayoutChangeListener {

    /* For drawing color field start */
    private static final int LINE_COLOR = Color.WHITE;
    private static final int OUTER_MASK_COLOR = Color.argb(191, 0, 0, 0);
    private static final int LINE_WIDTH_IN_DP = 1;
    private final float[] mMatrixValues = new float[9];
    protected Matrix mSupportMatrix;
    protected ScaleGestureDetector mScaleGestureDetector;
    /* For drawing color field end */
    protected Paint mPaint;
    /*
     * 寬比高
     */
    protected float mRatio = 1.0f;
    protected RectF mCropRect;
    //RectFPadding是適應產品需求,給裁剪框mCropRect設定一下padding -- chenglin 2016年04月18日
    protected float RectFPadding = 0;
    protected int mLastX;
    protected int mLastY;
    protected OPERATION mOperation;
    private onBitmapLoadListener iBitmapLoading = null;
    private boolean mEnableDrawCropWidget = true;
    /*
    For scale and drag
     */
    private Matrix mBaseMatrix;
    private Matrix mDrawMatrix;
    private AccelerateDecelerateInterpolator sInterpolator = new AccelerateDecelerateInterpolator();
    private Path mPath;
    private int mLineWidth;
    private float mScaleMax = 3.0f;
    private RectF mBoundaryRect;
    private int mRotation = 0;
    private int mImageWidth;
    private int mImageHeight;
    private int mDisplayW;
    private int mDisplayH;

    public SeniorCropImageView(Context context) {
        this(context, null);
    }

    public SeniorCropImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SeniorCropImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        if (attrs != null) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Life_CropImage);
            mRatio = a.getFloat(R.styleable.Life_CropImage_life_Crop_ratio, 1.0f);

            a.recycle();
        }

        init();
    }

    public static void decodeImageForCropping(final String path, final IDecodeCallback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int rotation = 0;

                // 讀取一下exif中的rotation
                try {
                    ExifInterface exif = new ExifInterface(path);
                    final int rotate = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
                    switch (rotate) {
                        case ExifInterface.ORIENTATION_ROTATE_90:
                            rotation = 90;
                            break;
                        case ExifInterface.ORIENTATION_ROTATE_180:
                            rotation = 180;
                            break;
                        case ExifInterface.ORIENTATION_ROTATE_270:
                            rotation = 270;
                            break;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }

                final BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                BitmapFactory.decodeFile(path, options);

                final int textureLimit = getMaxTextureSize();

                int scale = 1;
                while (options.outWidth / scale >= textureLimit) {
                    scale *= 2;
                }

                while (options.outHeight / scale >= textureLimit) {
                    scale *= 2;
                }

                options.inSampleSize = scale;
                options.inJustDecodeBounds = false;

                Bitmap bitmap = null;
                try {
                    bitmap = BitmapFactory.decodeFile(path, options);
                } catch (OutOfMemoryError e) {
                    e.printStackTrace();
                }

                final Bitmap bimapDecoded = bitmap;
                if (bimapDecoded == null) {
                    return;
                }

                if (callback != null) {
                    callback.onDecoded(rotation, bimapDecoded);
                }
            }
        }).start();

    }

    private static int getMaxTextureSize() {
        EGL10 egl = (EGL10) EGLContext.getEGL();
        EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);

        // Initialise
        int[] version = new int[2];
        egl.eglInitialize(display, version);

        // Query total number of configurations
        int[] totalConfigurations = new int[1];
        egl.eglGetConfigs(display, null, 0, totalConfigurations);

        // Query actual list configurations
        EGLConfig[] configurationsList = new EGLConfig[totalConfigurations[0]];
        egl.eglGetConfigs(display, configurationsList, totalConfigurations[0], totalConfigurations);

        int[] textureSize = new int[1];
        int maximumTextureSize = 0;

        // Iterate through all the configurations to located the maximum texture size
        for (int i = 0; i < totalConfigurations[0]; i++) {
            // Only need to check for width since opengl textures are always squared
            egl.eglGetConfigAttrib(display, configurationsList[i], EGL10.EGL_MAX_PBUFFER_WIDTH, textureSize);

            // Keep track of the maximum texture size
            if (maximumTextureSize < textureSize[0]) {
                maximumTextureSize = textureSize[0];
            }
        }

        // Release
        egl.eglTerminate(display);

        return maximumTextureSize;

    }

    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
        mDisplayW = right - left;
        mDisplayH = bottom - top;

        if (getDrawable() != null && ((BitmapDrawable) getDrawable()).getBitmap() != null) {
            calculateProperties(((BitmapDrawable) getDrawable()).getBitmap());
        }
    }

    private void init() {

        mScaleGestureDetector = new ScaleGestureDetector(getContext(), this);

        mBaseMatrix = new Matrix();
        mDrawMatrix = new Matrix();
        mSupportMatrix = new Matrix();

        mLineWidth = (int) dipToPixels(LINE_WIDTH_IN_DP);
        mPaint = new Paint();

        // 表示第一個實線段長dashOnWidth,第一個虛線段長dashOffWidth
        mPath = new Path();

        mCropRect = new RectF();
        mBoundaryRect = new RectF();

        setScaleType(ScaleType.MATRIX);
        setClickable(true);
    }

    private float dipToPixels(float dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
                getResources().getDisplayMetrics());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        addOnLayoutChangeListener(this);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();

        removeOnLayoutChangeListener(this);
    }

    /**
     * 設定圖片的裁剪比例,比如3:4就是0.75
     *
     * @param ratio
     */
    public void setCropRatio(final float ratio) {
        if (mRatio == ratio) {
            return;
        }
        mRatio = ratio;

        //重新選擇比例後,恢復旋轉角度
        //setImageRotation(0);

        if (getDrawable() == null) {
            return;
        }

        calculateProperties(((BitmapDrawable) getDrawable()).getBitmap());

        postInvalidate();
    }

    public void setImageRotation(int rotation) {
        if (mRotation == rotation) {
            return;
        }

        mRotation = rotation;

        if (getDrawable() == null) {
            return;
        }

        calculateProperties(((BitmapDrawable) getDrawable()).getBitmap());

        postInvalidate();
    }

    public void setCropRectPadding(float padding) {
        RectFPadding = padding;
    }

    public void setImagePath(final String path) {
        //textUtils.isEmpty()判斷是否為空。
        if (TextUtils.isEmpty(path)) {
            return;
        }

        if (iBitmapLoading != null) {
            Toast.makeText(getContext(),"you Glid框架",Toast.LENGTH_LONG).show();
            iBitmapLoading.onLoadPrepare();
        }
        decodeImageForCropping(path, new IDecodeCallback() {
            @Override
            public void onDecoded(final int rotation, final Bitmap bitmap) {
                post(new Runnable() {
                    @Override
                    public void run() {
                        mRotation = rotation;
                        setImageBitmap(bitmap);

                        if (iBitmapLoading != null) {
                            iBitmapLoading.onLoadFinish();
                        }
                    }
                });
            }
        });
    }

    @Override
    public void setImageBitmap(Bitmap bm) {
        calculateProperties(bm);
        super.setImageBitmap(bm);
    }

    public void setBitmapLoadingListener(onBitmapLoadListener iBitmapLoad) {
        iBitmapLoading = iBitmapLoad;
    }

    protected void calculateProperties(Bitmap bm) {
        mSupportMatrix.reset();
        mBaseMatrix.reset();

        int widthSize = mDisplayW;
        int heightSize = mDisplayH;

        generateCropRect(widthSize, heightSize);

        mImageWidth = bm.getWidth();
        mImageHeight = bm.getHeight();

        final boolean rotated = isImageRotated();

        final int bitmapWidth = rotated ? mImageHeight : mImageWidth;
        final int bitmapHeight = rotated ? mImageWidth : mImageHeight;

        mBoundaryRect.set(0, 0, bitmapWidth, bitmapHeight);

        final float widthScale = mCropRect.width() / bitmapWidth;
        final float heightScale = mCropRect.height() / bitmapHeight;

        final float scale = Math.max(widthScale, heightScale);
        final float scaledHeight = scale * bitmapHeight;
        final float scaledWidth = scale * bitmapWidth;

        // 移動到中心點
        final int translateX = (int) (mCropRect.left + mCropRect.width() / 2 - scaledWidth / 2);
        final int translateY = (int) (mCropRect.top + mCropRect.height() / 2 - scaledHeight / 2);

        mBaseMatrix.setScale(scale, scale);
        mBaseMatrix.postTranslate(translateX, translateY);

        mBaseMatrix.mapRect(mBoundaryRect);

        setImageMatrix(getDrawMatrix());
    }

    private boolean isImageRotated() {
        return ((mRotation % 360) == 90) || ((mRotation % 360) == 270);
    }

    private void generateCropRect(int boundaryWidth, int boundaryHeight) {
        //RectFPadding是適應產品需求,給裁剪框mCropRect設定一下padding -- chenglin 2016年04月18日
        boundaryWidth = boundaryWidth - (int)(RectFPadding * 2);
        boundaryHeight = boundaryHeight - (int)(RectFPadding * 2);

        int left;
        int top;
        int right;
        int bottom;

        boolean vertical;
        // 寬/高 大於比例的話,說明裁剪框是“豎直”的
        vertical = (float) boundaryWidth / boundaryHeight > mRatio;

        final int rectH = (int) (boundaryWidth / mRatio);
        final int rectW = (int) (boundaryHeight * mRatio);
        if (vertical) {
            left = (boundaryWidth - rectW) / 2;
            top = 0;
            right = (boundaryWidth + rectW) / 2;
            bottom = boundaryHeight;
        } else {
            left = 0;
            top = (boundaryHeight - rectH) / 2;
            right = boundaryWidth;
            bottom = (boundaryHeight + rectH) / 2;
        }

        //RectFPadding是適應產品需求,給裁剪框mCropRect設定一下padding -- chenglin 2016年04月18日
        mCropRect.set(left + RectFPadding, top + RectFPadding, right + RectFPadding, bottom + RectFPadding);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (!mEnableDrawCropWidget) {
            return;
        }

        if (getDrawable() == null) {
            return;
        }

        mPaint.reset();

        mPaint.setAntiAlias(true);
        mPaint.setColor(LINE_COLOR);
        mPaint.setStrokeWidth(mLineWidth);
        mPaint.setStyle(Paint.Style.STROKE);

        mPath.reset();

        // 上
        mPath.moveTo(mCropRect.left, mCropRect.top);
        mPath.lineTo(mCropRect.right, mCropRect.top);
        // 左
        mPath.moveTo(mCropRect.left, mCropRect.top);
        mPath.lineTo(mCropRect.left, mCropRect.bottom);
        // 右
        mPath.moveTo(mCropRect.right, mCropRect.top);
        mPath.lineTo(mCropRect.right, mCropRect.bottom);
        // 下
        mPath.moveTo(mCropRect.right, mCropRect.bottom);
        mPath.lineTo(mCropRect.left, mCropRect.bottom);

        canvas.drawPath(mPath, mPaint);

        // 繪製外部陰影部分
        mPaint.reset();
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.parseColor("#B3333333"));
        mPaint.setStyle(Paint.Style.FILL);

        //下面的四個矩形是裝飾性的,就是裁剪框四周的四個陰影
        final int lineOffset = mLineWidth;
        if (mCropRect.top > 0) {
            canvas.drawRect(0, 0, getMeasuredWidth(), mCropRect.top - lineOffset, mPaint);
        }

        if (mCropRect.left > 0) {
            canvas.drawRect(mCropRect.top - lineOffset - RectFPadding, RectFPadding - lineOffset, mCropRect.left - lineOffset, mCropRect.bottom + lineOffset, mPaint);
        }

        if (mCropRect.right < getMeasuredWidth()) {
            canvas.drawRect(mCropRect.right + lineOffset, mCropRect.top - lineOffset, getMeasuredWidth(), mCropRect.bottom + lineOffset, mPaint);
        }

        if (mCropRect.bottom < getMeasuredHeight()) {
            canvas.drawRect(0, mCropRect.bottom + lineOffset, getMeasuredWidth(), getMeasuredHeight(), mPaint);
        }
    }

    public boolean onTouchEvent(MotionEvent ev) {

        if (ev.getPointerCount() > 1) {
            mOperation = OPERATION.SCALE;
            return mScaleGestureDetector.onTouchEvent(ev);
        }

        final int action = ev.getActionMasked();
        final int x = (int) ev.getX();
        final int y = (int) ev.getY();

        switch (action) {
            case MotionEvent.ACTION_DOWN:

                mOperation = OPERATION.DRAG;

                mLastX = x;
                mLastY = y;

                break;
            case MotionEvent.ACTION_MOVE:

                if (mOperation == OPERATION.DRAG) {
                    int deltaX = x - mLastX;
                    int deltaY = y - mLastY;

                    RectF boundary = getDrawBoundary(getDrawMatrix());

                    if (boundary.left + deltaX > mCropRect.left) {
                        deltaX = (int) (mCropRect.left - boundary.left);
                    } else if (boundary.right + deltaX < mCropRect.right) {
                        deltaX = (int) (mCropRect.right - boundary.right);
                    }

                    if (boundary.top + deltaY > mCropRect.top) {
                        deltaY = (int) (mCropRect.top - boundary.top);
                    } else if (boundary.bottom + deltaY < mCropRect.bottom) {
                        deltaY = (int) (mCropRect.bottom - boundary.bottom);
                    }

                    mSupportMatrix.postTranslate(deltaX, deltaY);

                    setImageMatrix(getDrawMatrix());

                    mLastX = x;
                    mLastY = y;
                }
                break;
            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_UP:
                mLastX = 0;
                mLastY = 0;
                mOperation = null;
                break;
        }

        return super.onTouchEvent(ev);
    }

    public Bitmap getOriginBitmap() {
        BitmapDrawable drawable = (BitmapDrawable) getDrawable();
        return drawable == null ? null : drawable.getBitmap();
    }

    /**
     * 儲存圖片為bitmap
     */
    public Bitmap saveCrop() throws OutOfMemoryError {
        if (getDrawable() == null) {
            return null;
        }

        Bitmap origin = getOriginBitmap();
        Matrix drawMatrix = getDrawMatrix();

        // 反轉一下矩陣
        Matrix inverse = new Matrix();
        drawMatrix.invert(inverse);

        // 把裁剪框對應到原圖上去
        RectF cropMapped = new RectF();
        inverse.mapRect(cropMapped, mCropRect);

        clampCropRect(cropMapped, origin.getWidth(), origin.getHeight());

        // 如果產生了旋轉,需要一個旋轉矩陣
        Matrix rotationM = new Matrix();
        if (mRotation % 360 != 0) {
            rotationM.postRotate(mRotation, origin.getWidth() / 2, origin.getHeight() / 2);
        }

        Bitmap cropped = Bitmap.createBitmap(
                origin,
                (int) cropMapped.left,
                (int) cropMapped.top,
                (int) cropMapped.width(),
                (int) cropMapped.height(),
                rotationM, true
        );


        return cropped;
    }

    private void clampCropRect(RectF cropRect, int borderW, int borderH) {
        if (cropRect.left < 0) {
            cropRect.left = 0;
        }

        if (cropRect.top < 0) {
            cropRect.top = 0;
        }

        if (cropRect.right > borderW) {
            cropRect.right = borderW;
        }

        if (cropRect.bottom > borderH) {
            cropRect.bottom = borderH;
        }
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float scale = detector.getScaleFactor();

        if (scale == 1.0f) {
            return true;
        }

        final float currentScale = getScale(mSupportMatrix);

        final float centerX = detector.getFocusX();
        final float centerY = detector.getFocusY();

        if ((currentScale <= 1.0f && scale < 1.0f)
                || (currentScale >= mScaleMax && scale > 1.0f)) {
            return true;
        }

        if (currentScale * scale < 1.0f) {
            scale = 1.0f / currentScale;
        } else if (currentScale * scale > mScaleMax) {
            scale = mScaleMax / currentScale;
        }

        mSupportMatrix.postScale(scale, scale, centerX, centerY);

        RectF boundary = getDrawBoundary(getDrawMatrix());

        float translateX = 0;
        if (boundary.left > mCropRect.left) {
            translateX = mCropRect.left - boundary.left;
        } else if (boundary.right < mCropRect.right) {
            translateX = mCropRect.right - boundary.right;
        }

        Log.d("scale", "x==>" + translateX);

        float translateY = 0;
        if (boundary.top > mCropRect.top) {
            translateY = mCropRect.top - boundary.top;
        } else if (boundary.bottom < mCropRect.bottom) {
            translateY = mCropRect.bottom - boundary.bottom;
        }

        mSupportMatrix.postTranslate(translateX, translateY);

        setImageMatrix(getDrawMatrix());

        return true;
    }

    protected Matrix getDrawMatrix() {
        mDrawMatrix.reset();

        if (mRotation % 360 != 0) {
            final boolean rotated = isImageRotated();

            final int width = rotated ? mImageHeight : mImageWidth;
            final int height = rotated ? mImageWidth : mImageHeight;

            mDrawMatrix.postRotate(mRotation, mImageWidth / 2, mImageHeight / 2);

            if (rotated) {
                final int translateX = (width - mImageWidth) / 2;
                final int translateY = (height - mImageHeight) / 2;

                mDrawMatrix.postTranslate(translateX, translateY);
            }
        }

        mDrawMatrix.postConcat(mBaseMatrix);

        mDrawMatrix.postConcat(mSupportMatrix);

        return mDrawMatrix;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        final float currentScale = getScale(mSupportMatrix);
        if (currentScale < 1.0f) {
            Log.e("onScaleEnd", "currentScale==>" + currentScale);

            RectF boundary = getDrawBoundary(getDrawMatrix());
            post(new AnimatedZoomRunnable(currentScale, 1.0f, boundary.centerX(), boundary.centerY()));
        }
    }

    protected RectF getDrawBoundary(Matrix matrix) {
        Drawable drawable = getDrawable();
        if (drawable == null) {
            return mBoundaryRect;
        }

        final int bitmapWidth = drawable.getIntrinsicWidth();
        final int bitmapHeight = drawable.getIntrinsicHeight();

        mBoundaryRect.set(0, 0, bitmapWidth, bitmapHeight);

        matrix.mapRect(mBoundaryRect);

        return mBoundaryRect;
    }

    public float getScale(Matrix matrix) {
        return (float) Math.sqrt((float) Math.pow(getValue(matrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(matrix, Matrix.MSKEW_Y), 2));
    }

    /**
     * Helper method that 'unpacks' a Matrix and returns the required value
     *
     * @param matrix     - Matrix to unpack
     * @param whichValue - Which value from Matrix.M* to return
     * @return float - returned value
     */
    private float getValue(Matrix matrix, int whichValue) {
        matrix.getValues(mMatrixValues);
        return mMatrixValues[whichValue];
    }

    public void enableDrawCropWidget(boolean enable) {
        mEnableDrawCropWidget = enable;
    }

    protected enum OPERATION {
        DRAG, SCALE
    }

    public enum Type {
        CENTER_CROP, CENTER_INSIDE
    }

    public interface IDecodeCallback {
        void onDecoded(final int rotation, final Bitmap bitmap);
    }

    //setImagePath這個方法耗時,需要顯示進度條,這個是監聽
    public