我有一个点列表,表示我使用Path对象在画布上绘制的曲线。
path.moveTo(x, y);
for (int i = 0; i < points.size(); i++) {
path.lineTo(points.get(i).x, points.get(i).y);
}
canvas.drawPath(path, paint);
我想要实现的是能够设置用户可以触摸和移动的控制点,并根据该移动我的点将被转换。与photoshop Pen Tool
相同的事情看到图片:
注意:android Path仅用于绘图,我不需要修改Path我需要修改坐标。所以上面的代码可以替换为
canvas.drawLine();
它与Path对象无关。
答案 0 :(得分:3)
这是使用一个“锚点”和两个控制点的简单视图,如果您需要更多锚点,请在路径中添加另一个cubicTo
:
class V extends View {
static final float RADIUS = 32;
Path path = new Path();
Paint pathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Paint controlPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
PointF ctrl1 = new PointF();
PointF ctrl2 = new PointF();
PointF ctrl3 = new PointF();
PointF ctrl4 = new PointF();
PointF anchor = new PointF();
GestureDetector detector;
Layout layout;
public V(Context context) {
super(context);
pathPaint.setColor(Color.RED);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeWidth(16);
controlPaint.setColor(Color.GREEN);
controlPaint.setAlpha(128);
detector = new GestureDetector(context, listener);
}
GestureDetector.OnGestureListener listener = new GestureDetector.SimpleOnGestureListener() {
PointF target;
@Override
public boolean onDown(MotionEvent e) {
PointF[] targets = { ctrl2, ctrl3, anchor };
for (PointF t : targets) {
if (Math.hypot(t.x - e.getX(), t.y - e.getY()) < RADIUS) {
target = t;
return true;
}
}
target = null;
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (target == null) return false;
target.offset(-distanceX, -distanceY);
if (target == ctrl2 || target == ctrl3) {
PointF otherControl = target == ctrl2 ? ctrl3 : ctrl2;
// anchor just between points
double a = Math.atan2(anchor.y - target.y, anchor.x - target.x);
double r = Math.hypot(otherControl.x - anchor.x, otherControl.y - anchor.y);
otherControl.set((float) (anchor.x + r * Math.cos(a)), (float) (anchor.y + r * Math.sin(a)));
// anchor always in the center
// otherControl.set(2 * anchor.x - target.x, 2 * anchor.y - target.y);
} else {
ctrl2.offset(-distanceX, -distanceY);
ctrl3.offset(-distanceX, -distanceY);
}
rebuildPath();
invalidate();
return true;
}
};
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
ctrl1.set(w * 0.0f, h * 1.0f);
ctrl2.set(w * 0.1f, h * 0.5f);
ctrl3.set(w * 0.9f, h * 0.5f);
ctrl4.set(w * 1.0f, h * 1.0f);
anchor.set(w * 0.5f, h * 0.5f);
rebuildPath();
CharSequence src = "you can drag any green circle: the both control points or the anchor point\n\n" +
"notice that the control points can be adjusted individually - the only constraint for a smooth line is that the anchor point is between them (but not necessarily in the exact center)";
TextPaint tp = new TextPaint();
tp.setColor(Color.WHITE);
tp.setTextSize(32);
layout = new StaticLayout(src, tp, w - 64, Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
}
private void rebuildPath() {
path.reset();
path.moveTo(ctrl1.x, ctrl1.y);
path.cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, anchor.x, anchor.y);
path.cubicTo(ctrl3.x, ctrl3.y, ctrl4.x, ctrl4.y, ctrl4.x, ctrl4.y);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
detector.onTouchEvent(event);
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.translate(32, 32);
layout.draw(canvas);
canvas.restore();
canvas.drawPath(path, pathPaint);
controlPaint.setStyle(Paint.Style.FILL);
canvas.drawCircle(anchor.x, anchor.y, RADIUS, controlPaint);
canvas.drawCircle(ctrl2.x, ctrl2.y, RADIUS, controlPaint);
canvas.drawCircle(ctrl3.x, ctrl3.y, RADIUS, controlPaint);
controlPaint.setStyle(Paint.Style.STROKE);
canvas.drawLine(ctrl2.x, ctrl2.y, ctrl3.x, ctrl3.y, controlPaint);
}
}