Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下:

最终效果图
接下来一步一步的实现整个过程。

基本原理

其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的。然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小。最后就是松手后返回或者爆裂的实现。

Path介绍:

顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有

  1. moveTo() 移动Path到一个指定的点
  2. quadTo() 绘制二次贝塞尔曲线,接收两个点,第一个是控制弧度的点,第二个是终点。
  3. lineTo() 就是连线
  4. close() 闭合Path路径,
  5. reset() 重置Path的相关设置

Path入门热身:

path.reset();
 path.moveTo(200, 200);
 //第一个坐标是对应的控制的坐标,第二个坐标是终点坐标
 path.quadTo(400, 250, 600, 200);

 canvas.drawPath(path, paint);
 canvas.translate(0, 200);
 //调用close,就会首尾闭合连接
 path.close();
 canvas.drawPath(path, paint);

记得不要在onDraw方法中new Path或者 Paint哟!

Path

具体实现拆分:

其实整个过程就是绘制了两个贝塞尔二次曲线的的闭合Path路径,然后在上面添加两个圆形。

闭合的Path 路径实现从左上点画二次贝塞尔曲线到左下点,左下点连线到右下点,右下点二次贝塞尔曲线到右上点,最后闭合一下!!

相关坐标的确定

这是这次里面的难点之一,因为涉及到了数学里面的一个sin,cos,tan等等,我其实也忘完了,然后又脑补了一下,废话不多说,

为什么自己要亲自去画一下呢,因为画了你才知道,在360旋转的过程中,角标体系是有两套的,如果就使用一套来画的话,就画出现在旋转的过程中曲线重叠在一起的情况!

问题已经抛出来了,接下来直接看看代码实现!

角度确定

根据贴出来的原理图可以知道,我们可以使用起始圆心坐标和拖拽的圆心坐标,根据反正切函数来得到具体的弧度。


int dy = Math.abs(CIRCLEY - startY);
int dx = Math.abs(CIRCLEX - startX);
 angle = Math.atan(dy * 1.0 / dx);

ok,这里的startX,Y就是移动过程中的坐标。angle就是得到的对应的弧度(角度)。

相关Path绘制

前面已经提到在旋转的过程中有两套坐标体系,一开始我也很纠结这个坐标体系要怎么确定,后面又恍然大悟,其实相当于就是一三象限正比例增长,二四象限,反比例增长。

flag = (startY - CIRCLEY  ) * (startX- CIRCLEX ) <= 0;
 //增加一个flag,用于判断使用哪种坐标体系。

最最重要的来了,绘制相关的Path路径!

path.reset();
 if (flag) {
  //第一个点
 path.moveTo((float) (CIRCLEX - Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY - Math.cos(angle) * ORIGIN_RADIO));

 path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (startX - Math.sin(angle) * DRAG_RADIO), (float) (startY - Math.cos(angle) * DRAG_RADIO));
path.lineTo((float) (startX + Math.sin(angle) * DRAG_RADIO), (float) (startY + Math.cos(angle) * DRAG_RADIO));

path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (CIRCLEX + Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY + Math.cos(angle) * ORIGIN_RADIO));
path.close();
canvas.drawPath(path, paint);
 } else {
  //第一个点
  path.moveTo((float) (CIRCLEX - Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY + Math.cos(angle) * ORIGIN_RADIO));

  path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (startX - Math.sin(angle) * DRAG_RADIO), (float) (startY + Math.cos(angle) * DRAG_RADIO));
  path.lineTo((float) (startX + Math.sin(angle) * DRAG_RADIO), (float) (startY - Math.cos(angle) * DRAG_RADIO));

  path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (CIRCLEX + Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY - Math.cos(angle) * ORIGIN_RADIO));
  path.close();
  canvas.drawPath(path, paint);
 }

这里的代码就是把图片上相关的数学公式Java化而已!

到这里,其实主要的工作就完成的差不多了!

接下来,设置paint 为填充的效果,最后再画两个圆

