在QQ或者微信中,都会有消息拖拽效果:
实现起来其实都非常的简单,上代码:
public class BezierView extends View {
private Paint mPaint;
//两个圆 圆心点
private PointF c1,c2;
private float r2 = 25;
private float r1 = 25;
private float downX;
private float downY;
private float moveX;
private float moveY;
public static final int A = 200;
private float r;
public BezierView(Context context) {
this(context,null);
}
public BezierView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public BezierView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
initPoint();
}
private void initPoint() {
c1 = new PointF();
c2 = new PointF();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.RED);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算两点之间的距离
float d = (float) Math.sqrt((c1.x-c2.x)*(c1.x-c2.x)+(c1.y-c2.y)*(c1.y-c2.y));
r = r1 - (d/A)*r1;
if (r < 0)
r = 0;
Path path = getBezierPath();
if (c1.x != 0&&c1.y != 0&&c2.x != 0&&c2.y != 0){
canvas.drawCircle(c1.x,c1.y, r,mPaint);
canvas.drawCircle(c2.x,c2.y,r2,mPaint);
if (path != null){
canvas.drawPath(path,mPaint);
}
}
}
private Path getBezierPath() {
if (r == 0){
return null;
}
Path bezierPath = new Path();
// 贝塞尔曲线怎么求?
// 计算斜率
float dx = c1.x - c2.x;
float dy = c1.y - c2.y;
if (dx == 0) {
dx = 0.001f;
}
float tan = dy / dx;
// 获取角a度值
float arcTanA = (float) Math.atan(tan);
// 依次计算 p0 , p1 , p2 , p3 点的位置
float P0X = (float) (c1.x + r * Math.sin(arcTanA));
float P0Y = (float) (c1.y - r * Math.cos(arcTanA));
float P1X = (float) (c2.x + r2 * Math.sin(arcTanA));
float P1Y = (float) (c2.y - r2 * Math.cos(arcTanA));
float P2X = (float) (c2.x - r2 * Math.sin(arcTanA));
float P2Y = (float) (c2.y + r2 * Math.cos(arcTanA));
float P3X = (float) (c1.x - r * Math.sin(arcTanA));
float P3Y = (float) (c1.y + r * Math.cos(arcTanA));
// 求控制点 两个点的中心位置作为控制点
PointF controlPoint = getPointByPercent(c1,c2, 0.5f);
// 整合贝塞尔曲线路径
bezierPath.moveTo(P0X, P0Y);
bezierPath.quadTo(controlPoint.x, controlPoint.y, P1X, P1Y);
bezierPath.lineTo(P2X, P2Y);
bezierPath.quadTo(controlPoint.x, controlPoint.y, P3X, P3Y);
bezierPath.close();
return bezierPath;
}
private PointF getPointByPercent(PointF c1, PointF c2, float v) {
PointF pointF = new PointF();
pointF.x = (c1.x+c2.x)*v;
pointF.y = (c1.y+c2.y)*v;
return pointF;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
c1.x = downX;
c1.y = downY;
c2.x = downX;
c2.y = downY;
break;
case MotionEvent.ACTION_MOVE:
moveX = event.getX();
moveY = event.getY();
c2.x = moveX;
c2.y = moveY;
invalidate();
break;
}
//消费事件
return true;
}
}
在布局中使用:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.bezier.BezierView
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>