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