paint.setStyle(Paint.Style.FILL)
 canvas.drawCircle(CIRCLEX, CIRCLEY, ORIGIN_RADIO, paint);//默认的
 canvas.drawCircle(startX == 0 ? CIRCLEX : startX, startY == 0 ? CIRCLEY : startY, DRAG_RADIO, paint);//拖拽的

就可以绘制出想要的效果了!

这里不得不再说说onTouch的处理!

case MotionEvent.ACTION_DOWN://有事件先拦截再说!!
   getParent().requestDisallowInterceptTouchEvent(true);
   CurrentState = STATE_IDLE;
   animSetXY.cancel();
   startX = (int) ev.getX();
   startY = (int) ev.getRawY();
   break;

处理一下事件分发的坑!

测量和布局

这样基本过得去了,但是我们的布局什么的还没有处理,math_parent是万万没法使用到具体项目当中去的!
测量的时候,如果发现不是精准模式,那么都手动去计算出需要的宽度和高度。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

 int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
 int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
 if (modeWidth == MeasureSpec.UNSPECIFIED || modeWidth == MeasureSpec.AT_MOST) {
  widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_RADIO * 2, MeasureSpec.EXACTLY);
 }
 if (modeHeight == MeasureSpec.UNSPECIFIED || modeHeight == MeasureSpec.AT_MOST) {
  heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_RADIO * 2, MeasureSpec.EXACTLY);
 }
 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

然后在布局变化时,获取相关坐标,确定初始圆心坐标:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
 super.onSizeChanged(w, h, oldw, oldh);
 CIRCLEX = (int) ((w) * 0.5 + 0.5);
 CIRCLEY = (int) ((h) * 0.5 + 0.5);
}

然后清单文件里面就可以这样配置了:

<com.lovejjfg.circle.DragBubbleView
 android:id="@+id/dbv"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_gravity="center"/>

这样之后,又会出现一个问题,那就是wrap_content 之后,这个View能绘制的区域只有自身那么大了,拖拽了都看不见了!这个坑怎么办呢,其实很简单,父布局加上android:clipChildren="false" 的属性!
这个坑也算是解决了!!

相关状态的确定

我们是不希望它可以无限的拖拽的,就是有一个拖拽的最远距离,还有就是放手后的返回,爆裂。那么对应的,这里需要确定几种状态:

private final static int STATE_IDLE = 1;//静止的状态
 private final static int STATE_DRAG_NORMAL = 2;//正在拖拽的状态
 private final static int STATE_DRAG_BREAK = 3;//断裂后的拖拽状态
 private final static int STATE_UP_BREAK = 4;//放手后的爆裂的状态
 private final static int STATE_UP_BACK = 5;//放手后的没有断裂的返回的状态
 private final static int STATE_UP_DRAG_BREAK_BACK = 6;//拖拽断裂又返回的状态
 private int CurrentState = STATE_IDLE;

private int MIN_RADIO = (int) (ORIGIN_RADIO * 0.4);//最小半径
 private int MAXDISTANCE = (int) (MIN_RADIO * 13);//最远的拖拽距离

确定好这些之后,在move的时候,就要去做相关判断了:

case MotionEvent.ACTION_MOVE://移动的时候
   startX = (int) ev.getX();
   startY = (int) ev.getY();

   updatePath();
   invalidate();
   break;

private void updatePath() {
 int dy = Math.abs(CIRCLEY - startY);
 int dx = Math.abs(CIRCLEX - startX);

 double dis = Math.sqrt(dy * dy + dx * dx);
 if (dis <= MAXDISTANCE) {//增加的情况,原始半径减小
  if (CurrentState == STATE_DRAG_BREAK || CurrentState == STATE_UP_DRAG_BREAK_BACK) {
   CurrentState = STATE_UP_DRAG_BREAK_BACK;
  } else {
   CurrentState = STATE_DRAG_NORMAL;
  }
  ORIGIN_RADIO = (int) (DEFAULT_RADIO - (dis / MAXDISTANCE) * (DEFAULT_RADIO - MIN_RADIO));
  Log.e(TAG, "distance: " + (int) ((1 - dis / MAXDISTANCE) * MIN_RADIO));
  Log.i(TAG, "distance: " + ORIGIN_RADIO);
 } else {
  CurrentState = STATE_DRAG_BREAK;
 }
//  distance = dis;
 flag = (startY - CIRCLEY) * (startX - CIRCLEX) <= 0;
 Log.i("TAG", "updatePath: " + flag);
 angle = Math.atan(dy * 1.0 / dx);
}

