PathMeasure的基本使用

PathMeasure顾名思义是Path的一个测量工具类,可以对Path绘制的路径进行测量、裁剪等操作;在使用的时候直接new一个PathMeasure对象就可以了,系统提供了两种类型的构造方法:

//无参构造
public PathMeasure() {
    mPath = null;
    native_instance = native_create(0, false);
}
//有参构造
public PathMeasure(Path path, boolean forceClosed) {
    // The native implementation does not copy the path, prevent it from being GC'd
    mPath = path;
    native_instance = native_create(path != null ? path.readOnlyNI() : 0,
                                forceClosed);
}

公共方法:

//关联一个Path
void setPath(Path path, boolean forceClosed)    
//是否闭合
boolean isClosed()  
//获取Path的长度
float   getLength() 
//跳转到下一个轮廓
boolean nextContour()   
//截取片段
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
//获取指定长度的位置坐标及该点 切线值tangle  
boolean getPosTan(float distance, float[] pos, float[] tan)
//获取指定长度的位置坐标及该点Matrix(矩阵)
boolean getMatrix(float distance, Matrix matrix, int flags) 

如果创建对象的时候使用的是无参构造,可以通过setPath方法关联Path;

如果创建对象的时候使用的是有参构造,在传入第二个参数的时候需要注意;

forceClosed=false :path绘制没有关闭的话就不会进行测量
forceClosed=true:不管path绘制是否关闭,都会进行测量计算长度
//平移画布  viewWidth--->view宽度的一半   viewHeight--->view高度的一半
canvas.translate(viewWidth / 2, viewHeight / 2);
Path path = new Path();
path.lineTo(0, 100);
path.lineTo(100, 100);
path.lineTo(100, 0);

PathMeasure measure1 = new PathMeasure(path, false);
PathMeasure measure2 = new PathMeasure(path, true);
Log.e("TAG", "measure1---false-->" + measure1.getLength());
Log.e("TAG", "measure2---true-->" + measure2.getLength());

canvas.drawPath(path, paint);

看效果path并没有闭合;
PathMeasure的基本使用_第1张图片

但是measure1和measure2由于第二参数的不同,在通过getLength()方法获取path路径长度的时候结果就不一样;

这里写图片描述

measure1所获取的就是path绘制的长度,measure2获取的是path绘制的长度及未必闭合处的长度;所有在使用有参构造的时候第二个参数一般传入false。

nextContour—>跳转到下一个轮廓

Path path = new Path();
//多路径的效果需要关闭硬件加速
path.addRect(-100, -100, 100, 100, Path.Direction.CW);
path.addRect(-50, -50, 50, 50, Path.Direction.CW);

PathMeasure pathMeasure = new PathMeasure(path, false);
//获取下一个路径,有可能没有多个路径了,返回false
float length = pathMeasure.getLength();
boolean nextContour = pathMeasure.nextContour();//获取下一个路径,有可能没有多个路径了,返回false
float length2 = pathMeasure.getLength();
Log.i("TAG", "length1:" + length);
Log.i("TAG", "length2:" + length2);
canvas.drawPath(path, paint);

PathMeasure的基本使用_第2张图片

在使用绘制多条路径的时候需要注意,要关闭硬件加速,可以在AndroidManifest.xml文件中的application中设置

android:hardwareAccelerated="false"

也可以使用View进行设置关闭硬件加速。

getSegment—>截取片段

Path path=new Path();
path.addRect(-200, -200, 200, 200, Path.Direction.CW);
PathMeasure pathMeasure=new PathMeasure(path,false);
canvas.drawPath(path, paint);

Path dst=new Path();
dst.lineTo(-300,-300);
pathMeasure.getSegment(200,600,dst,true);
paint.setColor(Color.RED);
canvas.drawPath(dst, paint);

PathMeasure的基本使用_第3张图片

