[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不冲突 

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

你可能感兴趣的:(android)