updatePath() 的方法之前已经看过部分了,这次的就是完整的。
这里做的事就是根据拖拽的距离更改相关的状态,并根据百分比来修改原始圆形的半径大小。还有就是之前介绍的确定相关的弧度!

最后放手的时候:

case MotionEvent.ACTION_UP:
   if (CurrentState == STATE_DRAG_NORMAL) {
    CurrentState = STATE_UP_BACK;
    valueX.setIntValues(startX, CIRCLEX);
    valueY.setIntValues(startY, CIRCLEY);
    animSetXY.start();
   } else if (CurrentState == STATE_DRAG_BREAK) {
    CurrentState = STATE_UP_BREAK;
    invalidate();
   } else {
    CurrentState = STATE_UP_DRAG_BREAK_BACK;
    valueX.setIntValues(startX, CIRCLEX);
    valueY.setIntValues(startY, CIRCLEY);
    animSetXY.start();
   }
   break;

自动返回这里使用到的 ValueAnimator,

animSetXY = new AnimatorSet();

 valueX = ValueAnimator.ofInt(startX, CIRCLEX);
 valueY = ValueAnimator.ofInt(startY, CIRCLEY);
 animSetXY.playTogether(valueX, valueY);
 valueX.setDuration(500);
 valueY.setDuration(500);
 valueX.setInterpolator(new OvershootInterpolator());
 valueY.setInterpolator(new OvershootInterpolator());
 valueX.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   startX = (int) animation.getAnimatedValue();
   Log.e(TAG, "onAnimationUpdate-startX: " + startX);
   invalidate();
  }

 });
 valueY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  @Override
  public void onAnimationUpdate(ValueAnimator animation) {
   startY = (int) animation.getAnimatedValue();
   Log.e(TAG, "onAnimationUpdate-startY: " + startY);
   invalidate();

  }
 });

最后在看看完整的onDraw方法吧!

