Android 可变裁剪区及缩放裁剪图片
来源:互联网 发布:mac照片无法访问iphone 编辑:程序博客网 时间:2024/05/24 15:38
大多图片裁剪大多两种操作:改变裁剪区图片不能缩放、裁剪区固定图片缩放,两种方法都可以裁剪到不同图片,本次介绍的是可变裁剪区同时能缩放图片,同时记录自己的开发项目过程。
裁剪视图一共三个view,最底层的缩放CilpImageView ,中间是可变裁剪区CilpBorderView,还有最顶层的CilpTouchView。监听CilpTouchView的OnTouch事件,通过判断down手势是否落在拉伸裁剪区的按钮内分发给CilpBorderView或者CilpImageView 。
Options类是默认裁剪配置。
CilpBorderView类:
public class CilpBorderView extends View { private int borderColor = Color.parseColor("#FFFFFF"); private int outSideColor = Color.parseColor("#20000000"); private float borderWidth = 2; private float lineWidth = 1; private Rect[] rects=new Rect[2]; private Paint cutPaint; private Paint outSidePaint; private RectF cilpRectF; //图片右坐标 private int iconRight=0; //图片左坐标 private int iconLeft=0; private Bitmap bitmap; private int iconOffset; private int width=0; private int height=0; private int verLine1; private int verLine2; private Options options; private static final int TOP_ICON_ACTION=1; private static final int BOTTOM_ICON_ACTION=2; private int action=-1; private float actionY; public CilpBorderView(Context context) { super(context); initView(); } public CilpBorderView(Context context, AttributeSet attrs) { super(context, attrs); initView(); } public CilpBorderView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(); } private void initView() { cutPaint = new Paint(); cutPaint.setColor(borderColor); cutPaint.setStrokeWidth(borderWidth); cutPaint.setStyle(Paint.Style.STROKE); outSidePaint=new Paint(); outSidePaint.setAntiAlias(true); outSidePaint.setColor(outSideColor); outSidePaint.setStyle(Paint.Style.FILL); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_crop_drag_y); iconOffset = bitmap.getWidth()/2; options = new Options(); } @Override protected void onDraw(Canvas canvas) { if (cilpRectF == null) { cilpRectF = new RectF(options.paddingWidth, (getHeight() - options.cilpHeight) / 2, getWidth() - options.paddingWidth, (getHeight() + options.cilpHeight) / 2); verLine1 = (int) (cilpRectF.left + cilpRectF.width() / 3); verLine2 = (int) (cilpRectF.left + cilpRectF.width() * 2 / 3); } if (width == 0) width = getWidth(); if (height == 0) height = getHeight(); canvas.save(); drawLine(canvas); drawRound(canvas); drawIcon(canvas); canvas.restore(); super.onDraw(canvas); } private void drawIcon(Canvas canvas) { if (iconLeft == 0 && iconRight == 0) { iconLeft = width / 2 - iconOffset; iconRight=width / 2 + iconOffset; } canvas.drawBitmap(bitmap, iconLeft, cilpRectF.top-iconOffset, null); canvas.drawBitmap(bitmap, iconLeft, cilpRectF.bottom-iconOffset, null); Rect rect=new Rect(iconLeft-options.iconClick, (int)(cilpRectF.top-iconOffset)-options.iconClick,iconRight+options.iconClick, (int)(cilpRectF.top+iconOffset)+options.iconClick); rects[0]=rect; rect=new Rect(iconLeft-options.iconClick,(int)(cilpRectF.bottom-iconOffset)-options.iconClick,iconRight+options.iconClick, (int)(cilpRectF.bottom+iconOffset)+options.iconClick); rects[1]=rect; } private void drawLine(Canvas canvas){ cutPaint.setStrokeWidth(lineWidth); float p = cilpRectF.top + cilpRectF.height() / 3; //横线 canvas.drawLine(options.paddingWidth, p, width-options.paddingWidth, p, cutPaint); p = cilpRectF.top + cilpRectF.height() * 2 / 3; canvas.drawLine(options.paddingWidth, p, width-options.paddingWidth, p, cutPaint); //竖线 canvas.drawLine(verLine1, cilpRectF.top, verLine1, cilpRectF.bottom, cutPaint); canvas.drawLine(verLine2, cilpRectF.top, verLine2, cilpRectF.bottom, cutPaint); } private void drawRound(Canvas canvas) { //绘制边框 cutPaint.setStrokeWidth(borderWidth); canvas.drawRect(cilpRectF, cutPaint); //绘制外区域 //左中框 canvas.drawRect(0, cilpRectF.top, options.paddingWidth, cilpRectF.bottom, outSidePaint); //上框 canvas.drawRect(0, 0, width, cilpRectF.top, outSidePaint); //右中框 canvas.drawRect(cilpRectF.right, cilpRectF.top, width, cilpRectF.bottom, outSidePaint); //下框 canvas.drawRect(0, cilpRectF.bottom, width, height, outSidePaint); } public void setOptions(Options options) { this.options = options; } /** * 根据手势做拉伸 */ public boolean iconOntouch(MotionEvent event,RectF imgRect){ switch (event.getAction()){ case MotionEvent.ACTION_DOWN: if (rects[0].contains((int)event.getX(),(int)event.getY())) { action=TOP_ICON_ACTION; } if (rects[1].contains((int)event.getX(),(int)event.getY())) { action=BOTTOM_ICON_ACTION; } actionY=event.getY(); break; case MotionEvent.ACTION_MOVE: float y=actionY-event.getY(); switch (action){ case TOP_ICON_ACTION: cilpRectF.top=cilpRectF.top-y; break; case BOTTOM_ICON_ACTION: cilpRectF.bottom=cilpRectF.bottom-y; break; } checkBroad(imgRect); actionY=event.getY(); invalidate(); break; case MotionEvent.ACTION_UP: action=-1; break; } return true; } /** * @description 边界校验 * @param imgRect */ private void checkBroad(RectF imgRect) { if ((cilpRectF.bottom-cilpRectF.top) < options.min_height){//高度少于最小高度 switch (action) { case TOP_ICON_ACTION: cilpRectF.top=cilpRectF.bottom - options.min_height; break; case BOTTOM_ICON_ACTION: cilpRectF.bottom=cilpRectF.top + options.min_height; break; } } else if ((cilpRectF.bottom-cilpRectF.top) > options.max_height){//高度大于最大高度 switch (action) { case TOP_ICON_ACTION: cilpRectF.top=cilpRectF.bottom - options.max_height; break; case BOTTOM_ICON_ACTION: cilpRectF.bottom=cilpRectF.top + options.max_height; break; } } if (cilpRectF.top < options.paddingHeight) { cilpRectF.top = options.paddingHeight; } if (cilpRectF.bottom > height-options.paddingHeight){ cilpRectF.bottom = height-options.paddingHeight; } if ( cilpRectF.top < imgRect.top) { cilpRectF.top = imgRect.top; } if ( cilpRectF.bottom > imgRect.bottom) { cilpRectF.bottom = imgRect.bottom; } } //判断手势down事件是否落在拉伸按钮区域内 public boolean isIconClick(MotionEvent event){ if (rects[0].contains((int)event.getX(), (int)event.getY())) { System.out.println("点击顶部图标"); action=TOP_ICON_ACTION; return true; } if (rects[1].contains((int)event.getX(), (int)event.getY())) { System.out.println("点击底部图标"); action=BOTTOM_ICON_ACTION; return true; } actionY=event.getY(); return false; } public RectF getCilpRectF() { return cilpRectF; } public void setCilpRectF(RectF cilpRectF) { this.cilpRectF = cilpRectF; invalidate(); }
CilpImageView 类:
public class CilpImageView extends ImageView implements ScaleGestureDetector.OnScaleGestureListener,ViewTreeObserver.OnGlobalLayoutListener{ private static float SCALE_MID = 0.1f; /** * 初始化时的缩放比例,如果图片宽或高大于屏幕,此值将小0 */ private float initScale = 1.0f; /** * 缩放的手势检测 */ private ScaleGestureDetector scaleGestureDetector = null; private final Matrix scaleMatrix = new Matrix(); private final float[] matrixValues = new float[9]; /** * 用于双击缩放 */ private GestureDetector gestureDetector; //是否自动缩放任务 private boolean isAutoScale; private float mLastX; private float mLastY; private float centerX; private float centerY; //图片原始宽高 private int drawableW; private int drawableH; private boolean isCanDrag; private int lastPointerCount; private RectF borderRectF; private CilpRectFChangeListener cilpRectFChangeListener; private Options options; public CilpImageView(Context context) { super(context); initView(context); } public CilpImageView(Context context, AttributeSet attrs) { super(context, attrs); initView(context); } public CilpImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } private void initView(Context context) { options = new Options(); setScaleType(ScaleType.MATRIX); setBackgroundColor(Color.BLACK); gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) {//双击 if (isAutoScale) return true; float x=e.getX(); float y=e.getY(); if (getScale() != initScale) { CilpImageView.this.postDelayed(new AutoScaleRunnable(initScale, x, y),16); } else { CilpImageView.this.postDelayed(new AutoScaleRunnable(initScale * 2, x, y),16); } isAutoScale=true; return true; } }); scaleGestureDetector=new ScaleGestureDetector(context,this); } /** * 获得当前的缩放比例 * * @return */ public final float getScale() { scaleMatrix.getValues(matrixValues); return matrixValues[Matrix.MSCALE_X]; } /** * 边界校验 */ private void checkBorder(){ RectF rectF=getMatrixRectF(); float deltaX = 0; float deltaY = 0; int width=getWidth(); // 如果宽或高大于屏幕,则控制范围,这里的0.001是因为精度丢失会产生问题,但是误差一般很小,所以直接加了一个0.01 if (rectF.width() + 0.01 >= width - 2 * options.paddingWidth) { if (rectF.left > options.paddingWidth) { deltaX = -rectF.left + options.paddingWidth; } if (rectF.right < width - options.paddingWidth){ deltaX = width - options.paddingWidth - rectF.right; } } if (rectF.height() +0.01 >= borderRectF.height()){ if (rectF.top > borderRectF.top){ deltaY = -rectF.top + borderRectF.top; } if (rectF.bottom < borderRectF.bottom){ deltaY = borderRectF.bottom-rectF.bottom; } } scaleMatrix.postTranslate(deltaX,deltaY); } /** * 根据当前图片的Matrix获得图片的范围 */ public RectF getMatrixRectF() { Matrix matrix = scaleMatrix; RectF rect = new RectF(); Drawable d = getDrawable(); if (null != d) { rect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(rect); } return rect; } @Override public boolean onScale(ScaleGestureDetector detector) { if (getDrawable() == null) { return true; } float scaleFactor=detector.getScaleFactor(); scaleMatrix.postScale(scaleFactor, scaleFactor, detector.getFocusX(), detector.getFocusY()); checkBorder(); setImageMatrix(scaleMatrix); setCilpRectFIfNeed(); return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { } /** * 根据手势做缩放 */ public boolean onImageTouch(MotionEvent event, RectF borderRect) { this.borderRectF=borderRect; if (gestureDetector.onTouchEvent(event)){ return true; } scaleGestureDetector.onTouchEvent(event); float x = 0, y = 0; // 拿到触摸点的个数 final int pointerCount = event.getPointerCount(); // 得到多个触摸点的x与y for (int i = 0; i < pointerCount; i++) { x += event.getX(i); y += event.getY(i); } x = x / pointerCount; y = y / pointerCount; /** * 每当触摸点发生变化时,重置mLasX , mLastY */ if (pointerCount != lastPointerCount) { isCanDrag = false; mLastX = x; mLastY = y; } lastPointerCount = pointerCount; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: resetMidScale(); mLastX = x; mLastY = y; break; case MotionEvent.ACTION_MOVE: float dx = x - mLastX; float dy = y - mLastY; if (!isCanDrag) { isCanDrag = isCanDrag(dx, dy); } if (isCanDrag) { if (getDrawable() != null) { RectF rectF = getMatrixRectF(); // 如果宽度小于屏幕宽度,则禁止左右移动 if (rectF.width() <= borderRectF.width()) { dx = 0; } // 如果高度小于屏幕高度,则禁止上下移动 if (rectF.height() <= borderRectF.height()) { dy = 0; } scaleMatrix.postTranslate(dx, dy); checkBorder(); setImageMatrix(scaleMatrix); } } mLastX = x; mLastY = y; break; case MotionEvent.ACTION_UP: if (SCALE_MID>getScale()) { postDelayed( new AutoScaleRunnable(SCALE_MID, getWidth()/2, event.getY()), 1); } break; case MotionEvent.ACTION_CANCEL: lastPointerCount = 0; break; } return true; } /** * 是否是拖动行为 */ private boolean isCanDrag(float dx, float dy) { return Math.sqrt((dx * dx) + (dy * dy)) >= 0; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); getViewTreeObserver().addOnGlobalLayoutListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { getViewTreeObserver().removeOnGlobalLayoutListener(this); } else { getViewTreeObserver().removeGlobalOnLayoutListener(this); } } @Override public void onGlobalLayout() { if (0!=getHeight()) { if (borderRectF==null) borderRectF=new RectF(options.paddingWidth,(getHeight() - options.cilpHeight)/2, getWidth()-options.paddingWidth,(getHeight() + options.cilpHeight)/2); int height=getHeight(); Drawable d = getDrawable(); if (d == null) return; int width = getWidth(); // 拿到图片的宽和高 drawableW = d.getIntrinsicWidth(); drawableH = d.getIntrinsicHeight(); float scale ; scale= borderRectF.width() /drawableW; scaleMatrix.reset(); initScale = scale; SCALE_MID=initScale; centerX=(width - (borderRectF.width())) / 2; centerY=(height - drawableH*scale) / 2; scaleMatrix.postTranslate(centerX, centerY); scaleMatrix.postScale(scale, scale,centerX,centerY); // 图片移动至屏幕中心 setImageMatrix(scaleMatrix); setCilpRectFIfNeed(); } } //校验裁剪边界 private void setCilpRectFIfNeed(){ RectF rectF=getMatrixRectF(); if (cilpRectFChangeListener!=null && (rectF.height() < borderRectF.height())){ borderRectF.top = rectF.top; borderRectF.bottom = rectF.bottom; rectF.right = borderRectF.right; rectF.left = borderRectF.left; cilpRectFChangeListener.onChange(rectF); } } /** * 计算最小缩放值 */ private void resetMidScale(){ int distanceW = (int) (drawableW - borderRectF.width()); int distanceH = (int) (drawableH - borderRectF.height()); if (distanceH < distanceW) {//按高度算最小缩放比例 SCALE_MID= borderRectF.height() /drawableH; } else { SCALE_MID= borderRectF.width() /drawableW; } } /** * 剪切图片,返回剪切后的bitmap对象 * * @return */ public Bitmap clip(RectF rectF) { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); draw(canvas); bitmap=Bitmap.createBitmap(bitmap, (int)rectF.left, (int)rectF.top,(int)rectF.width(),(int)rectF.height()); return bitmap; } /** * 自动缩放任务 */ private class AutoScaleRunnable implements Runnable{ static final float BIGGER=1.07f; static final float SMALLER=0.93f; private float tarScale; private float tmpScale; /** * 缩放的中中心 */ private float x; private float y; AutoScaleRunnable(float tarScale, float x, float y) { this.tarScale=tarScale; this.x=x; this.y=y; if (getScale() < tarScale){ tmpScale=BIGGER; } else { tmpScale=SMALLER; } } @Override public void run() { scaleMatrix.postScale(tmpScale, tmpScale, x, y); checkBorder(); setImageMatrix(scaleMatrix); final float currentScale = getScale(); // 如果值在合法范围内,继续缩放 if ( ((tmpScale > 1f) && (currentScale < tarScale)) || ((tmpScale < 1f) && (tarScale <currentScale))){ final float nextScale = tmpScale * currentScale;//下次的缩放比例 if (((tmpScale > 1f) && (currentScale <tarScale))){//放大 if (nextScale > tarScale){ tmpScale = tarScale / currentScale; } } if (((tmpScale < 1f) && (currentScale > tarScale))){//缩小 if (nextScale < tarScale){ tmpScale = tarScale / currentScale; } } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { CilpImageView.this.postOnAnimation(this); }else{ CilpImageView.this.postDelayed(this,1); } } else { setCilpRectFIfNeed(); isAutoScale=false; } } } public void setCilpRectFChangeListener(CilpRectFChangeListener cilpRectFChangeListener) { this.cilpRectFChangeListener = cilpRectFChangeListener; } public void setOptions(Options options) { this.options = options; } public interface CilpRectFChangeListener{ void onChange(RectF rectF); }}
CilpTouchView类:
public class CilpTouchView extends View implements View.OnTouchListener{ private CilpImageView imageView; private CilpBorderView borderView; private boolean iconClick; private RectF changeRect; public CilpTouchView(Context context, CilpBorderView borderView, final CilpImageView imageView) { super(context); if (borderView == null || imageView == null) { throw new NullPointerException("view is null"); } this.borderView=borderView; this.imageView=imageView; imageView.setCilpRectFChangeListener(new CilpImageView.CilpRectFChangeListener() { @Override public void onChange(RectF rectF) { CilpTouchView.this.borderView.setCilpRectF(rectF); } }); setOnTouchListener(this); } @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (borderView.isIconClick(event)){ iconClick = true; changeRect= imageView.getMatrixRectF(); } else { iconClick = false; changeRect=borderView.getCilpRectF(); } } if (iconClick){ borderView.iconOntouch(event, changeRect); } else { imageView.onImageTouch(event, changeRect); } if (event.getAction() == MotionEvent.ACTION_UP) { iconClick=false; } return true; }}
效果图:
例子下载:https://github.com/gdflk/VariableCilpImageView
0 0
- Android 可变裁剪区及缩放裁剪图片
- android图片裁剪和缩放
- Android图片裁剪----移动、缩放图片进行裁剪
- 图片缩放裁剪
- iOS 图片裁剪 缩放
- 图片缩放裁剪
- nginx-图片裁剪缩放
- java 图片缩放、裁剪。
- surfaceview缩放裁剪图片
- 图片裁剪/旋转/缩放
- Android图片选择及裁剪
- Android 图片裁剪及保存
- 截屏,裁剪图片,缩放图片
- 图片缩放和裁剪 缩放和裁剪图片
- PHP图片裁剪、缩放函数
- C#缩放和裁剪图片
- C#缩放和裁剪图片
- 图片进行缩放裁剪等等
- 用yolo区分文字和模糊图像
- Python面向对象私有属性及案例详解
- Ubuntu14.04下配置svn
- Linux学习笔记
- malloc实现动态数组的创建,数组个数由用户输入
- Android 可变裁剪区及缩放裁剪图片
- python3操作windows剪贴板
- 数据结构实验之链表一:顺序建立链表
- React/React Native 的ES5 ES6写法对照表
- 【Openjudge2704】寻找平面上的极大点
- yum源介绍和安装
- 7.22 F uva 10870 Recurrences
- android adb 通信原理
- spring中的事件机制