高级UI-PathMeasure 使用(路径绘制) 小船在波浪上运动 demo
2018-02-24 17:20
387 查看
目录
通过path画出矩形
通过path画出小船跟随波浪运动
使用DashPathEffect 进行路径绘制
基础介绍看这两个. 讲的都很不错
https://www.jianshu.com/p/9ee023755ce8
https://www.jianshu.com/p/3efa5341abcc
getSegment 的使用
先写个基本使用的demo
代码如下:
这里我需要记住一个技巧 很多数据可以通过计算变成 0~1 或者-1~1区间 这样便于计算与理解
那么如何实现下面效果?
同样通过getSegment
通过计算改变起始值 这里的原理是当画到一半的时候 不断改变起始值 最终让起始点和终点相等,即可实现.
代码
在上一个 贝塞尔曲线 的波浪demo下继续写:
具体使用
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0907/3429.html
主要的原理:
我们可以把DashPathEffect的第一个参数(float数组)只填入两个值,都是path的总长度length,那么按照上面对DashPathEffect的解释,第一次绘制一条实线就已经完全绘制完了,间隔的空白区间得不到绘制的机会。事实上这样绘制完全不能产生虚线效果,跟不设置PathEffect是一样的。
但是我们注意第三个参数即起始位置的偏移量现在是为0的。如果我们不为0呢?
比如为100,那么第一次绘制实线就会跳过100的距离,第一次的实线就只能绘制length-100的长度,那么空白区域就可以绘制100的长度,但是你看不见空白,所以我们只会感觉到绘制了一条length-100的路径。
如果你按照我们的思路去做实验,那么很快你就会想到,把这个偏移量也设置成length,那么第一次的实线区间将完全得不到绘制,而直接进入空白区间,而我们的空白区间总长度也是length,因此它占用了全部的绘制区间,所以此时什么也看不到。如果空白区间小于length的话,是可以看到一点实线的(因为空白区间完了紧接着就是实线了)。
所以,我们可以设置一个百分比,取名叫phase,phase的增长是从0 .0-1.0,如果我们利用属性动画来改变它,然后根据它动态的构造一个这样的DashPathEffect:
这样就能产生跟踪绘制的效果。
通过path画出矩形
通过path画出小船跟随波浪运动
使用DashPathEffect 进行路径绘制
基础介绍看这两个. 讲的都很不错
https://www.jianshu.com/p/9ee023755ce8
https://www.jianshu.com/p/3efa5341abcc
getSegment 的使用
先写个基本使用的demo
public class MyView extends View { private static final String TAG = "MyView"; private int mViewWidth; private int mViewHeight; private Paint mDeafultPaint; private Paint mPaint; public MyView(Context context) { super(context); init(); } private void init() { 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.translate(mViewWidth/2,mViewHeight/2); // 画坐标线 canvas.drawLine(-canvas.getWidth(),0,canvas.getWidth(),0,mPaint); canvas.drawLine(0,-canvas.getHeight(),0,canvas.getHeight(),mPaint); //testForceClosed(canvas); //testGetSegment(canvas); //testGetSegmentMoveTo(canvas); testNextContour(canvas); } private void testNextContour(Canvas canvas) { Path path = new Path(); Path path1 = new Path(); Path path2 = new Path(); // 添加小矩形 path1.addRect(-100, -100, 100, 100, Path.Direction.CW); // 添加大矩形 //path.addRect(-200, 200, 200, 600, 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); } private void testGetSegmentMoveTo(Canvas canvas) { Path path = new Path(); // 创建Path并添加了一个矩形 path.addRect(-200, -200, 200, 200, Path.Direction.CW); Path dst = new Path(); dst.lineTo(-300, -300); // 将 Path 与 PathMeasure 关联 PathMeasure measure = new PathMeasure(path, false); // 截取一部分存入dst中,并使用 moveTo 保持截取得到的 Path 第一个点的位置不变 //measure.getSegment(200, 600, dst, false); measure.getSegment(0, 600, dst, true); canvas.drawPath(path,mPaint); // 绘制 dst canvas.drawPath(dst, mDeafultPaint); } //使用getSegment private void testGetSegment(Canvas canvas) { 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, false); canvas.drawPath(path,mPaint); // 绘制 dst canvas.drawPath(dst, mDeafultPaint); } private void testForceClosed(Canvas canvas) { 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); } }
01 通过path画出矩形
效果如下代码如下:
这里我需要记住一个技巧 很多数据可以通过计算变成 0~1 或者-1~1区间 这样便于计算与理解
package android.mybzdemo.pathMeasure; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.graphics.RectF; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; /** * @author liuml * @explain 利用getSegment 画 * @time 2018/2/6 16:55 */ public class MyPathMeasureBase extends View { private Path mPath; private Paint mPaint; private PathMeasure mPathMeasure; private float mAnimatorValue; private Path mDst; private float mLength; public MyPathMeasureBase(Context context) { super(context); } public MyPathMeasureBase(Context context, @Nullable AttributeSet attrs) { super(context, attrs); //---画笔 //ANTI_ALIAS_FLAG绘制时不允许使用反锯齿的标志。 mPaint =new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.RED); mPaint.setStrokeWidth(5); //---路径 mPath = new Path(); //画个矩形 RectF rect = new RectF(200, 200, 500, 500); mPath.addRect(rect, Path.Direction.CW); //---路径测量 mPathMeasure = new PathMeasure(); //和path关联 true getLength时候是包括闭合的 mPathMeasure.setPath(mPath,true); mLength = mPathMeasure.getLength(); mDst = new Path(); //---动画 //这里有个技巧 把所有大数 或者小数 全部改造成0-1 或者-1 到1 这个区间 这样就好操作了 final ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mAnimatorValue = (float) animation.getAnimatedValue(); postInvalidate(); } }); valueAnimator.setDuration(2000); valueAnimator.setRepeatCount(ValueAnimator.INFINITE); valueAnimator.start(); } public MyPathMeasureBase(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @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); canvas.drawPath(mDst, mPaint); } }
那么如何实现下面效果?
同样通过getSegment
//通过不断添加结束点 来画出矩形 float stop = mLength * mAnimatorValue; float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength)); mPathMeasure.getSegment(start, stop, mDst, true);
通过计算改变起始值 这里的原理是当画到一半的时候 不断改变起始值 最终让起始点和终点相等,即可实现.
02 通过path画出小船跟随波浪运动
效果代码
package android.mybzdemo.pathMeasure; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.mybzdemo.R; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.Log; import android.view.View; import android.view.animation.LinearInterpolator; /** * @author liuml * @explain pathMeasure 使用 实现小船在波浪上面 * @time 2018/2/6 16:55 */ public class BoatView2 extends View { private static final String TAG = "MyBzView3"; ValueAnimator animator; private Path mPath; private Paint mPaint; private static final int INT_WAVE_LENGTH = 1000;//波长 private int waveHeight = 60;// private int mDeltax;//运动的值 private Bitmap boatBmp;//小船 private PathMeasure pathMeasure; private float[] pos; private float[] tan; private Matrix mMatrix; private float faction = 0; private float length; private float[] test; public BoatView2(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setColor(Color.BLUE); //用这种风格绘制的几何图形和文本将会被填充 mPaint.setStyle(Paint.Style.FILL_AND_STROKE); //小船 BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2;//小船压缩一半 boatBmp = BitmapFactory.decodeResource(getResources(), R.drawable.timg, options); pos = new float[2]; tan = new float[2]; test = new float[2]; mMatrix = new Matrix(); mPath = new Path(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //清除路径上的任何线条和曲线,使其为空。 mPath.reset(); int orgin = 800; int halfLength = INT_WAVE_LENGTH / 2; //起始点移动到左边屏幕的左边,根据不断改变起始点 动态的改变位置 mDeltax =0 让波浪不动 mPath.moveTo(-INT_WAVE_LENGTH + mDeltax * INT_WAVE_LENGTH, orgin); // Log.d(TAG, "onDraw: getWidth= " + getWidth()); //从起始点开始 int number = 0; for (int i = -INT_WAVE_LENGTH; i < getWidth() + INT_WAVE_LENGTH; i += INT_WAVE_LENGTH) { //画一个圆点 这个圆点在交界处 // Paint paint = new Paint(); // paint.setColor(Color.RED); // canvas.drawCircle(-INT_WAVE_LENGTH + halfLength * number + (faction * INT_WAVE_LENGTH), orgin, 20, paint); // number++; //使用rQuadTo 是相对位移 不用重新设置起始点 mPath.rQuadTo(halfLength / 2, waveHeight, halfLength, 0); mPath.rQuadTo(halfLength / 2, -waveHeight, halfLength, 0); } //上面是画曲线 //下面是画出界面左边和右边的一条线 这样就可以闭合 mPath.lineTo(getWidth(), getHeight()); mPath.lineTo(0, getHeight()); mPath.close();//让线闭合 //把线画出来 canvas.drawPath(mPath, mPaint); //下面画出小船 //先测量path pathMeasure = new PathMeasure(mPath, false); //先获取长度 length = pathMeasure.getLength(); //将距离标记为0 <= distance <= getLength(),然后进行计算 对应的位置和切线。如果没有路径,返回false, //或者指定一条零长度的路径,在这种情况下,位置和切线变。 获取某一个点的tan boolean posTan = pathMeasure.getPosTan(length * faction, pos, tan); //方案一: 自己计算 // Log.d(TAG,"pos[0] = " + pos[0] + "pos[1] = " +pos[1]); // Log.d(TAG,"tan[0] = " + tan[0] + "tan[1] = " +tan[1]); if (posTan) { // 方案一 :自己计算 // 将tan值通过反正切函数得到对应的弧度,在转化成对应的角度度数 /* float degrees = (float) (Math.atan2(tan[1],tan[0])*180f / Math.PI); mMatrix.postRotate(degrees, boatBmp.getWidth()/2, boatBmp.getHeight() / 2);//旋转 mMatrix.postTranslate(pos[0]- boatBmp.getWidth() / 2,pos[1] - boatBmp.getHeight());//平移 canvas.drawBitmap(boatBmp,mMatrix,mPaint);*/ } //方案二:通过api获取matrix 获取指定长度的位置坐标及该点Matrix //在这里做一些操作 如果这个点超过屏幕就让他返回来 float v = 1080 * faction; float tmp; if (faction > 0.6) { tmp = 1 - faction; } else { tmp = faction; } // pathMeasure.getMatrix(length * faction, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG); //需要减去小船本身的宽高 向上平移 mMatrix.preTranslate(boatBmp.getWidth() / 2, -boatBmp.getHeight()); canvas.drawBitmap(boatBmp, mMatrix, mPaint); } /** * 开启动画 */ public void startAnimator() { /* animator = ValueAnimator.ofInt(0, INT_WAVE_LENGTH); animator.setDuration(1000); //设置为线性的 animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(ValueAnimator.INFINITE);//无限循环 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDeltax = (int) animation.getAnimatedValue(); //根据不断改变起始点 动态的改变位置 postInvalidate(); } }); animator.start();*/ //这里就之前说的 技巧 利用0到1的区间 转化数值 animator = ValueAnimator.ofFloat(0, 1); animator.setDuration(10000); animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(ValueAnimator.INFINITE); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { faction = (float) animation.getAnimatedValue(); Log.d(TAG, "onAnimationUpdate: =================faction =" + faction); Log.d(TAG, "onAnimationUpdate: =================length =" + length); postInvalidate(); } }); animator.start(); } public void stopanimator() { animator.cancel(); } }
在上一个 贝塞尔曲线 的波浪demo下继续写:
03 使用DashPathEffect 进行路径绘制
Effect效果具体使用
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0907/3429.html
主要的原理:
PathEffect effect = new DashPathEffect(new float[] { length, length }, 0);
我们可以把DashPathEffect的第一个参数(float数组)只填入两个值,都是path的总长度length,那么按照上面对DashPathEffect的解释,第一次绘制一条实线就已经完全绘制完了,间隔的空白区间得不到绘制的机会。事实上这样绘制完全不能产生虚线效果,跟不设置PathEffect是一样的。
但是我们注意第三个参数即起始位置的偏移量现在是为0的。如果我们不为0呢?
比如为100,那么第一次绘制实线就会跳过100的距离,第一次的实线就只能绘制length-100的长度,那么空白区域就可以绘制100的长度,但是你看不见空白,所以我们只会感觉到绘制了一条length-100的路径。
如果你按照我们的思路去做实验,那么很快你就会想到,把这个偏移量也设置成length,那么第一次的实线区间将完全得不到绘制,而直接进入空白区间,而我们的空白区间总长度也是length,因此它占用了全部的绘制区间,所以此时什么也看不到。如果空白区间小于length的话,是可以看到一点实线的(因为空白区间完了紧接着就是实线了)。
所以,我们可以设置一个百分比,取名叫phase,phase的增长是从0 .0-1.0,如果我们利用属性动画来改变它,然后根据它动态的构造一个这样的DashPathEffect:
new DashPathEffect(new float[] { length, length }, length - phase * length);
这样就能产生跟踪绘制的效果。
相关文章推荐
- Android UI SurfaceView的使用-绘制单个图型或多个图形
- iOS 使用贝塞尔曲线绘制路径
- iOS之UI高级---CALayer和CAAnimation的混合使用
- Android 使用贝塞尔和PathMeasure绘画心形路径
- c#画笔Pen使用路径绘制图形
- iOS开发UI高级—15UITabBarController生命周期(使用storyoard搭建)
- iOS开发UI高级—38Quartz2D使用(绘制基本图形)
- 使用j2me提供的低级UI:CanvasImage和Graphics对象来绘制九宫格,主要思路如下:
- HTML5 Canvas使用路径——绘制圆形
- html Demo工具类:网页使用ie另存为htm文件时,css样式文件的图片路径不对,进行修改,并下载图片
- ng中使用ui-route配置demo
- lwuit中如何直接使用高级ui控件?
- Android中使用Vectors(2)绘制优美的路径动画
- 使用josm绘制室内地图及路径(二)
- UI进阶--Quartz2D绘制图形的基本使用
- HTML5 绘制图形canvas 使用路径(一)
- QML让圆形物体按照圆形轨迹运动和color使用rgba值的Demo
- Android高级UI ACharEngine实现图形绘制
- Android UI SurfaceView的使用-绘制组合图型,并使其移动
- 使用原生JavaScript的Canvas实现拖拽式图形绘制,支持画笔、线条、箭头、三角形、矩形、平行四边形、梯形以及多边形和圆形,不依赖任何库和插件,有演示demo