使用贝塞尔曲线和Pathmeasure画粘连体

public class StickinessView1 extends View {
   Paint paint;
   Path path;
   private PathMeasure mPathMeasure;
   ValueAnimator valueAnimator;
   boolean isAnimation;
   PointF ponitF;
   float offset1=30;
   float offset2=30;
   public StickinessView1(Context context) {
      this(context, null);
   }
   public StickinessView1(Context context, AttributeSet attrs) {
      this(context, attrs, 0);
   }

   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
   public StickinessView1(Context context, AttributeSet attrs, int defStyleAttr) {
      super(context, attrs, defStyleAttr);
      initData();
   }

   private void initData() {
      mPathMeasure = new PathMeasure();
      path = new Path();
      paint = new Paint();
      paint.setColor(Color.RED);
      paint.setStyle(Paint.Style.FILL);
      valueAnimator=new ValueAnimator();
      //设置回弹插值器
      valueAnimator.setInterpolator(new BounceInterpolator());

      valueAnimator.setDuration(1000);
      valueAnimator.addListener(new Animator.AnimatorListener() {
         @Override
         public void onAnimationStart(Animator animation) {

         }

         @Override
         public void onAnimationEnd(Animator animation) {
            //恢复原状
            isAnimation=false;
            lastX1=200;
            lastY1=200;
         }

         @Override
         public void onAnimationCancel(Animator animation) {

         }

         @Override
         public void onAnimationRepeat(Animator animation) {

         }
      });

      valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
            isAnimation=true;
            ponitF= (PointF) animation.getAnimatedValue();
            Log.e("tt", "onAnimationUpdate: ponitF"+ponitF.x+" x "+ponitF.y );
            postInvalidate();
         }
      });
   }


   @Override
   public void draw(Canvas canvas) {
      super.draw(canvas);
      if(isAnimation) {
         drawAdhesionBody(canvas, 200, 200, 15f, offset1, ponitF.x, ponitF.y, 20f,
               offset2)
         ;
      }else{
         drawAdhesionBody(canvas, 200, 200, 15f, offset1, lastX1, lastY1, 20f,
               offset2)
         ;
      }
   }

   /**
    * 画粘连体
    *
    * @param cx1     圆心x1
    * @param cy1     圆心y1
    * @param r1      圆半径r1
    * @param offset1 贝塞尔曲线偏移角度offset1
    * @param cx2     圆心x2
    * @param cy2     圆心y2
    * @param r2      圆半径r2
    * @param offset2 贝塞尔曲线偏移角度offset2
    * @return
    */
   public void drawAdhesionBody(Canvas canvas,float cx1, float cy1, float r1, float offset1, float
         cx2, float cy2, float r2, float offset2) {


      path.reset();
      float dX = cx1 - cx2;
      float dY = cy1 - cy2;
      path.addCircle(cx1, cy1, r1, Path.Direction.CW);

      path.addCircle(cx2, cy2, r2, Path.Direction.CW);

      mPathMeasure.setPath(path, false);
      //canvas.drawPath(path, paint); 这样使用 发现判断不准确画的也不太对
      canvas.drawCircle(cx1, cy1, r1,paint);

      canvas.drawCircle(cx2, cy2, r2,paint);

    /* 求三角函数 */
      float degrees = (float) Math.toDegrees(Math.atan(Math.abs(cy2 - cy1) / Math.abs(cx2 -
            cx1)));

    /* 两条贝塞尔曲线的四个端点 */
      float x1 = 0, y1 = 0, x2 = 0, y2 = 0, x3 = 0, y3 = 0, x4 = 0, y4 = 0;

      float position1[] = new float[2];
      float position2[] = new float[2];
      float position3[] = new float[2];
      float position4[] = new float[2];
      if(dX==0&&dY==0)return ;
      //2在圆1的右下角 包括正右方,包括正下方
      if (dX <= 0 && dY <= 0) {
         Log.e("tt", "drawAdhesionBody: \t//2在圆1的右下角" );
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((offset1 + degrees) / 360), position3,
               null);
         if (degrees <= offset1) {
            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((360 - (offset1 - degrees)) / 360),
                  position1,
                  null);
         } else {
            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((degrees - offset1) / 360),
                  position1,
                  null);
         }
         Log.e("tt", "drawAdhesionBody: mPathMeasure.getLength() "+mPathMeasure.getLength()  );
         mPathMeasure.nextContour();
         Log.e("tt", "drawAdhesionBody: mPathMeasure.getLength() "+mPathMeasure.getLength()  );

         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 + offset2 + degrees) / 360),
               position2,
               null);
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 + degrees-offset2 ) / 360),
               position4,
               null);
      }
      //2在圆1的左下角 包括正左方
       else if (dX > 0 && dY <= 0) {
         Log.e("tt", "drawAdhesionBody: \t//2在圆1的左下角" );

         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((90 + offset1 +90- degrees) / 360),
               position3, null);
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((90 - offset1 + 90-degrees) / 360),
               position1, null);
         mPathMeasure.nextContour();
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((270 - offset2 + 90-degrees) / 360),
               position4,
               null);
         if(degrees<=offset2)
         mPathMeasure.getPosTan(mPathMeasure.getLength() * (( offset2-degrees) / 360),
               position2,
               null);
         else
            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((360- (degrees-offset2)) / 360),
                  position2,
                  null);

      }
      //2在圆1的左上角 包括正上方
      else if (dX >= 0 && dY > 0) {
         Log.e("tt", "drawAdhesionBody: \t//2在圆1的左上角" );

         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 + offset1 + degrees) / 360),
               position3,
               null);
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 - offset1 + degrees) / 360),
               position1,
               null);
         mPathMeasure.nextContour();
         if (degrees <= offset2) {
            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((360 - offset2 + degrees) /
                        360),
                  position4,
                  null);
         } else {
            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((degrees - offset2) / 360),
                  position4,
                  null);
         }
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((degrees + offset2) / 360),
               position2,
               null);
      }
      //2在圆1的右上角
       else if (dX < 0 && dY > 0) {
         Log.e("tt", "drawAdhesionBody: \t//2在圆1的右上角" );
         if (degrees <= offset1)
         mPathMeasure.getPosTan(mPathMeasure.getLength() * (( offset1-degrees) / 360),
               position3,
               null);
         else
            mPathMeasure.getPosTan(mPathMeasure.getLength() * (( 360-(degrees-offset1)) / 360),
                  position3,
                  null);

            mPathMeasure.getPosTan(mPathMeasure.getLength() * ((360-offset1 - degrees) / 360),
                  position1,
                  null);
         mPathMeasure.nextContour();
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 - offset2 - degrees) / 360),
               position4,
               null);
         mPathMeasure.getPosTan(mPathMeasure.getLength() * ((90-degrees + offset2 + 90) / 360),
               position2,
               null);

      }

    /* 贝塞尔曲线的控制点 */
      x1 = position1[0];
      y1 = position1[1];
      x2 = position2[0];
      y2 = position2[1];
      x3 = position3[0];
      y3 = position3[1];
      x4 = position4[0];
      y4 = position4[1];
      Log.e("tt", "drawAdhesionBody:  x1, y1 , x2, y2 , , y3 , x4, y4;" +
            x1 +","+y1 +","+x2 +","+y2 +","+ x3 +","+ y3 +","+x4 +","+ y4 );
      float anchorX1, anchorY1, anchorX2, anchorY2;

    /* 1大于圆2 */
      if (r1 >r2) {
         anchorX1 = (x2 + x3) / 2;
         anchorY1 = (y2 + y3) / 2;
         anchorX2 = (x1 + x4) / 2;
         anchorY2 = (y1 + y4) / 2;

      }
   /* 1小于或等于圆2 */
      else {
         anchorX1 = (x1 + x4) / 2;
         anchorY1 = (y1 + y4) / 2;
         anchorX2 = (x2 + x3) / 2;
         anchorY2 = (y2 + y3) / 2;
      }


      path.moveTo(x1, y1);
      path.quadTo(anchorX1, anchorY1, x2, y2);
      //path.lineTo(x2,y2);
      path.lineTo(x4, y4);
      //path.lineTo(x3,y3);
      path.quadTo(anchorX2, anchorY2, x3, y3);
      path.lineTo(x1, y1);

      canvas.drawPath(path,paint);
      //return path;
   }
   float cy2=200 ,cx2=200;
   float lastX=200,lastY=200;
   float lastX1=200,lastY1=200;
   @Override
   public boolean onTouchEvent(MotionEvent event) {
      cx2=event.getX();
      cy2=event.getY();
      switch (event.getAction()){

         case MotionEvent.ACTION_DOWN:
            lastX=cx2;
            lastY=cy2;
            break;
            case MotionEvent.ACTION_MOVE:
            float dx=  cx2-lastX;
            float dy=  cy2-lastY;
               if(Math.abs(dx)>0.2||Math.abs(dx)>0.2){
                  lastX1+=dx;
                  lastY1+=dy;
               postInvalidate();
               }
               break;
         case MotionEvent.ACTION_UP:

            valueAnimator.setObjectValues(new PointF(lastX1,lastY1),new PointF(200,200));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
               valueAnimator.setEvaluator(new PointFEvaluator(new PointF()));
            }
            valueAnimator.start();
            break;

      }
      lastX=cx2;
      lastY=cy2;
      return true;
   }
}

