效果挺简单的 ,简单说下思路。
这个图由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;
}
}