亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android自定義View繪制貝塞爾曲線實(shí)現(xiàn)流程

 更新時(shí)間:2022年11月01日 09:53:55   作者:幸大叔  
貝塞爾曲線的本質(zhì)是通過數(shù)學(xué)計(jì)算的公式來繪制平滑的曲線,分為一階,二階,三階及多階。但是這里不講數(shù)學(xué)公式和驗(yàn)證,那些偉大的數(shù)學(xué)家已經(jīng)證明過了,所以就只講講Android開發(fā)中的運(yùn)用吧

前言

對(duì)于Android開發(fā),實(shí)現(xiàn)貝塞爾曲線還是比較方便的,有對(duì)應(yīng)的API供你調(diào)用。由于一階貝塞爾曲線就是一條直線,實(shí)際沒啥多大用處,因此,下面主要講解二階和三階。

二階貝塞爾曲線

在Android中,使用quadTo來實(shí)現(xiàn)二階貝塞爾

        path.reset()
        path.moveTo(startX, startY)
        path.quadTo(currentX, currentY, endX, endY)
        canvas.drawPath(path, curvePaint)

startX和startY,endX和endY為兩個(gè)固定點(diǎn),currentX和currentY就是控制點(diǎn),通過改變控制點(diǎn)的位置來改變二階貝塞爾曲線的形狀。

a點(diǎn)和b點(diǎn)就是固定點(diǎn),c點(diǎn)是控制點(diǎn),我們可以改變c點(diǎn)的位置來改變曲線的形狀。

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                currentX = event.x
                currentY = event.y
                postInvalidate()
            }
        }
        return true
    }

三階貝塞爾曲線

在Android中,使用cubicTo來實(shí)現(xiàn)三階貝塞爾

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        path.reset()
        path.moveTo(startX, startY)
        path.cubicTo(fixedX1, fixedY1, fixedX2, fixedY2, endX, endY)
        canvas.drawPath(path, curvePaint)
        //繪制輔助線
        drawHelpLine(canvas)
    }
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                //divideLine區(qū)分觸摸點(diǎn)是左邊還是右邊
                if (event.x < divideLine) {
                    fixedX1 = event.x
                    fixedY1 = event.y
                } else {
                    fixedX2 = event.x
                    fixedY2 = event.y
                }
                postInvalidate()
            }
        }
        return true
    }

其中,startX和startY,endX和endY為兩個(gè)固定點(diǎn),fixedX1和fixedY1,fixedX2和fixedY2分別為兩個(gè)控制點(diǎn),通過改變控制點(diǎn)的位置來改變?nèi)A貝塞爾曲線的形狀。

a點(diǎn)和b點(diǎn)就是固定點(diǎn),c點(diǎn)和d點(diǎn)是控制點(diǎn),我們可以改變c點(diǎn)或d點(diǎn)的位置來改變曲線的形狀。

OK,貝塞爾曲線的基礎(chǔ)到此就講完了,下面來個(gè)實(shí)戰(zhàn),體驗(yàn)一下貝塞爾曲線的絲滑吧!

關(guān)于貝塞爾曲線,最典型的應(yīng)用就是波浪球了,那咱們也來整一個(gè),先上圖

首先裁剪一下畫布,變?yōu)閳A形

        val circlePath = Path()
        circlePath.addCircle(width / 2f, height / 2f, width / 2f, Path.Direction.CW)
        canvas.clipPath(circlePath)

Path.Direction.CW:沿順時(shí)針方向繪制,Path.Direction.CCW:沿逆時(shí)針方向繪制

以View為中心,畫圓

canvas.drawCircle(width / 2f, height / 2f, width / 2f, circularPaint)

利用二階貝塞爾,繪制波浪,起點(diǎn)為屏幕外,circleLen為曲線1/4周期長度

private val startPoint = Point(-4 * circleLen, 0)

根據(jù)進(jìn)度改變起點(diǎn)坐標(biāo)的y值,控制點(diǎn)為曲線的頂部和底部,循環(huán)繪制,然后構(gòu)建曲線之下的封閉區(qū)域,填充

        //根據(jù)進(jìn)度改變起點(diǎn)坐標(biāo)的y值
        startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
        //移動(dòng)到起點(diǎn)
        wavePath.moveTo(startPoint.x.toFloat(), startPoint.y.toFloat())
        var j = 1
        //循環(huán)繪制曲線
        for (i in 1..8) {
            val controlX = (startPoint.x + circleLen * j).toFloat()
            //波頂和波底
            val controlY =
                if (i % 2 == 0) (startPoint.y + waveHeight).toFloat() else (startPoint.y - waveHeight).toFloat()
            //二階貝塞爾
            wavePath.quadTo(
                controlX,
                controlY,
                (startPoint.x + circleLen * 2 * i).toFloat(),
                startPoint.y.toFloat()
            )
            j += 2
        }
        //繪制封閉的區(qū)域
        wavePath.lineTo(width.toFloat(), height.toFloat())
        wavePath.lineTo(startPoint.x.toFloat(), height.toFloat())
        wavePath.lineTo(startPoint.x.toFloat(), startPoint.y.toFloat())
        wavePath.close()
        canvas.drawPath(wavePath, wavePaint)
        wavePath.reset()
        //走完一周回到原點(diǎn)
        startPoint.x =
            if (startPoint.x + translateX >= 0) -circleLen * 4 else startPoint.x + translateX