这是完整的代码.主要的点是 运动时坐标的计算.

就拿

2在圆1的右下角 包括正右方,包括正下方
来说吧 图:


开始的位置是从红点而且是顺时针 通过PathMeasure getPosTan的方法可以得到某一个长度的坐标和正切.

长度如何求呢.... 既然我们已经知道 旋转角度 那就可以 通过PathMeasure getLength方法获取圆1的总长度乘 (degree+offset)/360 就获取到了长度.

如: x3 y3 的坐标就是这样求的

mPathMeasure.getPosTan(mPathMeasure.getLength() * ((offset1 + degrees) / 360), position3,
      null);
但x1 y1 的坐标是分情况的 如果 degree小于offset1 那么这角度是不是在360-offset1~0的范围.大于角度是不是degrees-offset1所以:

if (degrees <= offset1) {
   mPathMeasure.getPosTan(mPathMeasure.getLength() * ((360 - (offset1 - degrees)) / 360),
         position1,
         null);
} else {
   mPathMeasure.getPosTan(mPathMeasure.getLength() * ((degrees - offset1) / 360),
         position1,
         null);
}
 那么圆2计算也是一样的 在计算之前要调用 nextContour()跳到圆2的图形区域.

mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 + offset2 + degrees) / 360),
      position2,
      null);
mPathMeasure.getPosTan(mPathMeasure.getLength() * ((180 + degrees-offset2 ) / 360),
      position4,
      null);
 其他的位置状态计算,稍微的画一下,分析就简单了.效果如图

参考:http://blog.csdn.net/z82367825/article/details/51599245 核心代码改写的

ps:通过pathmeasure计算某一个点坐标很简单.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值