(x1, y1)为A点
(x2, y2)为B点
(x3, y3)为C点
(x4, y4)为D点
(anchorX, anchorY)为X点,这是二阶贝塞尔曲线的控制点,这里有两条二阶贝塞尔曲线,都是同一个控制点。
同时在canvas中将这几个点的字母标注出来,具体的做法是调用canvas.drawText,修改具体的代码我就不发了。

每个点的显示位置有所偏差(尤其是X点),这是因为canvas.drawText的参数需要根据字符的大小做调整,我为了简便,没有去做,但是这些点你应该知道他们的实际位置,A,B,C,D很好辨认,但是X应该是在中间才对。
有了上面那幅图对于这段代码就好理解了
path.moveTo(x1, y1);
path.quadTo(anchorX, anchorY, x2, y2);
path.lineTo(x3, y3);
path.quadTo(anchorX, anchorY, x4, y4);
path.lineTo(x1, y1);
拉伸的粘连效果主要取决于quadTo绘制的两条贝塞尔曲线,这两条曲线以他们之间的中间位置为控制点,导致曲线以相同的弧度往内弯曲。当两端的圆圈距离越来越长,控制点的位置以及两条曲线的端点也跟着变化(需要根据距离计算端点和控制点的位置)就形成了橡皮筋的粘连效果。
?
各坐标点的计算
那么现在的最后一个问题是如何寻找这些变化的点。
首先我们需要记录手指运动过程中,触摸点的变化情况,在demo中是使用(x,y)来代表这个触摸点,然后根据(startX,startY)(这个点是写死的)计算出控制点的坐标(anchorX,anchorY)
代码如下
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
// 判断触摸点是否在tipImageView中
Rect rect = new Rect();
int[] location = new int[2];
tipImageView.getDrawingRect(rect);
tipImageView.getLocationOnScreen(location);
rect.left = location[0];
rect.top = location[1];
rect.right = rect.right + location[0];
rect.bottom = rect.bottom + location[1];
if (rect.contains((int)event.getRawX(), (int)event.getRawY())){
isTouch = true;
}
}else if(event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL){
isTouch = false;
tipImageView.setX(startX - tipImageView.getWidth()/2);
tipImageView.setY(startY - tipImageView.getHeight()/2);
}
invalidate();
if(isAnimStart){
return super.onTouchEvent(event);
}
anchorX = (event.getX() + startX)/2;
anchorY = (event.getY() + startY)/2;
x = event.getX();
y = event.getY();
return true;
}
?
其中if和else代码块中的的代码和粘连效果无关,这些代码是关于气泡的ImageView显示与消失的。
主要就是下面的代码
invalidate();
if(isAnimStart){
return super.onTouchEvent(event);
}
anchorX = (event.getX() + startX)/2;
anchorY = (event.getY() + startY)/2;
x = event.getX();
y = event.getY();
可以看出在onTouchEvent中,主要工作是记录,坐标点的计算还是在calculate()方法里(不过这里也简单的计算了控制点的坐标(anchorX,anchorY),其实这也可以放到calculate里面)。另外
invalidate()方法我觉得还是放在最后比较好。不过没什么大碍,也就是落后一个点而已,你根本感觉不到。
?
而calculate()方法里面对坐标的计算也很简单,没几行代码,结合上面的几幅图应该很容易解出来。这里就不再赘述了。
?
?
其实整篇文章可以用一句话来概括:粘连效果的关键是由同一个控制点(中间点)“拖住”两条贝塞尔曲线。
最后做一点补充,为了将橡皮的效果做的更逼真,这个demo中还动态的改变了两端圆点的半径,当然这也会导致其他点也做相应的改变
float distance = (float) Math.sqrt(Math.pow(y-startY, 2) + Math.pow(x-startX, 2));
radius = -distance/15+DEFAULT_RADIUS;
?
?