這里是設(shè)置每隔100ms,進(jìn)度加一

        progress = if (progress >= 100) 0 else progress + 1
        postInvalidateDelayed(100)

全部代碼如下

class ProgressBallView : View {
    //曲線1/4周期的長度
    private val circleLen = DensityUtils.dp2px(context, 53)
    //曲線高度
    private val waveHeight = DensityUtils.dp2px(context, 27)
    //默認(rèn)的長寬值
    private val defaultSize = DensityUtils.dp2px(context, 300)
    //進(jìn)度
    private var progress = 0
    //平移的長度
    private val translateX = circleLen / 4
    //圓形Paint
    private val circularPaint = Paint()
    //波浪Paint
    private val wavePaint = Paint()
    //波浪的路徑
    private val wavePath = Path()
    //曲線的起始坐標(biāo)
    private val startPoint = Point(-4 * circleLen, 0)
    constructor(context: Context) : super(context)
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {
        initPaint()
    }
    private fun initPaint() {
        with(circularPaint) {
            isAntiAlias = true
            color = Color.GRAY
        }
        with(wavePaint) {
            isAntiAlias = true
            color = Color.RED
        }
    }
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var viewWidth = measureView(widthMeasureSpec)
        var viewHeight = measureView(heightMeasureSpec)
        //取最小的,作為長寬
        viewWidth = min(viewWidth, viewHeight)
        viewHeight = viewWidth
        setMeasuredDimension(viewWidth, viewHeight)
    }
    private fun measureView(measureSpec: Int): Int {
        val mode = MeasureSpec.getMode(measureSpec)
        return if (mode == MeasureSpec.AT_MOST || mode == MeasureSpec.EXACTLY) {
            MeasureSpec.getSize(measureSpec)
        } else {
            defaultSize
        }
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //裁剪畫布為圓形
        cutCanvas(canvas)
        //繪制圓形
        drawRound(canvas)
        //繪制波浪
        drawWave(canvas)
        //自動(dòng)增長進(jìn)度
        autoGrow()
    }
    //進(jìn)度從0-100,自動(dòng)增長
    private fun autoGrow() {
        progress = if (progress >= 100) 0 else progress + 1
        postInvalidateDelayed(100)
    }
    //裁剪畫布為圓形
    private fun cutCanvas(canvas: Canvas) {
        val circlePath = Path()
        circlePath.addCircle(width / 2f, height / 2f, width / 2f, Path.Direction.CW)
        canvas.clipPath(circlePath)
    }
    //繪制圓形
    private fun drawRound(canvas: Canvas) {
        canvas.drawCircle(width / 2f, height / 2f, width / 2f, circularPaint)
    }
    //繪制波浪
    private fun drawWave(canvas: Canvas) {
        //根據(jù)進(jìn)度改變起點(diǎn)坐標(biāo)的y值
        startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
        //移動(dòng)到起點(diǎn)
        wavePath.moveTo(startPoint.x.toFloat(), startPoint.y.toFloat())
        var j = 1
        //循環(huán)繪制曲線
        for (i in 1..8) {
            val controlX = (startPoint.x + circleLen * j).toFloat()
            //波頂和波底
            val controlY =
                if (i % 2 == 0) (startPoint.y + waveHeight).toFloat() else (startPoint.y - waveHeight).toFloat()
            //二階貝塞爾
            wavePath.quadTo(
                controlX,
                controlY,
                (startPoint.x + circleLen * 2 * i).toFloat(),
                startPoint.y.toFloat()
            )
            j += 2
        }
        //繪制封閉的區(qū)域
        wavePath.lineTo(width.toFloat(), height.toFloat())
        wavePath.lineTo(startPoint.x.toFloat(), height.toFloat())
        wavePath.lineTo(startPoint.x.toFloat(), startPoint.y.toFloat())
        wavePath.close()
        canvas.drawPath(wavePath, wavePaint)
        wavePath.reset()
        //走完一周回到原點(diǎn)
        startPoint.x =
            if (startPoint.x + translateX >= 0) -circleLen * 4 else startPoint.x + translateX
    }
}

到此這篇關(guān)于Android自定義View繪制貝塞爾曲線實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)Android貝塞爾曲線內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論