上次弄完调用系统裁剪之后,我又试着做一个自定义的裁剪工具。
原文地址请保留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 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不冲突
具体内容会在后面更新博客~~ 文章入口 (其实想写好多东西。。就是没空。。慢慢来)