[Android] 图片裁剪总结——自定义裁剪工具

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

原文地址请保留http://www.cnblogs.com/rossoneri/p/3988405.html

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

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         <Button
15             android:id="@+id/btn_crop"
16             android:layout_width="wrap_content"
17             android:layout_height="wrap_content"
18             android:text="Crop" />
19 
20         <Button
21             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.MyCropView
27             android:id="@+id/myCropView"
28             android:layout_width="900dp"
29             android:layout_height="600dp"
30             android:src="@drawable/violetsky" />
31 
32         <ImageView
33             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     @Override
26     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             @Override
43             public boolean onTouch(View v, MotionEvent event) {
44                 // TODO Auto-generated method stub
45                 myCropView.getParent().requestDisallowInterceptTouchEvent(false);
46                 return false;
47             }
48         });
49     }
50 
51     @Override
52     public void onClick(View v) {
53         // TODO Auto-generated method stub
54         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     @Override
69     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           |      |                    |       |<——————————mBmpBound
191           |      |                    |       |
192           | left |                    | right |
193           |      |                    |       |
194           |      |                  <─┼───────┼────mBorderBound
195           -------------------------------------
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 draw
203         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 position
247             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_FIELD
305                 && 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 left
388         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 top
396         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 right
404         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 bottom
412         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 }

 


补两张截图先:

 

界面神丑,无所谓啦,功能是有了  代码其实很简单了,一看就懂,但是有一些知识点,比如说下面这两段:

1 sv.setOnTouchListener(new View.OnTouchListener() {
2 
3             @Override
4             public boolean onTouch(View v, MotionEvent event) {
5                 // TODO Auto-generated method stub
6                 myCropView.getParent().requestDisallowInterceptTouchEvent(false);
7                 return false;
8             }
9         });
1 case MotionEvent.ACTION_DOWN:
2             setLastPosition(event);
3             getParent().requestDisallowInterceptTouchEvent(true);

这个代码就是为了让  移动或变化裁剪窗口  和  scrollview不冲突 

具体内容会在后面更新博客~~  文章入口   (其实想写好多东西。。就是没空。。慢慢来)

posted @ 2014-09-25 17:56  Wossoneri  阅读(12002)  评论(2编辑  收藏  举报