getSegment()方法可以和Path的lineTo()一起使用的,不过在调用getSegment()方法时,第四个参数是传入一个boolean值,传入false和true的效果是不一样的;

第四个参数false或true:代表该起始点是否是上一个的结束点(是否保持连续性)。

将上面的true改为false
PathMeasure的基本使用_第4张图片

getPosTan—>获取指定长度的位置坐标及该点 切线值tangle

Path path=new Path();
path.addCircle(0,0,300, Path.Direction.CW);

PathMeasure pathMeasure=new PathMeasure(path,false);

float [] pos=new float[2];
float [] tan=new float[2];
pathMeasure.getPosTan(pathMeasure.getLength()/4,pos,tan);
Log.i("TAG", "position:x-"+pos[0]+", y-"+pos[1]);
Log.i("TAG", "tan:x-"+tan[0]+", y-"+tan[1]);
canvas.drawPath(path, paint);

PathMeasure的基本使用_第5张图片

打印的值:

这里写图片描述

Path+PathMeasure自定义搜索放大镜效果

PathMeasure的基本使用_第6张图片
这个效果的话主要涉及到外圆环、放大镜的圆、放大镜的手柄这三个东西的绘制,而这三个东西有涉及到几种状态,普通、放大镜开始执行动画、开始搜索、搜索结束四种状态的绘制;代码如下:

public class SearchView extends View {
    //画笔 
    private Paint mPaint;
    //放大镜和外部圆环
    private Path pathSearch;
    private Path pathCircle;
    //测量Path并截取工具类
    private PathMeasure mMeasure;
    // 动画数值(用于控制动画状态,因为同一时间内只允许有一种状态出现,具体数值处理取决于当前状态)
    private float mAnimatorValue = 0;
    //用于控制动画状态转换
    private Handler mAnimatorHandler;
    //动效过程监听
    private ValueAnimator.AnimatorUpdateListener mUpdateListener;
    private Animator.AnimatorListener mAnimatorListener;
    //当前绘制的状态
    private State mCurrentState = State.NONE;
    //判断是否已经搜索结束
    private boolean isOver = false;

    //视图的所有状态
    public enum State {
        NONE,
        STARTING,
        SEARCHING,
        ENDING
    }

    //控制各个过程的动画
    //开始动画
    private ValueAnimator mStartingAnimator;
    //搜索动画
    private ValueAnimator mSearchingAnimator;
    //搜索结束动画
    private ValueAnimator mEndingAnimator;
    private int count = 0;
    //默认动画的时长
    private int defaultDuration = 2000;
    private int viewHeight;
    private int viewWidth;

    public SearchView(Context context) {
        this(context, null);
    }

    public SearchView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initPaint();
        initPath();
        initListener();
        initHandler();
        initAnimator();

