欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

前言

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

二階貝塞爾曲線

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

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

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

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

    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來實現(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ū)分觸摸點是左邊還是右邊
                if (event.x < divideLine) {
                    fixedX1 = event.x
                    fixedY1 = event.y
                } else {
                    fixedX2 = event.x
                    fixedY2 = event.y
                }
                postInvalidate()
            }
        }
        return true
    }

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

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

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

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

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

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

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

以View為中心,畫圓

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

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

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

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

        //根據(jù)進(jìn)度改變起點坐標(biāo)的y值
        startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
        //移動到起點
        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()
        //走完一周回到原點
        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)
        //自動增長進(jìn)度
        autoGrow()
    }
    //進(jìn)度從0-100,自動增長
    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)度改變起點坐標(biāo)的y值
        startPoint.y = ((1 - (progress / 100.0)) * height).toInt()
        //移動到起點
        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()
        //走完一周回到原點
        startPoint.x =
            if (startPoint.x + translateX >= 0) -circleLen * 4 else startPoint.x + translateX
    }
}

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

相關(guān)文章

  • 淺談一下Android的Activity

    淺談一下Android的Activity

    這篇文章主要介紹了淺談一下Android的Activity,活動是所有安卓應(yīng)用程序的門面,凡事你在應(yīng)用中看到的東西,都是放到活動中的,需要的朋友可以參考下
    2023-04-04
  • Android使用Handler實現(xiàn)倒計時功能

    Android使用Handler實現(xiàn)倒計時功能

    這篇文章主要為大家詳細(xì)介紹了Android使用Handler實現(xiàn)倒計時功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-06-06
  • Android實現(xiàn)自動輪播圖效果

    Android實現(xiàn)自動輪播圖效果

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)自動輪播圖效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Android個人中心的頭像上傳,圖片編碼及截取實例

    Android個人中心的頭像上傳,圖片編碼及截取實例

    本篇文章主要介紹了Android個人中心的頭像上傳,圖片編碼及截取實例,非常具有實用價值,需要的朋友可以參考下。
    2016-12-12
  • 詳解Android ContentProvider的基本原理和使用

    詳解Android ContentProvider的基本原理和使用

    ContentProvider(內(nèi)容提供者)是 Android 的四大組件之一,管理 Android 以結(jié)構(gòu)化方式存放的數(shù)據(jù),以相對安全的方式封裝數(shù)據(jù)(表)并且提供簡易的處理機(jī)制和統(tǒng)一的訪問接口供其他程序調(diào)用
    2021-06-06
  • Android 自定義密碼輸入框?qū)崿F(xiàn)代碼

    Android 自定義密碼輸入框?qū)崿F(xiàn)代碼

    最近做個項目自定義密碼輸入框功能,下面小編把實現(xiàn)思路分享到腳本之家平臺,需要的朋友參考下吧
    2018-03-03
  • Android AlertDialog多種創(chuàng)建方式案例詳解

    Android AlertDialog多種創(chuàng)建方式案例詳解

    這篇文章主要介紹了Android AlertDialog多種創(chuàng)建方式案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • Android Activity啟動模式之singleTask實例詳解

    Android Activity啟動模式之singleTask實例詳解

    這篇文章主要介紹了Android Activity啟動模式之singleTask,結(jié)合實例形式較為詳細(xì)的分析了singleTask模式的功能、使用方法與相關(guān)注意事項,需要的朋友可以參考下
    2016-01-01
  • Android UI 中的 ListView列表控件的示例

    Android UI 中的 ListView列表控件的示例

    本篇文章主要介紹了Android UI 中的 ListView列表控件的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • Android仿微信右滑返回功能的實例代碼

    Android仿微信右滑返回功能的實例代碼

    這篇文章主要介紹了Android仿微信右滑返回功能的實例代碼,需要的朋友可以參考下
    2017-09-09

最新評論