自定义View实现图片的缩放

     图片的缩放实现的方式有很多种,一般是通过两点之间距离的变化来实现图片的缩放,这样子体验不是太好,用起来不是很方便。这样做会好一点,当我们添加图片时会出现一个框把图片包围起来,框上面有两个按钮,一个是删除,一个是缩放和旋转功能按钮。当我们点击图片在屏幕托动时图片会跟着动,起到定位效果。当我们点击功能按钮时只能缩放和旋转。这样用起来就比较方便,不喜欢的图片还可以点击删除按钮删除掉。

     自定义View代码如下:

public class SuperImageView extends View {
private final int id;
	
	private final Context context;
	private Bitmap bitmap_content;//内容图片
	private Bitmap bitmap_delete;//删除图标
	private Bitmap bitmap_success;//操作图标
	
	private final Paint paint;//画笔
	private int outer_w = 0;//外圈宽度扩大,用于放2个图标
	private int outer_h = 0;//外圈高度扩大,用于放2个图标
	private Point point;//中心点
	private float angle;//图片角度
	private float zoon;//缩放系数
	
	private Point np1;
	private Point np2;
	private Point np3;
	private Point np4;
	
	private int dx, dy;
	private int rotatedImageW;//图片旋转宽度
	private int rotatedImageH;//图片旋转高度
	private Point iconP1,iconP2;// 图标p1,p2 中心点坐标。
	
	private Matrix matrix = new Matrix();//矩阵处理
	
	private int viewW;
	private int viewH;
	private int viewL;
	private int viewT;
	
	private PointF pA = new PointF();
	private PointF pB = new PointF();
	private PointF mid = new PointF();
	
	//定义几种模式
	private static final int NONE = 0;//初始状态
	private static final int DRAG = 1;//拖动
	private static final int ZOOM = 2;//缩放
	private static final int ROTATE = 3;//旋转
	private static final int ZOOM_OR_ROTATE = 4; //缩放或旋转
	private int mode = NONE;//当前默认模式
	
	private RelativeLayout lin;
	
	public boolean status = SHOW;//显示还是隐藏的状态,默认为SHOW  
    private static final boolean SHOW = true;//显示边框和按钮
    private static final boolean HIDE = false;//隐藏边框和按钮
    
    
	/**
	 * 用于构造实例
	 * @param lin 放置装饰素材的容器
	 * @param context 上下文
	 * @param content_ID 图片资源
	 * @param delete_ID 删除按钮资源
	 * @param success_ID 操作按钮资源
	 */
	public SuperImageView(int id,RelativeLayout lin,Context context,Bitmap content_ID,int delete_ID,int success_ID) {
		super(context);
		this.id = id;
		this.lin = lin;
		this.context = context;
		this.paint = new Paint();//实例化画笔
		this.paint.setAntiAlias(true);//是否消除锯齿
		this.paint.setStyle(Style.STROKE);//凹陷效果
		init(content_ID,delete_ID,success_ID);
	}
	
	
	public SuperImageView(int id,Context context, AttributeSet attrs,Bitmap content_ID,int delete_ID,int success_ID) {
		super(context, attrs);
		this.id = id;
		this.context = context;
		this.paint = new Paint();
		this.paint.setAntiAlias(true);
		this.paint.setStyle(Style.STROKE);
		init(content_ID,delete_ID,success_ID);
	}

	
	public SuperImageView(int id,Context context, AttributeSet attrs, int defStyle,Bitmap content_ID,int delete_ID,int success_ID) {
		super(context, attrs, defStyle);
		this.id = id;
		this.context = context;
		this.paint = new Paint();
		this.paint.setAntiAlias(true);
		this.paint.setStyle(Style.STROKE);
		init(content_ID,delete_ID,success_ID);
	}
	