        // 进入开始动画
        mCurrentState = State.STARTING;
        mStartingAnimator.start();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //视图大小改变时会回调
        viewWidth = w;
        viewHeight = h;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //进行绘制
        drawSearch(canvas);
    }

    /**
     * 进行绘制
     *
     * @param canvas 对应的画布
     */
    private void drawSearch(Canvas canvas) {
        mPaint.setColor(Color.WHITE);

        //平移画布
        canvas.translate(viewWidth / 2, viewHeight / 2);
        canvas.drawColor(Color.parseColor("#0082D7"));
        //根据状态进行绘制
        switch (mCurrentState) {
            case NONE:
                //普通状态
                canvas.drawPath(pathSearch, mPaint);
                break;
            case STARTING:
                //开始状态
                mMeasure.setPath(pathSearch, false);
                Path dts = new Path();
                //进行裁剪
                mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dts,true);
                //绘制截取部分
                canvas.drawPath(dts,mPaint);
                break;
            case SEARCHING:
                //搜索状态
                mMeasure.setPath(pathCircle,false);
                Path dts1 = new Path();
                float stop = mMeasure.getLength() * mAnimatorValue;
                float start=(float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * 200f));
                mMeasure.getSegment(start,stop,dts1,true);
                //绘制截取部分
                canvas.drawPath(dts1,mPaint);
                break;
            case ENDING:
                //结束状态
                mMeasure.setPath(pathSearch, false);
                Path dst2 = new Path();
                mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst2, true);
                canvas.drawPath(dst2, mPaint);
                break;
        }
    }

    /**
     * 初始化属性动画
     */
    private void initAnimator() {
        //初始化
        mStartingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
        mSearchingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
        mEndingAnimator = ValueAnimator.ofFloat(1, 0).setDuration(defaultDuration);

        //动画更新监听
        mStartingAnimator.addUpdateListener(mUpdateListener);
        mSearchingAnimator.addUpdateListener(mUpdateListener);
        mEndingAnimator.addUpdateListener(mUpdateListener);

        //动画执行监听
        mStartingAnimator.addListener(mAnimatorListener);
        mSearchingAnimator.addListener(mAnimatorListener);
        mEndingAnimator.addListener(mAnimatorListener);
    }

    /**
     * 初始化Handler 并根据消息更新绘制状态
     */
    private void initHandler() {
        mAnimatorHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (mCurrentState) {
                    case STARTING://开始
                        // 从开始动画转换好搜索动画
                        isOver = false;
                        mCurrentState = State.SEARCHING;
                        //移除开始动画的所有监听
                        mStartingAnimator.removeAllListeners();
                        //开始搜索动画
                        mSearchingAnimator.start();
                        break;
                    case SEARCHING:
                        // 如果搜索未结束 则继续执行搜索动画
                        if (isOver) {
                            mSearchingAnimator.start();
                            count++;
                            // count大于2则进入结束状态
                            if (count > 2) {
                                isOver = true;
                            }
                        } else {
                            // 如果搜索已经结束 则进入结束动画
                            mCurrentState = State.ENDING;
                            mEndingAnimator.start();
                        }
                        break;
                    case ENDING://结束
                        // 从结束动画转变为无状态
                        mCurrentState = State.NONE;
                        break;
                }
            }
        };
    }

    /**
     * 动画监听
     */
    private void initListener() {
        mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mAnimatorValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        };
        mAnimatorListener = new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                //动画开始回调
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束回调
                //发送消息通知动画已经结束
                mAnimatorHandler.sendEmptyMessage(0);
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                //动画取消回调
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                //动画重复回调
            }
        };
    }

    /**
     * 初始化Path
     */
    private void initPath() {
        pathSearch = new Path();
        pathCircle = new Path();
        mMeasure = new PathMeasure();
        // 注意,不要到360度,否则内部会自动优化,测量不能取到需要的数值
        // 放大镜圆环
        RectF oval1 = new RectF(-50, -50, 50, 50);
        pathSearch.addArc(oval1, 45, 359.9f);

        // 外部圆环
        RectF oval2 = new RectF(-100, -100, 100, 100);
        pathCircle.addArc(oval2, 45, -359.9f);

        float[] pos = new float[2];
        // 放大镜把手的位置
        //设置关联Path
        mMeasure.setPath(pathCircle, false);
        //获取坐标
        mMeasure.getPosTan(0, pos, null);
        // 放大镜把手
        pathSearch.lineTo(pos[0], pos[1]);
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint = new Paint();
        //设置样式为描边
        mPaint.setStyle(Paint.Style.STROKE);
        //设置画笔为白色
        mPaint.setColor(Color.WHITE);
        //设置画笔大小
        mPaint.setStrokeWidth(15);
        //设置画帽
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        //设置抗锯齿
        mPaint.setAntiAlias(true);
    }
}

自定义View的代码都在上面,都有表明注释,在布局文件中直接使用就可以了;


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.pathmeasuretest.MainActivity">

    <com.pathmeasuretest.SearchView
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
RelativeLayout>

你可能感兴趣的:(view)