@Override
protected void onDraw(Canvas canvas) {
 switch (CurrentState) {
  case STATE_IDLE://空闲状态,就画默认的圆
   if (showCircle) {
    canvas.drawCircle(CIRCLEX, CIRCLEY, ORIGIN_RADIO, paint);//默认的
   }
   break;
  case STATE_UP_BACK://执行返回的动画
  case STATE_DRAG_NORMAL://拖拽状态 画贝塞尔曲线和两个圆
   path.reset();
   if (flag) {
    //第一个点
    path.moveTo((float) (CIRCLEX - Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY - Math.cos(angle) * ORIGIN_RADIO));

    path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (startX - Math.sin(angle) * DRAG_RADIO), (float) (startY - Math.cos(angle) * DRAG_RADIO));
    path.lineTo((float) (startX + Math.sin(angle) * DRAG_RADIO), (float) (startY + Math.cos(angle) * DRAG_RADIO));

    path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (CIRCLEX + Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY + Math.cos(angle) * ORIGIN_RADIO));
    path.close();
    canvas.drawPath(path, paint);
   } else {
    //第一个点
    path.moveTo((float) (CIRCLEX - Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY + Math.cos(angle) * ORIGIN_RADIO));

    path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (startX - Math.sin(angle) * DRAG_RADIO), (float) (startY + Math.cos(angle) * DRAG_RADIO));
    path.lineTo((float) (startX + Math.sin(angle) * DRAG_RADIO), (float) (startY - Math.cos(angle) * DRAG_RADIO));

    path.quadTo((float) ((startX + CIRCLEX) * 0.5), (float) ((startY + CIRCLEY) * 0.5), (float) (CIRCLEX + Math.sin(angle) * ORIGIN_RADIO), (float) (CIRCLEY - Math.cos(angle) * ORIGIN_RADIO));
    path.close();
    canvas.drawPath(path, paint);
   }
   if (showCircle) {
    canvas.drawCircle(CIRCLEX, CIRCLEY, ORIGIN_RADIO, paint);//默认的
    canvas.drawCircle(startX == 0 ? CIRCLEX : startX, startY == 0 ? CIRCLEY : startY, DRAG_RADIO, paint);//拖拽的
   }
   break;

  case STATE_DRAG_BREAK://拖拽到了上限,画拖拽的圆:
  case STATE_UP_DRAG_BREAK_BACK:
   if (showCircle) {
    canvas.drawCircle(startX == 0 ? CIRCLEX : startX, startY == 0 ? CIRCLEY : startY, DRAG_RADIO, paint);//拖拽的
   }
   break;

  case STATE_UP_BREAK://画出爆裂的效果
   canvas.drawCircle(startX - 25, startY - 25, 10, circlePaint);
   canvas.drawCircle(startX + 25, startY + 25, 10, circlePaint);
   canvas.drawCircle(startX, startY - 25, 10, circlePaint);
   canvas.drawCircle(startX, startY, 18, circlePaint);
   canvas.drawCircle(startX - 25, startY, 10, circlePaint);
   break;

 }

}

到这里,成品就出来了!!

总结:

1、确定默认圆形的坐标;
2、根据move的情况,实时获取最新的坐标,根据移动的距离(确定出角度),更新相关的状态,画出相关的Path路径。超出上限,不再画Path路径。
3、松手时,根据相关的状态,要么带Path路径执行动画返回,要么不带Path路径直接返回,要么直接爆裂!

以上就是用Android Path 绘制贝塞尔曲线的示例,后续继续补充相关文章,谢谢大家对本站的支持!

(0)

