最终效果:
规则描述:
1、 要有三点之间构成了两个线段所组成的角度判断能力,不允许超过180度的角度出现,以免导致出现凹进去的形状。
2、不允许构成线段的端点两年交换导致线条出现相交。
实际构成:
1、裁剪框由4个端点所组成,在这里我使用了顺时针的方向排布了这4个点。
2、对规则1的实施,使用了代码:
其中first到middle是第一条线段端点,middle到end是第二条线条的端点:
private double linesAngle(PointF first, PointF middle, PointF end) {
double x1 = first.x - middle.x;
double x2 = end.x - middle.x;
double y1 = first.y - middle.y;
double y2 = end.y - middle.y;
double dot = x1 * x2 + y1 * y2;
double det = x1 * y2 - y1 * x2;
double angle = Math.atan2(det, dot) / Math.PI * 180f;
return angle;
}
3、对规则2的实现,使用了简单的判断添加移动偏移值后是否依然满足4个端点依然依次按照顺时针排序即可。结合2的调用,检查代码如下:
//禁止越界导致线条交叉:
switch (mSelectedPoint) {
case 0:
if (!(mVectorPoint[0].x + dx < mVectorPoint[1].x && mVectorPoint[0].y + dy < mVectorPoint[2].y
&& mVectorPoint[0].y + dy < mVectorPoint[3].y)) {
return true;
}
//不允许形成超过180度的角
double angle = linesAngle(mVectorPoint[1],
new PointF(mVectorPoint[0].x + dx, mVectorPoint[0].y + dy),
mVectorPoint[3]);
if (angle < 0f) {
return true;
}
break;
case 1:
if (!(mVectorPoint[1].x + dx > mVectorPoint[0].x && mVectorPoint[1].y + dy < mVectorPoint[2].y
&& mVectorPoint[1].y + dy < mVectorPoint[3].y)) {
return true;
}
//不允许形成超过180度的角
angle = linesAngle(mVectorPoint[0],
new PointF(mVectorPoint[1].x + dx, mVectorPoint[1].y + dy),
mVectorPoint[2]);
if (angle > 0f) {
return true;
}
break;
case 2:
if (!(mVectorPoint[2].x + dx > mVectorPoint[3].x && mVectorPoint[2].y + dy > mVectorPoint[0].y
&& mVectorPoint[2].y + dy > mVectorPoint[1].y)) {
return true;
}
angle = linesAngle(mVectorPoint[1],
new PointF(mVectorPoint[2].x + dx, mVectorPoint[2].y + dy),
mVectorPoint[3]);
if (angle > 0f) {
return true;
}
break;
case 3:
if (!(mVectorPoint[3].x + dx < mVectorPoint[2].x && mVectorPoint[3].y + dy > mVectorPoint[0].y
&& mVectorPoint[3].y + dy > mVectorPoint[1].y)) {
return true;
}
angle = linesAngle(mVectorPoint[2],
new PointF(mVectorPoint[3].x + dx, mVectorPoint[3].y + dy),
mVectorPoint[0]);
if (angle > 0f) {
return true;
}
break;
}
4、绘制:
? ? ? ? 4.1、先把图片移动到中间(即控件中点减去图片宽的一半、控件中点减去图片高的一半这对坐标为图片左上角的虚拟(0,0)点,即绘制图片的起始点),并使用较大的透明度绘制它:
//移动到图片到控件中间并贴图
Matrix matrix = new Matrix();
matrix.setTranslate(mWidth / 2f - mBitmap.getWidth() / 2f,
mHeight / 2f - mBitmap.getHeight() / 2f);
matrix.postScale(mScale, mScale, mWidth / 2f, mHeight / 2f);
mSelectorRectPointPaint.setAlpha(100);
canvas.drawBitmap(mBitmap, matrix, mSelectorRectPointPaint);
mSelectorRectPointPaint.setAlpha(255);
? ? ? ? 4.2、绘制选择框后,使用clipPath把选择框的封闭区域作为填充图片的限制区域,再绘制不透明的图片,即可使得选择区域的图片变得更“亮”:
//绘制选择框:
if (null != mSelectorRectPointPaint) {
mSelectorRectPointPaint.setStyle(Paint.Style.FILL);
Path path = new Path();
for (int i = 0; i < mVectorPoint.length; i++) {
PointF pointF = mVectorPoint[i];
if (i == 0) {
path.moveTo(pointF.x, pointF.y);
} else {
path.lineTo(pointF.x, pointF.y);
}
}
path.lineTo(mVectorPoint[0].x, mVectorPoint[0].y);
//选择框内的图片透明度为0:
canvas.save();
canvas.clipPath(path);
canvas.drawBitmap(mBitmap, matrix, mSelectorRectPointPaint);
canvas.restore();
//绘制选择器边框:
mSelectorRectPointPaint.setStyle(Paint.Style.STROKE);
canvas.drawPath(path, mSelectorRectPointPaint);
mSelectorRectPointPaint.setStyle(Paint.Style.FILL);
for (PointF pointF : mVectorPoint) {
//绘制选择器端点
canvas.drawCircle(pointF.x, pointF.y, 30, mSelectorRectPointPaint);
}
}
|