	/**
	 * 初始化
	 * @param content_ID 图片资源ID
	 * @param delete_ID 删除图标资源ID
	 * @param success_ID 操作图标资源ID
	 */
	private void init(Bitmap content_ID,int delete_ID,int success_ID){
		//加载图片资源
		bitmap_content = content_ID;
		bitmap_delete = BitmapFactory.decodeResource(getResources(), delete_ID);
		bitmap_success = BitmapFactory.decodeResource(getResources(), success_ID);
		outer_w = bitmap_delete.getWidth()/2;
		outer_h = bitmap_delete.getWidth()/2;
		setImageBitmap(bitmap_content, new Point(lin.getWidth()/2, lin.getHeight()/2), 0, 1.5f);//设置初始的位置、角度、大小
	}
	
	
	/**
	 * 设置图片样式
	 * @param bmp 图片资源
	 * @param pnt 中心点
	 * @param jd 角度
	 * @param sf 缩放的系数
	 */
	private void setImageBitmap(Bitmap bmp,Point pnt,float jd,float sf){
		bitmap_content = bmp;
		point = pnt;
		angle = jd;
		zoon = sf;
		//包围的边框线条
		surroundLine(0, 0, (int) (bitmap_content.getWidth() * zoon),(int) (bitmap_content.getHeight() * zoon), jd);
		
		matrix = new Matrix();
		matrix.setScale(sf, sf);//缩放
		// 设置旋转角度
		matrix.postRotate(jd % 360, bitmap_content.getWidth() * sf / 2,bitmap_content.getHeight() * sf / 2);
		//平移
		matrix.postTranslate(dx + outer_w, dy + outer_h);
		setViewWH(rotatedImageW, rotatedImageH, point.x - rotatedImageW / 2,point.y - rotatedImageH / 2);
	}

	
	public void setViewWH(int w, int h, int l, int t) {
		int nviewW = w + outer_w * 2;
		int nviewH = h + outer_h * 2;
		int nviewL = l - outer_w;
		int nviewT = t - outer_h;
		
		viewW = nviewW;
		viewH = nviewH;
		viewL = nviewL;
		viewT = nviewT;
		this.layout(viewL, viewT, viewL + viewW, viewT + viewH);// 定位,和大小
	}

	
	/**
	 * 画包围线条
	 * @param l 坐标
	 * @param t 坐标
	 * @param r 坐标
	 * @param b 坐标
	 * @param jd 角度
	 */
	private void surroundLine(int l,int t,int r,int b,float jd){
		Point p1 = new Point(l, t);
		Point p2 = new Point(r, t);
		Point p3 = new Point(r, b);
		Point p4 = new Point(l, b);
		Point tp = new Point((l + r) / 2, (t + b) / 2);
		np1 = roationPoint(tp, p1, jd);
		np2 = roationPoint(tp, p2, jd);
		np3 = roationPoint(tp, p3, jd);
		np4 = roationPoint(tp, p4, jd);
		int w = 0;
		int h = 0;
		int maxn = 0;
		int mixn = 0;
		maxn = np1.x;
		mixn = np1.x;
		if (np2.x > maxn) {
			maxn = np2.x;
		}
		if (np3.x > maxn) {
			maxn = np3.x;
		}
		if (np4.x > maxn) {
			maxn = np4.x;
		}
		
		if (np2.x < mixn) {
			mixn = np2.x;
		}
		if (np3.x < mixn) {
			mixn = np3.x;
		}
		if (np4.x < mixn) {
			mixn = np4.x;
		}
		w = maxn - mixn;

		maxn = np1.y;
		mixn = np1.y;
		if (np2.y > maxn) {
			maxn = np2.y;
		}
		if (np3.y > maxn) {
			maxn = np3.y;
		}
		if (np4.y > maxn) {
			maxn = np4.y;
		}

		if (np2.y < mixn) {
			mixn = np2.y;
		}
		if (np3.y < mixn) {
			mixn = np3.y;
		}
		if (np4.y < mixn) {
			mixn = np4.y;
		}

		h = maxn - mixn;
		//中心点位置计算。
		Point npc = intersects(np4, np2, np1, np3);
		dx = w / 2 - npc.x;
		dy = h / 2 - npc.y;
		np1.x = np1.x + dx + outer_w;
		np2.x = np2.x + dx + outer_w;
		np3.x = np3.x + dx + outer_w;
		np4.x = np4.x + dx + outer_w;

		np1.y = np1.y + dy + outer_h;
		np2.y = np2.y + dy + outer_h;
		np3.y = np3.y + dy + outer_h;
		np4.y = np4.y + dy + outer_h;
		rotatedImageW = w;
		rotatedImageH = h;
		iconP1 = np1;
		iconP2 = np3;
	}
	
	
	//2条直线交点(中心点)
	public Point intersects(Point sp3, Point sp4, Point sp1, Point sp2) {
		Point localPoint = new Point(0, 0);
		double num = (sp4.y - sp3.y) * (sp3.x - sp1.x) - (sp4.x - sp3.x)
				* (sp3.y - sp1.y);
		double denom = (sp4.y - sp3.y) * (sp2.x - sp1.x) - (sp4.x - sp3.x)
				* (sp2.y - sp1.y);
		localPoint.x = (int) (sp1.x + (sp2.x - sp1.x) * num / denom);
		localPoint.y = (int) (sp1.y + (sp2.y - sp1.y) * num / denom);
		return localPoint;
	}
	
	
	/**
	 * 设置旋转点参数
	 * @param target 旋转目标
	 * @param source 旋转原始点
	 * @param degree 旋转角度(这里是360度)
	 * @return
	 */
	public static Point roationPoint(Point target, Point source, float degree){
		source.x = source.x - target.x;
		source.y = source.y - target.y;
		double alpha = 0;
		double beta = 0;
		Point result = new Point();
		double dis = Math.sqrt(source.x * source.x + source.y * source.y);// 2点间,距离
		if (source.x == 0 && source.y == 0) {
			return target;
			// 第一象限
		} else if (source.x >= 0 && source.y >= 0) {
			// 计算与x正方向的夹角
			alpha = Math.asin(source.y / dis);
			// 第二象限
		} else if (source.x < 0 && source.y >= 0) {
			// 计算与x正方向的夹角
			alpha = Math.asin(Math.abs(source.x) / dis);
			alpha = alpha + Math.PI / 2;
			// 第三象限
		} else if (source.x < 0 && source.y < 0) {
			// 计算与x正方向的夹角
			alpha = Math.asin(Math.abs(source.y) / dis);
			alpha = alpha + Math.PI;
		} else if (source.x >= 0 && source.y < 0) {
			// 计算与x正方向的夹角
			alpha = Math.asin(source.x / dis);
			alpha = alpha + Math.PI * 3 / 2;
		}
		// 弧度换算成角度
		alpha = radianToDegree(alpha);
		beta = alpha + degree;
		// 角度转弧度
		beta = degreeToRadian(beta);
		result.x = (int) Math.round(dis * Math.cos(beta));
		result.y = (int) Math.round(dis * Math.sin(beta));
		result.x += target.x;
		result.y += target.y;
		return result;
	}
	
	
	/**
	 * 弧度换算成角度
	 * @param radian 弧度
	 * @return
	 */
	public static double radianToDegree(double radian) {
		return radian * 180 / Math.PI;
	}
	
	
	/**
	 * 角度换算成弧度
	 * @param degree 角度
	 * @return
	 */
	public static double degreeToRadian(double degree) {
		return degree * Math.PI / 180;
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		this.paint.setARGB(200, 138, 43, 226);//设置对象的颜色
		this.paint.setStrokeWidth(1);//设置空心线的宽度,画笔的粗细
		this.paint.setAntiAlias(true);//抗锯齿
		this.paint.setFilterBitmap(true);//过滤
		this.paint.setDither(true);//防抖动
		this.paint.setColor(Color.WHITE);//设置边框线条的颜色
		DrawFilter df = new PaintFlagsDrawFilter(0,Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
		canvas.setDrawFilter(df);
		if(status == SHOW){
			//画包围图的框
			canvas.drawLine(np1.x, np1.y, np2.x, np2.y, paint);
			canvas.drawLine(np2.x, np2.y, np3.x, np3.y, paint);
			canvas.drawLine(np3.x, np3.y, np4.x, np4.y, paint);
			canvas.drawLine(np4.x, np4.y, np1.x, np1.y, paint);
		}
		//画内容图
		canvas.drawBitmap(bitmap_content, matrix, paint);
		if(status == SHOW){
			//画2个功能图标
			canvas.drawBitmap(this.bitmap_delete, iconP1.x - outer_w, iconP1.y - outer_h, paint);
			canvas.drawBitmap(this.bitmap_success, iconP2.x - outer_w, iconP2.y - outer_h, paint);
		}
		setViewWH(rotatedImageW, rotatedImageH, point.x - rotatedImageW / 2,point.y - rotatedImageH / 2);
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction() & MotionEvent.ACTION_MASK) {
		//用户开始触摸
		case MotionEvent.ACTION_DOWN:
			pA.set(event.getX() + viewL, event.getY() + viewT);
			int result = isactiondownicon((int) event.getX(), (int) event.getY());
			if (result == 2) {
				//表示点中移动或旋转
				if(status==HIDE){
					break;
				}
				mode = ZOOM_OR_ROTATE;
			}else if(result == 1){
				//表示点中删除
				if(status==HIDE){
					break;
				}
				lin.removeView(this);
			}else {
				//表示点中边框
				mode = DRAG;
				this.bringToFront();//把当前View置顶
			}
			//处理隐藏除当前对象以外的其它对象的边框和按钮
			List<SuperImageView> list = SuperImageViewSingleMode.getInstance().getSuperImageList();
			for (int i = 0; i < list.size(); i++) {
				if(list.get(i).id!=this.id){
					list.get(i).status = false;
					list.get(i).invalidate();
				}else{
					list.get(i).status = true;
					list.get(i).invalidate();
				}
			}
			break;
		//一个非主要的手指按下
		case MotionEvent.ACTION_POINTER_DOWN:
			break;
		//表示用户抬起了手指
		case MotionEvent.ACTION_UP:
			break;
		//表示一个非主要的手指抬起
		case MotionEvent.ACTION_POINTER_UP:
			mode = NONE;
			break;
		//表示用户在移动(或其他)
		case MotionEvent.ACTION_MOVE:
			if(status==HIDE){
				break;
			}
			if (mode == ZOOM_OR_ROTATE){
				//进行放大缩小
				float sf = 1.5f;
				pB.set(event.getX() + viewL, event.getY() + viewT);
				float realL = (float) Math.sqrt((float) (bitmap_content.getWidth()
						* bitmap_content.getWidth() + bitmap_content.getHeight()
						* bitmap_content.getHeight()) / 4);
				float newL = (float) Math.sqrt((pB.x - (float) point.x)
						* (pB.x - (float) point.x) + (pB.y - (float) point.y)
						* (pB.y - (float) point.y));
				sf = newL / realL;
				// 角度
				double a = spacing(pA.x, pA.y, (float) point.x,(float) point.y);
				double b = spacing(pB.x, pB.y, pA.x, pA.y);
				double c = spacing(pB.x, pB.y, (float) point.x,(float) point.y);
				double cosB = (a * a + c * c - b * b) / (2 * a * c);
				if (cosB > 1) {
					//浮点运算的时候 cosB有可能大于1
					cosB = 1f;
				}
				double angleB = Math.acos(cosB);
				float newjd = (float) (angleB / Math.PI * 180);
				
				float p1x = pA.x - (float) point.x;
				float p2x = pB.x - (float) point.x;

				float p1y = pA.y - (float) point.y;
				float p2y = pB.y - (float) point.y;
				//正反向
				if (p1x == 0) {
					if (p2x > 0 && p1y >= 0 && p2y >= 0) {
						//由第4-》第3
						newjd = -newjd;
					} else if (p2x < 0 && p1y < 0 && p2y < 0) {
						//由第2-》第1
						newjd = -newjd;
					}
				} else if (p2x == 0) {
					if (p1x < 0 && p1y >= 0 && p2y >= 0) {
						//由第4-》第3
						newjd = -newjd;
					} else if (p1x > 0 && p1y < 0 && p2y < 0) {
						//由第2-》第1
						newjd = -newjd;
					}
				} else if (p1x != 0 && p2x != 0 && p1y / p1x < p2y / p2x) {
					if (p1x < 0 && p2x > 0 && p1y >= 0 && p2y >= 0) {
						//由第4-》第3
						newjd = -newjd;
					} else if (p2x < 0 && p1x > 0 && p1y < 0 && p2y < 0) {
						//由第2-》第1
						newjd = -newjd;
					} else {

					}
				} else {
					if (p2x < 0 && p1x > 0 && p1y >= 0 && p2y >= 0) {
						//由第3-》第4
					} else if (p2x > 0 && p1x < 0 && p1y < 0 && p2y < 0) {
						//由第1-》第2
					} else {
						newjd = -newjd;
					}
				}
				pA.x = pB.x;
				pA.y = pB.y;
				if (sf <= 0.5) {
					sf = 0.5f;
				} else if (sf >= 3) {
					sf = 3f;
				}
				this.setImageBitmap(this.bitmap_content, point, angle + newjd, sf);
			}
			if (mode == DRAG) {
				pB.set(event.getX() + viewL, event.getY() + viewT);
				//修改中心点
				point.x += pB.x - pA.x;
				point.y += pB.y - pA.y;
				pA.x = pB.x;
				pA.y = pB.y;
				setCPoint(point);
			}
			break;
		}
		return true;
	}
	
	
	/**
	 * 是否点中2个图标
	 * @param x
	 * @param y
	 * @return 1点中delete、2点中移动或旋转、0没有点中
	 */
	public int isactiondownicon(int x, int y) {
		int xx = x;
		int yy = y;
		//勾股算法
		int kk1 = ((xx - iconP1.x) * (xx - iconP1.x) + (yy - iconP1.y)* (yy - iconP1.y));
		int kk2 = ((xx - iconP2.x) * (xx - iconP2.x) + (yy - iconP2.y)* (yy - iconP2.y));
		Log.e("===>","kk1:" + kk1 + "\nkk2:" + kk2 + "\nevent的x和y:" + xx + "|"+ yy +"\n功能图标1中心点.x:"+iconP1.x+"\n功能图标1中心点.y:"+iconP1.y+"\n功能图标2中心点.x:"+iconP2.x+"\n功能图标2中心点.y:"+iconP2.y+"\n外围圈宽度:"+outer_w);
		if (kk1 < outer_w * outer_w) {
			return 1;
		} else if (kk2 < outer_w * outer_w) {
			return 2;
		}
		return 0;
	}
	
	
	/**
	 * 计算两点之间的距离
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return
	 */
	private float spacing(float x1, float y1, float x2, float y2) {
		float x = x1 - x2;
		float y = y1 - y2;
		return FloatMath.sqrt(x * x + y * y);
	}
	
	
	public void setCPoint(Point c) {
		point = c;
		setViewWH(rotatedImageW, rotatedImageH, point.x - rotatedImageW / 2,point.y - rotatedImageH / 2);
	}
	
	
	/**
	 * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
	 * @param context
	 * @param dpValue
	 * @return
	 */
	public static int dip2px(Context context, float dpValue) {
		final float scale = context.getResources().getDisplayMetrics().density;
		return (int) (dpValue * scale + 0.5f);
	}
	
}

