(十五)PathMeasure
来源:互联网 发布:苹果mac自有办公软件 编辑:程序博客网 时间:2024/06/05 10:33
版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
一、PathMeasure 基础
从名字就可以看出, PathMeasure 是一个用来测量 Path 的类。
1.构造方法 PathMeasure()
创建一个空的 PathMeasure。
2.构造方法 PathMeasure(Path path, boolean forceClosed)
创建 PathMeasure 并关联一个指定的Path(Path需要已经创建完成)。
这边需要强调的是参数 forceClosed : 当 Path 有调用 close() 方法的时候,forceClosed 为 true 或 false 没有区别。当 Path 没有调用 close() 方法的,且 forceClosed 为 true 的时候,调用 PathMeasure 的 getLength() 方法,长度会增加关闭区域的长度。
有调用 close() 方法:
Path path = new Path(); path.lineTo(0,200); path.lineTo(200,200); path.lineTo(200,0); path.close(); PathMeasure measure1 = new PathMeasure(path,false); PathMeasure measure2 = new PathMeasure(path,true); Log.e(TAG, "forceClosed=false length = "+measure1.getLength()); Log.e(TAG, "forceClosed=true length = "+measure2.getLength()); canvas.drawPath(path,mDeafultPaint);
效果:
日志输出:
E/MyView: forceClosed=false length = 800.0E/MyView: forceClosed=true length = 800.0
没有调用 close() 方法:
Path path = new Path(); path.lineTo(0,200); path.lineTo(200,200); path.lineTo(200,0); PathMeasure measure1 = new PathMeasure(path,false); PathMeasure measure2 = new PathMeasure(path,true); Log.e(TAG, "forceClosed=false length = "+measure1.getLength()); Log.e(TAG, "forceClosed=true length = "+measure2.getLength()); canvas.drawPath(path,mDeafultPaint);
效果:
日志输出:
E/MyView: forceClosed=false length = 600.0E/MyView: forceClosed=true length = 800.0
3.getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
截取片段。
startD : 截取片段的起始长度(取值范围在 0 ~ Path 的长度之间)
stopD : 截取片段的终止长度(取值范围在 0 ~ Path 的长度之间),切必须大于 startD,否则取不到
dst : 截取后片段会赋值给 dst
startWithMoveTo : 起始点是否使用 moveTo 方法(一般情况为 true)
如果 startWithMoveTo 设置为 false的话,dst 中保存的 Path 是就会不断被添加的,而不是被覆盖,新增的片段会从上一次 Path 终点开始计算,这样所有截取的 Path 就会连续起来。
这边比较难理解的是第四个参数 startWithMoveTo。为了方便看效果,这里先建立了一个坐标系,并把中心移到屏幕中心。
startWithMoveTo 为 true:
// 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); // 创建Path并添加了一个矩形 path.addRect(-200, -200, 200, 200, Path.Direction.CW); Path dst = new Path(); // 将 Path 与 PathMeasure 关联 PathMeasure measure = new PathMeasure(path, false); // 截取一部分存入dst中,并使用 moveTo 保持截取得到的 Path 第一个点的位置不变 measure.getSegment(200, 600, dst, true); canvas.drawPath(path,mPaint); canvas.drawPath(dst, mDeafultPaint);
效果:
为 dst 添加一条初始的线段。
// 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); // 创建Path并添加了一个矩形 path.addRect(-200, -200, 200, 200, Path.Direction.CW); Path dst = new Path(); dst.lineTo(100, 100); // 将 Path 与 PathMeasure 关联 PathMeasure measure = new PathMeasure(path, false); // 截取一部分存入dst中,并使用 moveTo 保持截取得到的 Path 第一个点的位置不变 measure.getSegment(200, 600, dst, true); canvas.drawPath(path,mPaint); // 绘制 dst canvas.drawPath(dst, mDeafultPaint);
效果:
startWithMoveTo 为 false:
// 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); // 创建Path并添加了一个矩形 path.addRect(-200, -200, 200, 200, Path.Direction.CW); Path dst = new Path(); // 将 Path 与 PathMeasure 关联 PathMeasure measure = new PathMeasure(path, false); // 截取一部分存入dst中,不使用 moveTo measure.getSegment(200, 600, dst, false); canvas.drawPath(path,mPaint); canvas.drawPath(dst, mDeafultPaint);
效果:
startWithMoveTo 为 false,截取的 Path 没有被 moveTo 到起始位置,所以默认 moveTo 到(0, 0)所以导致了绘制的红线从原点开始
为 dst 添加一条初始的线段。
// 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); // 创建Path并添加了一个矩形 path.addRect(-200, -200, 200, 200, Path.Direction.CW); Path dst = new Path(); dst.lineTo(100, 100); // 将 Path 与 PathMeasure 关联 PathMeasure measure = new PathMeasure(path, false); // 截取一部分存入dst中,并使用 moveTo 保持截取得到的 Path 第一个点的位置不变 measure.getSegment(200, 600, dst, false); canvas.drawPath(path,mPaint); // 绘制 dst canvas.drawPath(dst, mDeafultPaint);
效果:
4.nextContour()
获取下一条曲线轮廓,当 Path 里面有多条轮廓的时候,会依次取获取。
// 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); Path path1 = new Path(); Path path2 = new Path(); // 添加小矩形 path1.addRect(-100, -100, 100, 100, Path.Direction.CW); // 添加大矩形 path2.addRect(-200, -200, 200, 200, Path.Direction.CW); path.op(path1,path2, Path.Op.XOR); canvas.drawPath(path,mDeafultPaint); PathMeasure measure = new PathMeasure(path, false); float len1 = measure.getLength(); // 跳转到下一条路径 measure.nextContour(); float len2 = measure.getLength(); Log.d(TAG,"len1 = "+len1); Log.d(TAG,"len2 = "+len2);
注:这里必须采用自己设置线条的叠加模式,否则会被覆盖。
效果:
日志输出:
D/MyView: len1 = 1600.0D/MyView: len2 = 800.0
5.getPosTan(float distance, float[] pos, float[] tan)
获取指定长度的位置坐标及该点切线值 tangle。
distance: 位置距起始点的距离
pos: 指定位置的坐标,长度为2 (x==pos[0], y==pos[1])
tan: 这个值长度也是2,比较复杂,还是直接看后面效果
效果:
public class MyView1 extends View { private float currentValue = 0; // 用于纪录当前的位置,取值范围[0,1]映射Path的整个长度 private float[] pos; // 当前点的实际位置 private float[] tan; // 当前点的tangent值,用于计算图片所需旋转的角度 private Bitmap mBitmap; // 箭头图片 private Matrix mMatrix; // 矩阵,用于对图片进行一些操作 private Paint mDeafultPaint; private int mViewWidth; private int mViewHeight; private Paint mPaint; public MyView1(Context context) { super(context); init(context); } private void init(Context context) { pos = new float[2]; tan = new float[2]; BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; // 缩放图片 mBitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.arrow, options); mMatrix = new Matrix(); mDeafultPaint = new Paint(); mDeafultPaint.setColor(Color.RED); mDeafultPaint.setStrokeWidth(5); mDeafultPaint.setStyle(Paint.Style.STROKE); mPaint = new Paint(); mPaint.setColor(Color.DKGRAY); mPaint.setStrokeWidth(2); mPaint.setStyle(Paint.Style.STROKE); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mViewWidth = w; mViewHeight = h; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(Color.WHITE); // 平移坐标系 canvas.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); Path path = new Path(); // 创建 Path path.addCircle(0, 0, 200, Path.Direction.CW); // 添加一个圆形 PathMeasure measure = new PathMeasure(path, false); // 创建 PathMeasure currentValue += 0.005; // 计算当前的位置在总长度上的比例[0,1] if (currentValue >= 1) { currentValue = 0; } // 获取当前位置的坐标以及趋势 measure.getPosTan(measure.getLength() * currentValue, pos, tan); // 重置Matrix mMatrix.reset(); // 计算图片旋转角度 float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // 旋转图片 mMatrix.postRotate(degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2); // 将图片绘制中心调整到与当前点重合 mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2); canvas.drawPath(path, mDeafultPaint); canvas.drawBitmap(mBitmap, mMatrix, mDeafultPaint); invalidate(); }}
核心代码:
// 获取当前位置的坐标以及趋势 measure.getPosTan(measure.getLength() * currentValue, pos, tan); // 重置Matrix mMatrix.reset(); // 计算图片旋转角度 float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); // 旋转图片 mMatrix.postRotate(degrees, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2); // 将图片绘制中心调整到与当前点重合 mMatrix.postTranslate(pos[0] - mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight() / 2);
对与移动图片不仅进行了平移,也进行了旋转。平移这边是采用后乘平移 postTranslate,这个平移是相对于坐标点(0, 0)。这边比较难懂的是旋转的角度,
以(0, 0)为圆心,新建半径为 1 的圆。直线旋转后与圆相交的点的 x, y 坐标即 getPosTan 方法中 tan 的两个值。
利用反 tan 函数,可以登出旋转角度为:Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI。
6.getMatrix(float distance, Matrix matrix, int flags)
获取指定长度的位置坐标及该点 Matrix (矩阵)
distance: 位置距起始点的距离
matrix: 指定位置的矩阵
flags : 标志,flags 有两个选项值,表示矩阵关联的关系
public static final int POSITION_MATRIX_FLAG = 0x01; // must match flags in SkPathMeasure.h //位置信息 public static final int TANGENT_MATRIX_FLAG = 0x02; // must match flags in SkPathMeasure.h //切边信息
核心代码:
// 获取当前位置的坐标以及趋势的矩阵 measure.getMatrix(measure.getLength() * currentValue, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG); // 将图片绘制中心调整到与当前点重合(注意:此处是前乘pre) mMatrix.preTranslate(-mBitmap.getWidth() / 2, -mBitmap.getHeight() / 2);
把 getPosTan 的核心代码替换成这个也能实现一样的功能。
注:这边采用平移前乘矩阵,且由于 getMatrix 标志用到了位置信息,所以平移的时候相对位置点是路径上选中的那个点。
三、getSegment 实现的进度圆圈
代码比较简单,就不再具体注释了。
public class LoadingView1 extends View { private Path mPath; private Paint mPaint; private PathMeasure mPathMeasure; private float mAnimatorValue; private Path mDst; private float mLength; public LoadingView1(Context context) { this(context,null); } public LoadingView1(Context context, AttributeSet attrs) { this(context, attrs,0); } public LoadingView1(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPathMeasure = new PathMeasure(); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(5); mPath = new Path(); mPath.addCircle(400, 400, 100, Path.Direction.CW); mPathMeasure.setPath(mPath, true); mLength = mPathMeasure.getLength(); mDst = new Path(); final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { mAnimatorValue = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); valueAnimator.setDuration(2000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); mDst.reset(); // 硬件加速的BUG mDst.lineTo(0,0); /*float stop = mLength * mAnimatorValue; mPathMeasure.getSegment(0, stop, mDst, true);*/ float stop = mLength * mAnimatorValue; float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength)); mPathMeasure.getSegment(start, stop, mDst, true); canvas.drawPath(mDst, mPaint); }}
四、水波纹小船改变方向
对上一节的小船效果进行优化,是小船的船头随波浪上下起伏。
主要是在绘制小船的时候,运用 getPosTan 或者 getMatrix 对小船进行旋转。
getPosTan 旋转
mPathMeasure = new PathMeasure(path, false); //方案一,getPosTan mPathMeasure.getPosTan(width / 2 + length * (1 - faction), pos, tan); float degrees = (float) (Math.atan2(tan[1], tan[0])*180f / Math.PI); mMatrix.postRotate(degrees, mBitmap.getWidth()/2, mBitmap.getHeight() / 2); mMatrix.postTranslate(pos[0]- mBitmap.getWidth() / 2, pos[1] - mBitmap.getHeight()); canvas.drawBitmap(mBitmap, mMatrix, paint);
getMatrix 旋转
mPathMeasure = new PathMeasure(path, false); mPathMeasure.getMatrix(width / 2 + length * (1 - faction), mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG); mMatrix.preTranslate(- mBitmap.getWidth() / 2, - mBitmap.getHeight()); canvas.drawBitmap(mBitmap, mMatrix, paint);
- (十五)PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- PathMeasure
- Android PathMeasure工具类笔记(仿UC loaddingview)
- PathMeasure用法
- 仿支付宝支付成功打勾动画(关于PathMeasure你该知道的东西)
- 59.自定义View练习(四)使用PathMeasure简单模仿系统ProgressBar
- Android PathMeasure使用
- Android Path,PathMeasure
- PathMeasure的相关文章
- PathMeasure加载完成
- Path&PathMeasure完全解析
- PathMeasure 轨迹动画神器
- PathMeasure的基本使用
- Android Path 之 PathMeasure
- HDU
- C语言排序之堆排序篇
- Qt Creator使用
- 105. Construct Binary Tree from Preorder and Inorder Traversal
- 将数据库中的数据导入Solr索引库
- (十五)PathMeasure
- CT图之cnn
- [PHP]PhpStorm搭建PHP环境及PHP.ini丢失问题
- Marjar Cola
- Java键盘事件
- [USACO3.3]商店购物 Shopping Offers
- 实现多系统网络yum服务器和客户端配置
- JavaScript的字符串定义及实例
- PHP做中文分词技术