原理图
可以看一下和了解一下贝塞尔曲线:http://www.html-js.com/article/1628
MessageBubbleView:自定义view
public class MessageBubbleView extends View {
// 两个圆的圆形
private PointF mFixationPoint, mDragPoint;
// 拖拽圆的半径
private int mDragRadius = 10;
private Paint mPaint;
// 固定圆的最大半径(初始半径)
private int mFixationRadiusMax = 7;
private int mFixationRadiusMin = 3;
private int mFixationRadius;
public MessageBubbleView(Context context) {
this(context, null);
}
public MessageBubbleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MessageBubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mDragRadius = dp2x(mDragRadius);
mFixationRadiusMax = dp2x(mFixationRadiusMax);
mFixationRadiusMin = dp2x(mFixationRadiusMin);
}
private int dp2x(int dpValue) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
}
@Override
protected void onDraw(Canvas canvas) {
if (mDragPoint == null || mFixationPoint == null) {
return;
}
//绘制拖拽的圆
canvas.drawCircle(mDragPoint.x, mDragPoint.y, mDragRadius, mPaint);
Path bezeierPath = getBezeierPath();
if (bezeierPath != null) {//当大于最小的时候才绘制
//绘制固定的圆
canvas.drawCircle(mFixationPoint.x, mFixationPoint.y, mFixationRadius, mPaint);
//绘制贝塞尔曲线
canvas.drawPath(bezeierPath, mPaint);
}
}
/**
* 获取两个圆之间的距离
*/
private double getDistance(PointF point1, PointF point2) {
return Math.sqrt((point1.x - point2.x) * (point1.x - point2.x)
+ (point1.y - point2.y) * (point1.y - point2.y));
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
float downX = event.getX();
float downY = event.getY();
initPoint(downX, downY);
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
updatePoint(moveX, moveY);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
/**
* 更新拖拽点的坐标
*/
private void updatePoint(float moveX, float moveY) {
mDragPoint.x = moveX;
mDragPoint.y = moveY;
}
/**
* 初始化点
*/
private void initPoint(float downX, float downY) {
mFixationPoint = new PointF(downX, downY);
mDragPoint = new PointF(downX, downY);
}
/**
* 获得贝塞尔的曲线
*
* @return
*/
public Path getBezeierPath() {
//获得两个圆之间的距离
double distance = getDistance(mDragPoint, mFixationPoint);
mFixationRadius = (int) (mFixationRadiusMax - distance / 14);
if (mFixationRadius < mFixationRadiusMin) {
// 超过一定距离 贝塞尔和固定圆都不要画了
return null;
}
Path bezeierPath = new Path();
//求角a
//求斜率
float dy = mDragPoint.y - mFixationPoint.y;
float dx = mDragPoint.x - mFixationPoint.x;
float tanA = dy / dx;
//求出角a
double arcTanA = Math.atan(tanA);
//p0
float p0x = (float) (mFixationRadius * Math.sin(arcTanA) + mFixationPoint.x);
float p0y = (float) (mFixationPoint.y - mFixationRadius * Math.cos(arcTanA));
//p1
float p1x = (float) (mDragRadius * Math.sin(arcTanA) + mDragPoint.x);
float p1y = (float) (mDragPoint.y - mDragRadius * Math.cos(arcTanA));
//p2
float p2x = (float) (mDragPoint.x - mDragRadius * Math.sin(arcTanA));
float p2y = (float) (mDragPoint.y + mDragRadius * Math.cos(arcTanA));
//p3
float p3x = (float) (mFixationPoint.x - mFixationRadius * Math.sin(arcTanA));
float p3y = (float) (mFixationPoint.y + mFixationRadius * Math.cos(arcTanA));
//绘制贝塞尔曲线
bezeierPath.moveTo(p0x, p0y);
//绘制第一条线
PointF controlPoint = getControlPoint();
bezeierPath.quadTo(controlPoint.x, controlPoint.y, p1x, p1y);//控制点+目标坐标
//绘制到第二条
bezeierPath.lineTo(p2x, p2y);
bezeierPath.quadTo(controlPoint.x, controlPoint.y, p3x, p3y);
//最后闭合
bezeierPath.close();
return bezeierPath;
}
/**
* 获取控制点
*/
public PointF getControlPoint() {
return new PointF((mDragPoint.x + mFixationPoint.x) / 2, (mDragPoint.y + mFixationPoint.y) / 2);
}
}