我们在activity中就可以使用这个View,代码如下:

public void setStampImage(Bitmap b, int c){
		Log.i("jjf", "b的值:" + b);
		Log.i("jjf", "c的值:" + c);
		stampImage = new SuperImageView(c, rootLayout, this, b, R.drawable.img_delete, R.drawable.img_move);
		singleMode.setSuperImageList(stampImage);
		rootLayout.addView(stampImage);
	}

其中singleMode.setSuperImageList(stampImage);是一个工具类 代码如下:

public class SuperImageViewSingleMode {
static private SuperImageViewSingleMode instance;
	
	private static List<SuperImageView> SuperImageList = new ArrayList<SuperImageView>();
	
	
	/**唯一实例**/
	static public SuperImageViewSingleMode getInstance() { 
		if(instance==null) { 
			instance=new SuperImageViewSingleMode(); 
		}
		return instance; 
	}

	/**获取所有控件**/
	public List<SuperImageView> getSuperImageList() {
		return SuperImageList;
	}

	/**保存控件**/
	public void setSuperImageList(SuperImageView superImageList) {
		SuperImageList.add(superImageList);}
	

}

删除所有控件的工具类 代码如下:

public class SuperImageClose {
	public static void getClose() {
		List<SuperImageView> list = SuperImageViewSingleMode.getInstance()
				.getSuperImageList();
		for (int i = 0; i < list.size(); i++) {
			list.get(i).status = false;
			list.get(i).invalidate();
		}
	}
}
效果如下:



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值