相关推荐

  • Android通过Path实现搜索按钮和时钟复杂效果

    在Android中复杂的图形的绘制绝大多数是通过path来实现,比如绘制一条曲线,然后让一个物体随着这个曲线运动,比如搜索按钮,比如一个简单时钟的实现: 那么什么是path呢! 定义:path  就是路径,就是图形的路径的集合,它里边包含了路径里边的坐标点,等等的属性.我们可以获取到任意点的坐标,正切值. 那么要获取Path上边所有点的坐标还需要用到一个类,PathMeasure; PathMesure: PathMeasure是一个用来测量Path的类,主要有以下方法: 构造方法 公共方法 可

  • Android编程开发之在Canvas中利用Path绘制基本图形(圆形,矩形,椭圆,三角形等)

    本文实例讲述了Android编程开发之在Canvas中利用Path绘制基本图形的方法.分享给大家供大家参考,具体如下: 在Android中绘制基本的集合图形,本程序就是自定义一个View组件,程序重写该View组件的onDraw(Canvase)方法,然后在该Canvas上绘制大量的基本的集合图形. 直接上代码: 1.自定义的View组件代码: package com.infy.configuration; import android.content.Context; import andro

  • Android实现Path平滑的涂鸦效果实例

    前言 在最近的一个项目中做了一个涂鸦的效果,手指快速移动,会出现折线,这篇文章记录笔触优化.下面话不多说了,来一起看看详细的介绍吧. 优化前 优化 设计到的类:Paint,Path Path类记录了坐标点集合决定线条轨迹,Paint决定怎么画 Paint处理 //连接的外边缘以圆弧的方式相交 paint.setStrokeJoin(Paint.Join.ROUND); //线条结束处绘制一个半圆 paint.setStrokeCap(Paint.Cap.ROUND); Path处理 这里用的到有

  • Android自定义View系列之Path绘制仿支付宝支付成功动画

    前言 使用支付宝付款时,我们可以看到成功或者失败都会有个动画提示,如果我们需要做这样的效果的话,当然,你可以让设计师给你做个GIF,但是我们知道图像比较耗内存的,我们自己可以用代码实现还是代码实现好点吧. 效果 实现方法 首先我们需要了解PathMeasure这个类,这个类我们可以理解为用来管理Path.我们主要看几个方法. PathMeasure(): 构造方法 ,实例化一个对象 PathMeasure(Path path,boolean isClosed):传入Path对象和是否闭合,pat

  • Android 使用Path实现涂鸦功能

    今天实现一个涂鸦效果,会分几步实现,这里有一个重要的知识点就是图层,要理解这个,不然你看这篇博客,很迷茫,迷茫的苍茫的天涯是我的爱,先从简单的需求做起,绘制一条线,代码如下: package com.tuya; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.util

  • Android自定义View实现支付宝支付成功-极速get花式Path炫酷动画

    本文手把手教你图片->SVG->Path的姿势.. 从此酷炫Path动画,如此简单. 效果先随便上几个图,以后你找到的图有多精彩,gif就有多精彩: 随便搜了一个铅笔画的图,丢进去 随手复制的二维码icon 来自大佬wing的铁塔 前文回顾 这里简单回顾一下前文,GIF如下图: PathAnimView接受的唯一数据源是Path(给我一个Path,还你一个动画View) 所以内置了几种将别的资源->Path的方法: 直接传string.(A-Z,0-9 "." &qu

  • Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

    这两天学习了使用Path绘制贝塞尔曲线相关,然后自己动手做了一个类似QQ未读消息可拖拽的小气泡,效果图如下: 最终效果图 接下来一步一步的实现整个过程. 基本原理 其实就是使用Path绘制三点的二次方贝塞尔曲线来完成那个妖娆的曲线的.然后根据触摸点不断绘制对应的圆形,根据距离的改变改变原始固定圆形的半径大小.最后就是松手后返回或者爆裂的实现. Path介绍: 顾名思义,就是一个路径的意思,Path里面有很多的方法,本次设计主要用到的相关方法有 moveTo() 移动Path到一个指定的点 qua

  • Android贝塞尔曲线实现消息拖拽消失

    写在前头 写消息拖拽效果的文章不少,但是大部分都把自定义View写死了,我们要实现的是传入一个View,每个View都可以实现拖拽消失爆炸的效果,当然我也是站在巨人的肩膀上来学习的.但个人觉得程序员本就应该敢于学习和借鉴. 源码地址:源码Github地址 效果图 分析(用到的知识点):  (1)ValueAnimator (数值生成器) 用于生成数值,可以设置差值器来改变数字的变化幅度. (2)ObjectAnimator (动画生成器) 用于生成各种属性,布局动画,同样也可以设置差值器来改变效

  • Android自定义View绘制贝塞尔曲线中小红点的方法

    目录 前言 需求 效果图 代码 主要问题 简单画法 使用贝塞尔曲线 前言 上一篇文章用扇形图练习了一下安卓的多点触控,实现了单指旋转.二指放大.三指移动,四指以上同时按下进行复位的功能.今天这篇文章用很多应用常见的小红点,来练习一下贝塞尔曲线的使用. 需求 这里想法来自QQ的拖动小红点取消显示聊天条数功能,不过好像是记忆里的了,现在看了下好像效果变了.总而言之,就是一个小圆点,拖动的时候变成水滴状,超过一定范围后触发消失回调,核心思想如下: 1.一个正方形view,中间是小红点,小红点距离边框有

  • Android自定义View绘制贝塞尔曲线的方法

    本文实例为大家分享了Android自定义View绘制贝塞尔曲线的具体代码,供大家参考,具体内容如下 在平面内任选 3 个不共线的点,依次用线段连接. 在第一条线段上任选一个点 D.计算该点到线段起点的距离 AD,与该线段总长 AB 的比例. 根据上一步得到的比例,从第二条线段上找出对应的点 E,使得 AD:AB = BE:BC. 连接这两点 DE. 从新的线段 DE 上再次找出相同比例的点 F,使得 DF:DE = AD:AB = BE:BC. 到这里,我们就确定了贝塞尔曲线上的一个点 F.接下

  • Android自定义View绘制贝塞尔曲线实现流程

    目录 前言 二阶贝塞尔曲线 三阶贝塞尔曲线 前言 对于Android开发,实现贝塞尔曲线还是比较方便的,有对应的API供你调用.由于一阶贝塞尔曲线就是一条直线,实际没啥多大用处,因此,下面主要讲解二阶和三阶. 二阶贝塞尔曲线 在Android中,使用quadTo来实现二阶贝塞尔 path.reset() path.moveTo(startX, startY) path.quadTo(currentX, currentY, endX, endY) canvas.drawPath(path, cur

  • Android 利用三阶贝塞尔曲线绘制运动轨迹的示例

    本篇文章主要介绍了Android 利用三阶贝塞尔曲线绘制运动轨迹的示例,分享给大家,具体如下: 实现点赞效果,自定义起始点以及运动轨迹 效果图: xml布局: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/rl_root&

  • Android 实现仿QQ拖拽气泡效果的示例

    目录 效果图: 一.实现思路 二.功能实现 三.全屏拖拽效果实现 源码地址: 效果图: 一.实现思路 在列表中默认使用自定义的TextView控件来展示消息气泡,在自定义的TextView控件中重写onTouchEvent方法,然后在DOWN.MOVE.UP事件中分别处理拖拽效果. 整个拖拽效果我们可以拆分成以下几步来实现: 1.默认状态 2.两气泡相连状态 3.两气泡分离状态 4.气泡消失状态 二.功能实现 默认状态:用来做一个状态的标识,无需特别处理. 两气泡相连状态:绘制一个固定圆和一个移

  • Android利用二阶贝塞尔曲线实现添加购物车动画详解

    一.引入 其实之前一直以为像饿了么或者是美团外卖那种把商品添加到购物车的动画会很难做,但是实际做起来好像并没有想象中的那么难哈哈. 布局主要使用CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout+TabLayout+ViewPager 动画主要使用二阶贝塞尔曲线与属性动画 消息传递使用EventBus普通事件 二.大致思路 1.如图所示主要有三个点,起点.终点.以及贝塞尔曲线的控制点 2.起点即点击的View的位置,一般来说用如下方式即可

  • Android Flutter利用贝塞尔曲线画一个小海豚

    目录 前言 效果图 实现步骤 总结 前言 贝塞尔曲线的应用填补了计算机绘制与手绘之前的差距,更能表达人想画出的曲线,为了更好的理解万能的贝塞尔曲线,而海豚是我认为在海洋生物中身体曲线最完美的海洋生物,在海洋中游泳速度最高可达80km/h;比驱逐舰速度还快,学习绘制正好学到了贝塞尔曲线,那么我们今天就用贝塞尔曲线画看看能不能画一只可爱的小海豚呢. 效果图 先上效果图: 实现步骤 path路径绘制贝塞尔曲线的方法非常简单,只需要传入控制点即可,二阶就传1个控制点1个终点,三阶就传2个控制点和1个终点

  • Android自定义view贝塞尔曲线

    本文实例为大家分享了Android自定义view贝塞尔曲线,供大家参考,具体内容如下 贝塞尔曲线 以一个简单的贝塞尔曲线为例,二阶曲线原理 贝塞尔曲线很多功能都会用到,比如小火箭发射,再比如淘宝的购物车功能 所幸的是Android有封装好的贝塞尔曲线,我们直接拿过来用就可以了: //二阶贝赛尔  public void quadTo(float x1, float y1, float x2, float y2)  public void rQuadTo(float dx1, float dy1,

随机推荐