首页 > 代码库 > Android图片裁剪——自定义裁剪工具

Android图片裁剪——自定义裁剪工具

上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具。

老习惯,文章开始前还是先把我参考的资料贴出来。您愿意节省点时间看别人的更好的就直接从下面链接跳走~愿意看看我怎么做的那就先谢谢了!

GitHub上老外做的一个非常棒的demo,代码也很漂亮

android自定义view实现裁剪图片功能,不使用系统的

第一个链接代码写的太好了,不过很多我用不上,也不需要那么麻烦的文件结构;第二个代码比较简单,但有些地方还是有借鉴意义的。

下面是我的代码,时间紧,就先不写太详细了:

注意几点:

我是在平板上做的测试,代码可能不适应手机,这个很好改..

我写这个是当作从外部传递一个绝对路径进来再做裁剪的,所以图省事儿就在设备里/sdcard/下放了一张图片,从mainActivity传进去..所以运行前自己先随便整个图片进去..或者自己改代码..

这个做起来不难.就是特麻烦.我也是粗略做做..UI什么的都没去搞..有空再弄吧..好歹也是个能用用的工具..

做好了会传到github上..到时候发链接..

 

activity_main.xml

 1 <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" 2     xmlns:tools="http://schemas.android.com/tools" 3     android:id="@+id/scrollview" 4     android:layout_width="wrap_content" 5     android:layout_height="wrap_content" > 6  7     <LinearLayout 8         android:layout_width="match_parent" 9         android:layout_height="wrap_content"10         android:layout_marginLeft="30dp"11         android:layout_marginRight="30dp"12         android:orientation="vertical" >13 14         <Button15             android:id="@+id/btn_crop"16             android:layout_width="wrap_content"17             android:layout_height="wrap_content"18             android:text="Crop" />19 20         <Button21             android:id="@+id/btn_cancel"22             android:layout_width="wrap_content"23             android:layout_height="wrap_content"24             android:text="Cancel" />25 26         <com.example.crop_image_my.MyCropView27             android:id="@+id/myCropView"28             android:layout_width="900dp"29             android:layout_height="600dp"30             android:src="@drawable/violetsky" />31 32         <ImageView33             android:id="@+id/croppedImageView"34             android:layout_width="wrap_content"35             android:layout_height="wrap_content" />36     </LinearLayout>37 38 </ScrollView>

