Android 自定义View - PathMeasure

2021-04-13  本文已影响0人  改名_f64e

Api

/*
  设置path
  path : path对象
  forceClosed : 是否闭合(获取长度是需要)
  (例如一个半圆,不闭合长度是周长的一般,闭合是周长的一半+直径)
*/
public void setPath(Path path, boolean forceClosed)

/*
  获取路径的长度(闭合和不闭合的长度不一样)
  如果有两个线段,他们之间不相连,那么长度只是第一个线段的,不是两个线段相加
*/
public float getLength()

/*
  是否是闭合
*/
public boolean isClosed()
/*
  指定到下一段线段
  当有两个线段,他们不相连的时候,通过这个方法,可以指定到下一段线段
*/
public boolean nextContour()

测试代码 : 
        mPaint.strokeWidth = 10f
        mPaint.style = Paint.Style.STROKE
        mPath.moveTo(100f, 500f)
        mPath.lineTo(800f, 500f)

        mPath.moveTo(100f, 800f)
        mPath.lineTo(900f, 800f)
        mPathMeasure.setPath(mPath, false)
        SystemLog.log("length = ${mPathMeasure.length}")
        mPathMeasure.nextContour()
        SystemLog.log("length = ${mPathMeasure.length}")

System.out: length = 700.0
System.out: length = 800.0

1618297945(1).png
/*
  distance: 距离Path起始点的距离,取值范围0 <= distance <= getLength()
  pos[]: distance在path上的坐标,即pos[]存的该点的坐标x,y值
  tan[]: distance在path上对应坐标点在path上的方向,tan[0]是邻边边长,tan[1]是对边边长。
          通过Math.atan2(tan[1], tan[0])*180.0/Math.PI 可以得到正切角的弧度值
*/
public boolean getPosTan(float distance, float pos[], float tan[])
测试代码:
    private val mRegion: Region = Region()
    private val mPaint: Paint = Paint()
    private val mPath: Path = Path()
    private val mPathMeasure: PathMeasure = PathMeasure()
    private val mPos: FloatArray = FloatArray(2)
    private val mTan: FloatArray = FloatArray(2)
    private var mDistance: Float = 0f
    private var mLength: Float = 0f
    private lateinit var mBitmap: Bitmap
    private val mMatrix: Matrix = Matrix()
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

        mPaint.color = Color.GREEN
        mPaint.strokeWidth = 10f
        mPaint.style = Paint.Style.STROKE
        mPath.addCircle(measuredWidth / 2f, measuredHeight / 2f, 200f, Path.Direction.CW)
        mPathMeasure.setPath(mPath, false)
        mLength = mPathMeasure.length
        mPathMeasure.getPosTan(mDistance, mPos, mTan)
        SystemLog.log("resources = $resources")
        mBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_plane)
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.drawPath(mPath, mPaint)
        canvas?.drawBitmap(mBitmap, mMatrix, mPaint)
        mPathMeasure.getPosTan(mDistance, mPos, mTan)
        mMatrix.reset()
        mMatrix.postRotate(
            (atan2(mTan[1].toDouble(), mTan[0].toDouble()) * 180.0 / Math.PI).toFloat() + 90,
            mBitmap.width / 2f, mBitmap.height / 2f
        )
        mMatrix.postTranslate(mPos[0] - mBitmap.width / 2, mPos[1] - mBitmap.height / 2)
        if (mDistance >= mLength) {
            mDistance = 0f
        } else {
            mDistance += 3
        }
        invalidate()
    }
59052b8dc535cec349c62b95a3c21244[00_00_01--00_00_09].gif
/*
  用于得到路径上某一长度的位置以及该位置的正切值的矩阵
  distance: 距离 Path 起点的长度,0 <= distance <= getLength
  matrix : 根据 falgs 封装好的matrix
  flags : 规定哪些内容会存入到matrix中
          PathMeasure.POSITION_MATRIX_FLAG(位置)
          PathMeasure.ANGENT_MATRIX_FLAG(正切)
*/
public boolean getMatrix(float distance, Matrix matrix, int flags)
在 getPosTan()方法中,我们通过数组获取其中的数据,最后还是封装到了matrix 中
这个方法是直接封装到 matrix 中,不需要我们自己封装
同时选中flags : PathMeasure.POSITION_MATRIX_FLAG | PathMeasure.ANGENT_MATRIX_FLAG
/*
  截取path路径上的一段数据
  startD : 开始位置(startD>=0&&startD<=length)
  stopD: 开始位置(stopD>startD &&stopD<=length)
  dst:一个新的path路径,截取后的数据存储在这里
          注意: 截取后的数据是添加到path中,不是替换(覆盖),如果你多次的截取,数据会不断的添加
 startWithMoveTo : 起点是否使用moveTo,
                   true : 起点还是原来path的起点
                   false : 会将截取出来的 Path 片段的起始点移动到 dst 的最后一个点,以保证 dst 的连续性
*/
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
测试代码:
    private val mPaint: Paint = Paint()
    private val mPathCircle: Path = Path()
    private val mPathLine: Path = Path()
    private val mPathMeasureCircle: PathMeasure = PathMeasure()
    private val mPathMeasureLine: PathMeasure = PathMeasure()
    private var mStart: Float = 0f
    private var mLengthCircle: Float = 0f
    private var mLengthLine: Float = 0f
    private var mState: Boolean = true

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        mPaint.strokeWidth = 10f
        mPaint.style = Paint.Style.STROKE
        mPathCircle.addCircle(w / 2f, h / 2f, 200f, Path.Direction.CW)
        mPathLine.moveTo(w / 2f + 200f, h / 2f)
        mPathLine.lineTo(w / 2f + 200f + 200f, h / 2f)
        mPathMeasureCircle.setPath(mPathCircle, false)
        mPathMeasureLine.setPath(mPathLine, false)
        mLengthCircle = mPathMeasureCircle.length
        mLengthLine = mPathMeasureLine.length
    }

    override fun onDraw(canvas: Canvas?) {
        canvas?.drawPath(mPathCircle, mPaint)
        canvas?.drawPath(mPathLine, mPaint)
        if (mState) {
            if (mStart < mLengthCircle) {
                mPathCircle.reset()
                mPathMeasureCircle.getSegment(mStart, mLengthCircle, mPathCircle, true)
                mStart += 4
            } else if (mStart > mLengthCircle && mStart <= (mLengthCircle + mLengthLine)) {
                mPathLine.reset()
                mPathMeasureLine.getSegment(mStart - mLengthCircle, mLengthLine, mPathLine, true)
                mStart += 4
            } else {
                mState = false
            }
        } else {
            if (mStart >= mLengthCircle) {
                mPathLine.reset()
                mPathMeasureLine.getSegment(mStart - mLengthCircle, mLengthLine, mPathLine, true)
                mStart -= 4
            } else if (mStart < mLengthCircle && mStart >= 0) {
                mPathCircle.reset()
                mPathMeasureCircle.getSegment(mStart, mLengthCircle, mPathCircle, true)
                mStart -= 4
            } else {
                mState = true
            }
        }
        invalidate()
    }
a25e050fa91ca61aa6bc1fc6888fbfea[00_00_01--00_00_21].gif
上一篇 下一篇

猜你喜欢

热点阅读