上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具。
原文地址请保留http://www.cnblogs.com/rossoneri/p/3988405.html
老习惯,文章开始前还是先把我参考的资料贴出来。您愿意节省点时间看别人的更好的就直接从下面链接跳走~愿意看看我怎么做的那就先谢谢了!
第一个链接代码写的太好了,不过很多我用不上,也不需要那么麻烦的文件结构;第二个代码比较简单,但有些地方还是有借鉴意义的。
下面是我的代码,时间紧,就先不写太详细了:
注意几点:
我是在平板上做的测试,代码可能不适应手机,这个很好改..
我写这个是当作从外部传递一个绝对路径进来再做裁剪的,所以图省事儿就在设备里/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 packagecom.example.crop_image_my; 2 3 importandroid.os.Bundle; 4 importandroid.app.Activity; 5 importandroid.graphics.Bitmap; 6 importandroid.view.Menu; 7 importandroid.view.MotionEvent; 8 importandroid.view.View; 9 importandroid.view.View.OnClickListener; 10 importandroid.widget.Button; 11 importandroid.widget.ImageView; 12 importandroid.widget.ScrollView; 13 14 public class MainActivity extends Activity implementsOnClickListener { 15 16 privateMyCropView myCropView; 17 privateButton btnCrop; 18 privateButton btnCancel; 19 privateImageView croppedImageView; 20 privateScrollView sv; 21 22 //假设从图片选择器传递来的图片路径如下 23 private static final String CROP_IMAGE_PATH = "/sdcard/crop.jpg"; 24 25 @Override 26 protected voidonCreate(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(newView.OnTouchListener() { 41 42 @Override 43 public booleanonTouch(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 voidonClick(View v) { 53 //TODO Auto-generated method stub 54 switch(v.getId()) { 55 caseR.id.btn_crop: 56 Bitmap croppedImage =myCropView.getCroppedImage(); 57 58 croppedImageView.setImageBitmap(croppedImage); 59 break; 60 caseR.id.btn_cancel: 61 62 break; 63 default: 64 break; 65 } 66 } 67 68 @Override 69 public booleanonCreateOptionsMenu(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 packagecom.example.crop_image_my; 2 3 importandroid.content.Context; 4 importandroid.drm.DrmStore.RightsStatus; 5 importandroid.graphics.Bitmap; 6 importandroid.graphics.BitmapFactory; 7 importandroid.graphics.Canvas; 8 importandroid.graphics.Color; 9 importandroid.graphics.Paint; 10 importandroid.graphics.Paint.Style; 11 importandroid.graphics.PointF; 12 importandroid.graphics.RectF; 13 importandroid.util.AttributeSet; 14 importandroid.view.MotionEvent; 15 importandroid.view.View; 16 importandroid.widget.Toast; 17 18 public class MyCropView extendsView { 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 privateString mBmpPath; 44 privateBitmap mBmpToCrop; 45 privateRectF mBmpBound; 46 privatePaint mBmpPaint; 47 48 private Paint mBorderPaint;//裁剪区边框 49 privatePaint mGuidelinePaint; 50 privatePaint mCornerPaint; 51 privatePaint mBgPaint; 52 53 privateRectF mDefaultBorderBound; 54 privateRectF mBorderBound; 55 56 private PointF mLastPoint = newPointF(); 57 58 private floatmBorderWidth; 59 private floatmBorderHeight; 60 61 private inttouchPos; 62 63 //Constructors //////////////////////////////////////////////////////////// 64 publicMyCropView(Context context) { 65 super(context); 66 //TODO Auto-generated constructor stub 67 init(context); 68 } 69 70 publicMyCropView(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, intoldh) { 78 //TODO Auto-generated method stub 79 //super.onSizeChanged(w, h, oldw, oldh); 80 } 81 82 @Override 83 protected voidonDraw(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 booleanonTouchEvent(MotionEvent event) { 96 //TODO Auto-generated method stub 97 //super.onTouchEvent(event); 98 switch(event.getAction()) { 99 caseMotionEvent.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 caseMotionEvent.ACTION_MOVE: 106 onActionMove(event.getX(), event.getY()); 107 setLastPosition(event); 108 break; 109 caseMotionEvent.ACTION_UP: 110 break; 111 } 112 113 return true; 114 } 115 116 //Public Methods ////////////////////////////////////////////////////////// 117 publicString getBmpPath() { 118 returnmBmpPath; 119 } 120 121 public voidsetBmpPath(String picPath) { 122 this.mBmpPath =picPath; 123 setBmp(); 124 } 125 126 publicBitmap 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 voidinit(Context context) { 135 136 mBmpPaint = newPaint(); 137 //以下是抗锯齿 138 mBmpPaint.setAntiAlias(true);//防止边缘的锯齿 139 mBmpPaint.setFilterBitmap(true);//对位图进行滤波处理 140 141 mBorderPaint = newPaint(); 142 mBorderPaint.setStyle(Style.STROKE); 143 mBorderPaint.setColor(Color.parseColor("#AAFFFFFF")); 144 mBorderPaint.setStrokeWidth(BORDER_LINE_WIDTH); 145 146 mGuidelinePaint = newPaint(); 147 mGuidelinePaint.setColor(Color.parseColor("#AAFFFFFF")); 148 mGuidelinePaint.setStrokeWidth(1f); 149 150 mCornerPaint = newPaint(); 151 152 mBgPaint = newPaint(); 153 mBgPaint.setColor(Color.parseColor("#B0000000")); 154 mBgPaint.setAlpha(150); 155 156 } 157 158 private voidsetBmp() { 159 mBmpToCrop =BitmapFactory.decodeFile(mBmpPath); 160 161 mBmpBound = newRectF(); 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 = newRectF(); 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 = newRectF(); 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 voiddrawBackground(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 voiddrawGuidlines(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, floaty) { 236 237 } 238 239 private void onActionMove(float x, floaty) { 240 float deltaX = x -mLastPoint.x; 241 float deltaY = y -mLastPoint.y; 242 //这里先不考虑裁剪框放最大的情况 243 switch(touchPos) { 244 casePOS_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 casePOS_TOP: 265 resetTop(deltaY); 266 break; 267 casePOS_BOTTOM: 268 resetBottom(deltaY); 269 break; 270 casePOS_LEFT: 271 resetLeft(deltaX); 272 break; 273 casePOS_RIGHT: 274 resetRight(deltaX); 275 break; 276 casePOS_TOP_LEFT: 277 resetTop(deltaY); 278 resetLeft(deltaX); 279 break; 280 casePOS_TOP_RIGHT: 281 resetTop(deltaY); 282 resetRight(deltaX); 283 break; 284 casePOS_BOTTOM_LEFT: 285 resetBottom(deltaY); 286 resetLeft(deltaX); 287 break; 288 casePOS_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, floaty) { 300 301 } 302 303 private int detectTouchPosition(float x, floaty) { 304 if (x > mBorderBound.left + TOUCH_FIELD && x < mBorderBound.right -TOUCH_FIELD 305 && y > mBorderBound.top + TOUCH_FIELD && y < mBorderBound.bottom -TOUCH_FIELD) 306 returnPOS_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 returnPOS_TOP; 311 if (y > mBorderBound.bottom - TOUCH_FIELD && y < mBorderBound.bottom +TOUCH_FIELD) 312 returnPOS_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 returnPOS_LEFT; 318 if (x > mBorderBound.right - TOUCH_FIELD && x < mBorderBound.right +TOUCH_FIELD) 319 returnPOS_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 returnPOS_TOP_LEFT; 326 if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom +TOUCH_FIELD) 327 returnPOS_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 returnPOS_TOP_RIGHT; 333 if (y > mBorderBound.bottom - BORDER_CORNER_LENGTH && y < mBorderBound.bottom +TOUCH_FIELD) 334 returnPOS_BOTTOM_RIGHT; 335 } 336 337 return -1; 338 } 339 340 private voidsetLastPosition(MotionEvent event) { 341 mLastPoint.x =event.getX(); 342 mLastPoint.y =event.getY(); 343 } 344 345 private voidgetBorderEdgeLength() { 346 mBorderWidth =mBorderBound.width(); 347 mBorderHeight =mBorderBound.height(); 348 } 349 350 private voidgetBorderEdgeWidth() { 351 mBorderWidth =mBorderBound.width(); 352 } 353 354 private voidgetBorderEdgeHeight() { 355 mBorderHeight =mBorderBound.height(); 356 } 357 358 private void resetLeft(floatdelta) { 359 mBorderBound.left +=delta; 360 361 getBorderEdgeWidth(); 362 fixBorderLeft(); 363 } 364 365 private void resetTop(floatdelta) { 366 mBorderBound.top +=delta; 367 getBorderEdgeHeight(); 368 fixBorderTop(); 369 } 370 371 private void resetRight(floatdelta) { 372 mBorderBound.right +=delta; 373 374 getBorderEdgeWidth(); 375 fixBorderRight(); 376 377 } 378 379 private void resetBottom(floatdelta) { 380 mBorderBound.bottom +=delta; 381 382 getBorderEdgeHeight(); 383 fixBorderBottom(); 384 } 385 386 private voidfixBorderLeft() { 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 voidfixBorderTop() { 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 voidfixBorderRight() { 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 voidfixBorderBottom() { 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(newView.OnTouchListener() { 2 3 @Override 4 public booleanonTouch(View v, MotionEvent event) { 5 //TODO Auto-generated method stub 6 myCropView.getParent().requestDisallowInterceptTouchEvent(false); 7 return false; 8 } 9 });
1 caseMotionEvent.ACTION_DOWN: 2 setLastPosition(event); 3 getParent().requestDisallowInterceptTouchEvent(true);
这个代码就是为了让 移动或变化裁剪窗口 和 scrollview不冲突
具体内容会在后面更新博客~~ 文章入口 (其实想写好多东西。。就是没空。。慢慢来)