mainActivity.java

 1 package com.example.crop_image_my; 2  3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.graphics.Bitmap; 6 import android.view.Menu; 7 import android.view.MotionEvent; 8 import android.view.View; 9 import android.view.View.OnClickListener;10 import android.widget.Button;11 import android.widget.ImageView;12 import android.widget.ScrollView;13 14 public class MainActivity extends Activity implements OnClickListener {15 16     private MyCropView myCropView;17     private Button btnCrop;18     private Button btnCancel;19     private ImageView croppedImageView;20     private ScrollView sv;21 22     // 假设从图片选择器传递来的图片路径如下23     private static final String CROP_IMAGE_PATH = "/sdcard/crop.jpg";24 25     @Override26     protected void onCreate(Bundle savedInstanceState) {27         super.onCreate(savedInstanceState);28         setContentView(R.layout.activity_main);29 30         myCropView = (MyCropView) findViewById(R.id.myCropView);31         btnCrop = (Button) findViewById(R.id.btn_crop);32         btnCancel = (Button) findViewById(R.id.btn_cancel);33         croppedImageView = (ImageView) findViewById(R.id.croppedImageView);34         sv = (ScrollView) findViewById(R.id.scrollview);35 36         myCropView.setBmpPath(CROP_IMAGE_PATH);37         btnCrop.setOnClickListener(this);38         btnCancel.setOnClickListener(this);39 40         sv.setOnTouchListener(new View.OnTouchListener() {41 42             @Override43             public boolean onTouch(View v, MotionEvent event) {44                 // TODO Auto-generated method stub45                 myCropView.getParent().requestDisallowInterceptTouchEvent(false);46                 return false;47             }48         });49     }50 51     @Override52     public void onClick(View v) {53         // TODO Auto-generated method stub54         switch (v.getId()) {55         case R.id.btn_crop:56             Bitmap croppedImage = myCropView.getCroppedImage();57 58             croppedImageView.setImageBitmap(croppedImage);59             break;60         case R.id.btn_cancel:61 62             break;63         default:64             break;65         }66     }67 68     @Override69     public boolean onCreateOptionsMenu(Menu menu) {70         // Inflate the menu; this adds items to the action bar if it is present.71         getMenuInflater().inflate(R.menu.activity_main, menu);72         return true;73     }74 75 }

MyCropView.java

  1 package com.example.crop_image_my;  2   3 import android.content.Context;  4 import android.drm.DrmStore.RightsStatus;  5 import android.graphics.Bitmap;  6 import android.graphics.BitmapFactory;  7 import android.graphics.Canvas;  8 import android.graphics.Color;  9 import android.graphics.Paint; 10 import android.graphics.Paint.Style; 11 import android.graphics.PointF; 12 import android.graphics.RectF; 13 import android.util.AttributeSet; 14 import android.view.MotionEvent; 15 import android.view.View; 16 import android.widget.Toast; 17  18 public class MyCropView extends View { 19  20     // Private Constants /////////////////////////////////////////////////////// 21     private static final float BMP_LEFT = 0f; 22     private static final float BMP_TOP = 20f; 23  24     private static final float DEFAULT_BORDER_RECT_WIDTH = 200f; 25     private static final float DEFAULT_BORDER_RECT_HEIGHT = 200f; 26  27     private static final int POS_TOP_LEFT = 0; 28     private static final int POS_TOP_RIGHT = 1; 29     private static final int POS_BOTTOM_LEFT = 2; 30     private static final int POS_BOTTOM_RIGHT = 3; 31     private static final int POS_TOP = 4; 32     private static final int POS_BOTTOM = 5; 33     private static final int POS_LEFT = 6; 34     private static final int POS_RIGHT = 7; 35     private static final int POS_CENTER = 8; 36  37     // this constant would be best to use event number 38     private static final float BORDER_LINE_WIDTH = 6f; 39     private static final float BORDER_CORNER_LENGTH = 30f; 40     private static final float TOUCH_FIELD = 10f; 41  42     // Member Variables //////////////////////////////////////////////////////// 43     private String mBmpPath; 44     private Bitmap mBmpToCrop; 45     private RectF mBmpBound; 46     private Paint mBmpPaint; 47  48     private Paint mBorderPaint;// 裁剪区边框 49     private Paint mGuidelinePaint; 50     private Paint mCornerPaint; 51     private Paint mBgPaint; 52  53     private RectF mDefaultBorderBound; 54     private RectF mBorderBound; 55  56     private PointF mLastPoint = new PointF(); 57  58     private float mBorderWidth; 59     private float mBorderHeight; 60  61     private int touchPos; 62  63     // Constructors //////////////////////////////////////////////////////////// 64     public MyCropView(Context context) { 65         super(context); 66         // TODO Auto-generated constructor stub 67         init(context); 68     } 69  70     public MyCropView(Context context, AttributeSet attrs) { 71         super(context, attrs); 72         init(context); 73     } 74  75     // View Methods //////////////////////////////////////////////////////////// 76     @Override 77     protected void onSizeChanged(int w, int h, int oldw, int oldh) { 78         // TODO Auto-generated method stub 79         // super.onSizeChanged(w, h, oldw, oldh); 80     } 81  82     @Override 83     protected void onDraw(Canvas canvas) { 84         // TODO Auto-generated method stub 85         // super.onDraw(canvas); 86         if (mBmpPath != null) { 87             canvas.drawBitmap(mBmpToCrop, null, mBmpBound, mBmpPaint); 88             canvas.drawRect(mBorderBound.left, mBorderBound.top, mBorderBound.right, mBorderBound.bottom, mBorderPaint); 89             drawGuidlines(canvas); 90             drawBackground(canvas); 91         } 92     } 93  94     @Override 95     public boolean onTouchEvent(MotionEvent event) { 96         // TODO Auto-generated method stub 97         // super.onTouchEvent(event); 98         switch (event.getAction()) { 99         case MotionEvent.ACTION_DOWN:100             setLastPosition(event);101             getParent().requestDisallowInterceptTouchEvent(true);102             // onActionDown(event.getX(), event.getY());103             touchPos = detectTouchPosition(event.getX(), event.getY());104             break;105         case MotionEvent.ACTION_MOVE:106             onActionMove(event.getX(), event.getY());107             setLastPosition(event);108             break;109         case MotionEvent.ACTION_UP:110             break;111         }112 113         return true;114     }115 116     // Public Methods //////////////////////////////////////////////////////////117     public String getBmpPath() {118         return mBmpPath;119     }120 121     public void setBmpPath(String picPath) {122         this.mBmpPath = picPath;123         setBmp();124     }125 126     public Bitmap getCroppedImage() {127         // 先不考虑图片被压缩的情况 就当作现在的图片就是1:1的128 129         return Bitmap.createBitmap(mBmpToCrop, (int) mBorderBound.left, (int) mBorderBound.top, (int) mBorderWidth,130                 (int) mBorderHeight);131     }132 133     // Private Methods /////////////////////////////////////////////////////////134     private void init(Context context) {135 136         mBmpPaint = new Paint();137         // 以下是抗锯齿138         mBmpPaint.setAntiAlias(true);// 防止边缘的锯齿139         mBmpPaint.setFilterBitmap(true);// 对位图进行滤波处理140 141         mBorderPaint = new Paint();142         mBorderPaint.setStyle(Style.STROKE);143         mBorderPaint.setColor(Color.parseColor("#AAFFFFFF"));144         mBorderPaint.setStrokeWidth(BORDER_LINE_WIDTH);145 146         mGuidelinePaint = new Paint();147         mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF"));148         mGuidelinePaint.setStrokeWidth(1f);149 150         mCornerPaint = new Paint();151 152         mBgPaint = new Paint();153         mBgPaint.setColor(Color.parseColor("#B0000000"));154         mBgPaint.setAlpha(150);155 156     }157 158     private void setBmp() {159         mBmpToCrop = BitmapFactory.decodeFile(mBmpPath);160 161         mBmpBound = new RectF();162         mBmpBound.left = BMP_LEFT;163         mBmpBound.top = BMP_TOP;164         mBmpBound.right = mBmpBound.left + mBmpToCrop.getWidth();165         mBmpBound.bottom = mBmpBound.top + mBmpToCrop.getHeight();166 167         // 使裁剪框一开始出现在图片的中心位置168         mDefaultBorderBound = new RectF();169         mDefaultBorderBound.left = (mBmpBound.left + mBmpBound.right - DEFAULT_BORDER_RECT_WIDTH) / 2;170         mDefaultBorderBound.top = (mBmpBound.top + mBmpBound.bottom - DEFAULT_BORDER_RECT_HEIGHT) / 2;171         mDefaultBorderBound.right = mDefaultBorderBound.left + DEFAULT_BORDER_RECT_WIDTH;172         mDefaultBorderBound.bottom = mDefaultBorderBound.top + DEFAULT_BORDER_RECT_HEIGHT;173 174         mBorderBound = new RectF();175         mBorderBound.left = mDefaultBorderBound.left;176         mBorderBound.top = mDefaultBorderBound.top;177         mBorderBound.right = mDefaultBorderBound.right;178         mBorderBound.bottom = mDefaultBorderBound.bottom;179 180         getBorderEdgeLength();181         invalidate();182     }183 184     private void drawBackground(Canvas canvas) {185 186         /*-187           -------------------------------------188           |                top                |189           -------------------------------------190           |      |                    |       |<——————————mBmpBound191           |      |                    |       |192           | left |                    | right |193           |      |                    |       |194           |      |                  <─┼───────┼────mBorderBound195           -------------------------------------196           |              bottom               |197           -------------------------------------198          */199 200         // Draw "top", "bottom", "left", then "right" quadrants.201         // because the border line width is larger than 1f, in order to draw a complete border rect ,202         // i have to change zhe rect coordinate to draw203         float delta = BORDER_LINE_WIDTH / 2;204         float left = mBorderBound.left - delta;205         float top = mBorderBound.top - delta;206         float right = mBorderBound.right + delta;207         float bottom = mBorderBound.bottom + delta;208 209         // -------------------------------------------------------------------------------移动到上下两端会多出来阴影210         canvas.drawRect(mBmpBound.left, mBmpBound.top, mBmpBound.right, top, mBgPaint);211         canvas.drawRect(mBmpBound.left, bottom, mBmpBound.right, mBmpBound.bottom, mBgPaint);212         canvas.drawRect(mBmpBound.left, top, left, bottom, mBgPaint);213         canvas.drawRect(right, top, mBmpBound.right, bottom, mBgPaint);214     }215 216     // 画裁剪区域中间的参考线217     private void drawGuidlines(Canvas canvas) {218         // Draw vertical guidelines.219         final float oneThirdCropWidth = mBorderBound.width() / 3;220 221         final float x1 = mBorderBound.left + oneThirdCropWidth;222         canvas.drawLine(x1, mBorderBound.top, x1, mBorderBound.bottom, mGuidelinePaint);223         final float x2 = mBorderBound.right - oneThirdCropWidth;224         canvas.drawLine(x2, mBorderBound.top, x2, mBorderBound.bottom, mGuidelinePaint);225 226         // Draw horizontal guidelines.227         final float oneThirdCropHeight = mBorderBound.height() / 3;228 229         final float y1 = mBorderBound.top + oneThirdCropHeight;230         canvas.drawLine(mBorderBound.left, y1, mBorderBound.right, y1, mGuidelinePaint);231         final float y2 = mBorderBound.bottom - oneThirdCropHeight;232         canvas.drawLine(mBorderBound.left, y2, mBorderBound.right, y2, mGuidelinePaint);233     }234 235     private void onActionDown(float x, float y) {236 237     }238 239     private void onActionMove(float x, float y) {240         float deltaX = x - mLastPoint.x;241         float deltaY = y - mLastPoint.y;242         // 这里先不考虑裁剪框放最大的情况243         switch (touchPos) {244         case POS_CENTER:245             mBorderBound.left += deltaX;246             // fix border position247             if (mBorderBound.left < mBmpBound.left)248                 mBorderBound.left = mBmpBound.left;249             if (mBorderBound.left > mBmpBound.right - mBorderWidth)250                 mBorderBound.left = mBmpBound.right - mBorderWidth;251 252             mBorderBound.top += deltaY;253             if (mBorderBound.top < mBmpBound.top)254                 mBorderBound.top = mBmpBound.top;255 256             if (mBorderBound.top > mBmpBound.bottom - mBorderHeight)257                 mBorderBound.top = mBmpBound.bottom - mBorderHeight;258 259             mBorderBound.right = mBorderBound.left + mBorderWidth;260             mBorderBound.bottom = mBorderBound.top + mBorderHeight;261 262             break;263 264         case POS_TOP:265             resetTop(deltaY);266             break;267         case POS_BOTTOM:268             resetBottom(deltaY);269             break;270         case POS_LEFT:271             resetLeft(deltaX);272             break;273         case POS_RIGHT:274             resetRight(deltaX);275             break;276         case POS_TOP_LEFT:277             resetTop(deltaY);278             resetLeft(deltaX);279             break;280         case POS_TOP_RIGHT:281             resetTop(deltaY);282             resetRight(deltaX);283             break;284         case POS_BOTTOM_LEFT:285             resetBottom(deltaY);286             resetLeft(deltaX);287             break;288         case POS_BOTTOM_RIGHT:289             resetBottom(deltaY);290             resetRight(deltaX);291             break;292         default:293 294             break;295         }296         invalidate();297     }298 299     private void onActionUp(float x, float y) {300 301     }302 303     private int detectTouchPosition(float x, float y) {304         if (x > mBorderBound.left + TOUCH_FIELD && x < mBorderBound.right - TOUCH_FIELD305                 && y > mBorderBound.top + TOUCH_FIELD && y < mBorderBound.bottom - TOUCH_FIELD)306             return POS_CENTER;307 308         if (x > mBorderBound.left + BORDER_CORNER_LENGTH && x < mBorderBound.right - BORDER_CORNER_LENGTH) {309             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + TOUCH_FIELD)310                 return POS_TOP;311             if (y > mBorderBound.bottom - TOUCH_FIELD && y < mBorderBound.bottom + TOUCH_FIELD)312                 return POS_BOTTOM;313         }314 315         if (y > mBorderBound.top + BORDER_CORNER_LENGTH && y < mBorderBound.bottom - BORDER_CORNER_LENGTH) {316             if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + TOUCH_FIELD)317                 return POS_LEFT;318             if (x > mBorderBound.right - TOUCH_FIELD && x < mBorderBound.right + TOUCH_FIELD)319                 return POS_RIGHT;320         }321 322         // 前面的逻辑已经排除掉了几种情况 所以后面的 ┏ ┓ ┗ ┛ 边角就按照所占区域的方形来判断就可以了323         if (x > mBorderBound.left - TOUCH_FIELD && x < mBorderBound.left + BORDER_CORNER_LENGTH) {324             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)325                 return POS_TOP_LEFT;326             if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)327                 return POS_BOTTOM_LEFT;328         }329 330         if (x > mBorderBound.right - BORDER_CORNER_LENGTH && x < mBorderBound.right + TOUCH_FIELD) {331             if (y > mBorderBound.top - TOUCH_FIELD && y < mBorderBound.top + BORDER_CORNER_LENGTH)332                 return POS_TOP_RIGHT;333             if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom + TOUCH_FIELD)334                 return POS_BOTTOM_RIGHT;335         }336 337         return -1;338     }339 340     private void setLastPosition(MotionEvent event) {341         mLastPoint.x = event.getX();342         mLastPoint.y = event.getY();343     }344 345     private void getBorderEdgeLength() {346         mBorderWidth = mBorderBound.width();347         mBorderHeight = mBorderBound.height();348     }349 350     private void getBorderEdgeWidth() {351         mBorderWidth = mBorderBound.width();352     }353 354     private void getBorderEdgeHeight() {355         mBorderHeight = mBorderBound.height();356     }357 358     private void resetLeft(float delta) {359         mBorderBound.left += delta;360 361         getBorderEdgeWidth();362         fixBorderLeft();363     }364 365     private void resetTop(float delta) {366         mBorderBound.top += delta;367         getBorderEdgeHeight();368         fixBorderTop();369     }370 371     private void resetRight(float delta) {372         mBorderBound.right += delta;373 374         getBorderEdgeWidth();375         fixBorderRight();376 377     }378 379     private void resetBottom(float delta) {380         mBorderBound.bottom += delta;381 382         getBorderEdgeHeight();383         fixBorderBottom();384     }385 386     private void fixBorderLeft() {387         // fix left388         if (mBorderBound.left < mBmpBound.left)389             mBorderBound.left = mBmpBound.left;390         if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)391             mBorderBound.left = mBorderBound.right - 2 * BORDER_CORNER_LENGTH;392     }393 394     private void fixBorderTop() {395         // fix top396         if (mBorderBound.top < mBmpBound.top)397             mBorderBound.top = mBmpBound.top;398         if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)399             mBorderBound.top = mBorderBound.bottom - 2 * BORDER_CORNER_LENGTH;400     }401 402     private void fixBorderRight() {403         // fix right404         if (mBorderBound.right > mBmpBound.right)405             mBorderBound.right = mBmpBound.right;406         if (mBorderWidth < 2 * BORDER_CORNER_LENGTH)407             mBorderBound.right = mBorderBound.left + 2 * BORDER_CORNER_LENGTH;408     }409 410     private void fixBorderBottom() {411         // fix bottom412         if (mBorderBound.bottom > mBmpBound.bottom)413             mBorderBound.bottom = mBmpBound.bottom;414         if (mBorderHeight < 2 * BORDER_CORNER_LENGTH)415             mBorderBound.bottom = mBorderBound.top + 2 * BORDER_CORNER_LENGTH;416     }417 }

 

Android图片裁剪——自定义裁剪工具