一、美图旋转缩放方案
最近遇到了对图像进行旋转缩放操作的需求,以往的双指缩放、双指旋转体验明显没有美图app的旋转缩放方式好:
所以按照这个方案来实现,实现效果如下:
二、实现思路及代码
Android的图像旋转缩放,首先想到的是Matrix:Android Matrix原理
原理比较复杂,总结一句话:Matrix是记录图形变换数据的矩阵
实现思路:
1.获取bitmap的原始尺寸,及图形的关键定位点
【Bitmap手动压缩下一篇讲】
//读取Bitmap的矩形区域**RectF(0,0,width,height)**
RectF photoRectSrc = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
//Bitmap的初始关键定位点
//0,1代表左上角点XY,2,3代表右上角点XY,4,5代表右下角点XY,6,7代表左下角点XY,8,9代表中心点XY
float[] photoCornersSrc = new float[10];
//读取Bitmap关键定位点
photoCornersSrc[0] = photoRectSrc.left;
photoCornersSrc[1] = photoRectSrc.top;
photoCornersSrc[2] = photoRectSrc.right;
photoCornersSrc[3] = photoRectSrc.top;
photoCornersSrc[4] = photoRectSrc.right;
photoCornersSrc[5] = photoRectSrc.bottom;
photoCornersSrc[6] = photoRectSrc.left;
photoCornersSrc[7] = photoRectSrc.bottom;
photoCornersSrc[8] = photoRectSrc.centerX();
photoCornersSrc[9] = photoRectSrc.centerY();
2.判断手势动作来改变Matrix的参数
判断点击点是否图像当前显示区域:
private boolean isInPhotoRect(Matrix matrix, RectF photoRectSrc,float[] downPoint) {
float[] invertPoint = new float[2];//逆变换后的点击点数组
Matrix invertMatrix = new Matrix();//当前Matrix矩阵的逆矩阵
matrix.invert(invertMatrix);//通过当前Matrix得到对应的逆矩阵数据
invertMatrix.mapPoints(invertPoint, downPoint);//通过逆矩阵变化得到逆变换后的点击点
return photoRectSrc.contains(invertPoint[0], invertPoint[1]);//判断逆变换后的点击点是否在图像初始RectF内
}
计算点击边角操作图标时的缩放倍数及旋转角度
private void onRotateAction(Matrix matrix,float[] photoCorners) {
//放大
//目前触摸点与图片显示中心距离
float a = (float) Math.sqrt(Math.pow(curX - photoCorners[8], 2) + Math.pow(curY - photoCorners[9], 2));
//目前上次旋转图标与图片显示中心距离
float b = (float) Math.sqrt(Math.pow(photoCorners[4] - photoCorners[0], 2) + Math.pow(photoCorners[5] - photoCorners[1], 2)) / 2;
//设置Matrix缩放参数
double photoLen = Math.sqrt(Math.pow(record.photoRectSrc.width(), 2) + Math.pow(record.photoRectSrc.height(), 2));
if (a >= photoLen / 2 * SCALE_MIN && a >= SCALE_MIN_LEN && a <= photoLen / 2 * SCALE_MAX) {
//这种计算方法可以保持旋转图标坐标与触摸点同步缩放
float scale = a / b;
record.matrix.postScale(scale, scale, photoCorners[8], photoCorners[9]);
}
//旋转
//根据移动坐标的变化构建两个向量,以便计算两个向量角度.
PointF preVector = new PointF();
PointF curVector = new PointF();
preVector.set(preX - photoCorners[8], preY - photoCorners[9]);//旋转后向量
curVector.set(curX - photoCorners[8], curY - photoCorners[9]);//旋转前向量
//计算向量长度
double preVectorLen = getVectorLength(preVector);
double curVectorLen = getVectorLength(curVector);
//计算两个向量的夹角.
double cosAlpha = (preVector.x * curVector.x + preVector.y * curVector.y)
/ (preVectorLen * curVectorLen);
//由于计算误差,可能会带来略大于1的cos,例如
if (cosAlpha > 1.0f) {
cosAlpha = 1.0f;
}
//本次的角度已经计算出来。
double dAngle = Math.acos(cosAlpha) * 180.0 / Math.PI;
// 判断顺时针和逆时针.
//判断方法其实很简单,这里的v1v2其实相差角度很小的。
//先转换成单位向量
preVector.x /= preVectorLen;
preVector.y /= preVectorLen;
curVector.x /= curVectorLen;
curVector.y /= curVectorLen;
//作curVector的逆时针垂直向量。
PointF verticalVec = new PointF(curVector.y, -curVector.x);
//判断这个垂直向量和v1的点积,点积>0表示俩向量夹角锐角。=0表示垂直,<0表示钝角
float vDot = preVector.x * verticalVec.x + preVector.y * verticalVec.y;
if (vDot > 0) {
//v2的逆时针垂直向量和v1是锐角关系,说明v1在v2的逆时针方向。
} else {
dAngle = -dAngle;
}
matrix.postRotate((float) dAngle, photoCorners[8], photoCorners[9]);
}
Matrix操作:
matrix.postTranslate(x, y);//图像偏移的X、Y轴距离
matrix.postScale(scale, scale, centerX, centerY);//图像根据缩放中心点缩放的倍数
matrix.postRotate(angle, centerX, centerY);//图像根据旋转中心点旋转的弧度
3.根据Matrix变换得到新的图形关键定位点
float[] photoCorners = new float[10];//变换后的关键定位点数组
matrix.mapPoints(photoCorners, photoCornersSrc);//通过matrix进行变换,得到新的关键定位点数据
4.绘制Matrix变换后的图像、边框线及边角操作图标
//绘制变换后图像
private void drawBitmap(Canvas canvas, Bitmap bitmap, Matrix matrix) {
canvas.drawBitmap(bitmap,matrix, null);//根据Matrix绘制变化后的Bitmap
}
//绘制图像边线(由于图像旋转后不一定是矩形,所以用Path绘制边线)
private void drawBoard(Canvas canvas, float[] photoCorners) {
Path photoBorderPath = new Path();
photoBorderPath.moveTo(photoCorners[0], photoCorners[1]);
photoBorderPath.lineTo(photoCorners[2], photoCorners[3]);
photoBorderPath.lineTo(photoCorners[4], photoCorners[5]);
photoBorderPath.lineTo(photoCorners[6], photoCorners[7]);
photoBorderPath.lineTo(photoCorners[0], photoCorners[1]);
canvas.drawPath(photoBorderPath, boardPaint);
}
//绘制边角操作图标
private void drawMarks(Canvas canvas, float[] photoCorners) {
float x;
float y;
x = photoCorners[0] - markerCopyRect.width() / 2;
y = photoCorners[1] - markerCopyRect.height() / 2;
markerCopyRect.offsetTo(x, y);
canvas.drawBitmap(mirrorMarkBM, x, y, null);
x = photoCorners[2] - markerDeleteRect.width() / 2;
y = photoCorners[3] - markerDeleteRect.height() / 2;
markerDeleteRect.offsetTo(x, y);
canvas.drawBitmap(deleteMarkBM, x, y, null);
x = photoCorners[4] - markerRotateRect.width() / 2;
y = photoCorners[5] - markerRotateRect.height() / 2;
markerRotateRect.offsetTo(x, y);
canvas.drawBitmap(rotateMarkBM, x, y, null);
x = photoCorners[6] - markerResetRect.width() / 2;
y = photoCorners[7] - markerResetRect.height() / 2;
markerResetRect.offsetTo(x, y);
canvas.drawBitmap(resetMarkBM, x, y, null);
}
三、Github源码
白板Demo源码,欢迎一起学习交流!