效果挺简单的 ,简单说下思路。
这个图由7个点确定:两个圆心P1,P2,每个圆的两个对称点P3,P4,P5,P6,以及两个圆心的终点P7(P5,P6,P7图中没画,相信大家清楚)
点的获取:首先选取一个固定点作为中间不动的球的圆心,然后在onTouchEvent中记录按下的点重新绘制,难点在于每个球取的点的获取,动球在固定的球的左上,右上,左下,右下方向时,sin α和cos α值会有正负变化,如图以动圆在固定圆的右下方为例:
点P4的坐标为:
P4.x = P1.x - r * sin α
P4.y = P1.y + r * cos α
点P3的坐标为:
P3.x = P2.x - r * sin α
P3.y = P2.y + r * cos α
以P3为例,P3相对圆心P2的对称点P5(图中没画)的坐标为:
P5.x = 2 * P2.x - P3.x
P5.y = 2 * P2.y - P3.y
P4的对称点类似写法。
好了,点找完了 接下来画图吧,直接上代码:
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.os.Build; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * Created by Administrator on 2017/5/16. */ public class BezierView extends View { private static final String TAG = "wsy"; private Paint mPaint,mCirclePaint; private Path mPath1,mPath2; private Point startPoint; private Point endPoint; private Point centerPoint; private Point startPointEdge1,startPointEdge2; //起点两个边界点 private Point endPointEdge1,endPointEdge2; //终点两个边界点 private int radius=50; //初始半径 public BezierView(Context context) { super(context); init(context); } public BezierView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public BezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); getPointsByCenterAndRadius(); mPath1.reset(); mPath1.moveTo(startPointEdge1.x, startPointEdge1.y); mPath1.quadTo(centerPoint.x, centerPoint.y, endPointEdge1.x, endPointEdge1.y); mPath2.reset(); mPath2.moveTo(startPointEdge2.x, startPointEdge2.y); mPath2.quadTo(centerPoint.x, centerPoint.y, endPointEdge2.x, endPointEdge2.y); // 画路径 canvas.drawPath(mPath2, mPaint); canvas.drawPath(mPath1, mPaint); canvas.drawCircle(startPoint.x,startPoint.y,radius,mCirclePaint); canvas.drawCircle(endPoint.x,endPoint.y,radius,mCirclePaint); // canvas.drawOval(startPoint.x - radius,startPoint.y - radius, startPoint.x + radius , startPoint.y + radius,mPaint); // canvas.drawOval(endPoint.x - radius,endPoint.y - radius, endPoint.x + radius , endPoint.y + radius,mPaint); } private void init(Context context) { mPaint = new Paint(); mCirclePaint = new Paint(); mPath1 = new Path(); mPath2 = new Path(); startPoint = new Point(600, 600); centerPoint = new Point(350,350); endPoint = new Point(100, 100); startPointEdge1 = new Point(); startPointEdge2 = new Point(); endPointEdge1 = new Point(); endPointEdge2 = new Point(); mPaint.setStrokeWidth(2); mPaint.setStyle(Paint.Style.STROKE); mPaint.setAntiAlias(true); mPaint.setDither(true); mPaint.setColor(Color.RED); mCirclePaint.setColor(Color.RED); mCirclePaint.setAntiAlias(true); mCirclePaint.setStrokeWidth(100); mCirclePaint.setDither(true); } private void getPointsByCenterAndRadius() { centerPoint.x = (startPoint.x+endPoint.x)/2; centerPoint.y = (startPoint.y+endPoint.y)/2; double startToEndK ; //起点和终点的连线斜率 double sin ,cos; startToEndK = (double) (startPoint.y - endPoint.y) / (double)(startPoint.x - endPoint.x); cos = Math.pow(1/(startToEndK*startToEndK+1),0.5); sin = Math.pow(startToEndK*startToEndK/(startToEndK*startToEndK+1),0.5); radius = (int) (50 + Math.pow(((endPoint.y - startPoint.y)*(endPoint.y - startPoint.y) + (endPoint.x - startPoint.x)*(endPoint.x - startPoint.x)),0.5)/15); if ((endPoint.x>startPoint.x&&endPoint.y>startPoint.y)||(endPoint.x<startPoint.x&&endPoint.y<startPoint.y)) //右下角或左上角 { startPointEdge1.x = (int) (startPoint.x - radius*sin); //根据tan获得cos 和sin 再获得第一个边界点的x和y startPointEdge1.y = (int) (startPoint.y + radius*cos); startPointEdge2.x = startPoint.x * 2 - startPointEdge1.x; startPointEdge2.y = startPoint.y * 2 - startPointEdge1.y; endPointEdge1.x = (int) (endPoint.x - radius*sin); //根据tan获得cos 和sin 再获得第一个边界点的x和y endPointEdge1.y = (int) (endPoint.y + radius*cos); endPointEdge2.x = endPoint.x * 2 - endPointEdge1.x; endPointEdge2.y = endPoint.y * 2 - endPointEdge1.y; }else if ((endPoint.x>startPoint.x&&endPoint.y<startPoint.y) ||(endPoint.x<startPoint.x&&endPoint.y>startPoint.y) ) //右上角或者左下角 { startPointEdge1.x = (int) (startPoint.x + radius*sin); //根据tan获得cos 和sin 再获得第一个边界点的x和y startPointEdge1.y = (int) (startPoint.y + radius*cos); startPointEdge2.x = startPoint.x * 2 - startPointEdge1.x; startPointEdge2.y = startPoint.y * 2 - startPointEdge1.y; endPointEdge1.x = (int) (endPoint.x + radius*sin); endPointEdge1.y = (int) (endPoint.y + radius*cos); endPointEdge2.x = endPoint.x * 2 - endPointEdge1.x; endPointEdge2.y = endPoint.y * 2 - endPointEdge1.y; } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: endPoint.x = (int) event.getX(); endPoint.y = (int) event.getY(); invalidate(); break; } return true; } }